8

I created a tool that is able to dock in Maya's main ui, but I can't figure out a way to clean it up once it closes. The problem is if I create multiple instances of the tool then drag it in place to dock it, they will ALL show up when I right-click on Maya's window. How do I properly clean these up when the tool closes?

I already tried cmds.deleteUI, QObject.deleteLater() and at best I can only clear the tool's contents, but it will still exist in Maya. Here's an example of what I have so far:

from shiboken import wrapInstance
from PySide import QtGui, QtCore
from maya import OpenMayaUI as OpenMayaUI
from maya.app.general.mayaMixin import MayaQWidgetDockableMixin

class Window(MayaQWidgetDockableMixin, QtGui.QWidget):
    def __init__(self, parent = None):
        super(self.__class__, self).__init__(parent = parent)
        mayaMainWindowPtr = OpenMayaUI.MQtUtil.mainWindow() 
        self.mayaMainWindow = wrapInstance(long(mayaMainWindowPtr), QtGui.QWidget) 
        self.setWindowFlags(QtCore.Qt.Window)

        if cmds.window('myTool', q = True, ex = True):
            cmds.deleteUI('myTool')

        self.setObjectName('myTool')

        self.setWindowTitle('My tool')
        self.resize(200, 200)

        self.myButton = QtGui.QPushButton('TEMP')

        self.mainLayout = QtGui.QVBoxLayout()
        self.mainLayout.addWidget(self.myButton)
        self.setLayout(self.mainLayout)

    def dockCloseEventTriggered(self):
        self.deleteLater()

    def run(self):
        self.show(dockable = True)

myWin = Window()
myWin.run()
Green Cell
  • 4,677
  • 2
  • 18
  • 49
  • Sorry but I can't reproduce your problem. If I run your script, I can drag, dock, undock and close multiple instances in the correct way. What version of maya are you using? – Ale_32 Nov 06 '15 at 10:03
  • Another things: if you have in your code `if cmds.window('myTool', q = True, ex = True): cmds.deleteUI('myTool')` you should not run multiple instances of Window! – Ale_32 Nov 06 '15 at 10:06
  • Maybe I didn't explain it properly. I can run, dock, undock and close multple instances too. Let's say you create multiple instances and dock each one to the channel box /attribute editor, then close them. Right-click on the channel box / attribute editor window's title, and it'll show a bunch of 'My tool' check boxes there! It's those that I can't clean out. – Green Cell Nov 06 '15 at 10:07
  • ^ erm no.. There shouldn't be multiple instances at once. There should only ever be one instance, that's why it's there to try and delete the previous one. – Green Cell Nov 06 '15 at 10:09
  • Ok now I understand. I have tried also with `self.setAttribute(QtCore.Qt.WA_DeleteOnClose)` or `self.setParent(None)` but only dockwidget content is deleted. If I find a solution I'll tell you – Ale_32 Nov 06 '15 at 10:26
  • When trying to wonder why your code wasn't running on my computer (mayaMixin isn't part of Maya 2014 apparently), I came across this topic [Executing code on close of a docked PySide QDialog](https://groups.google.com/forum/#!topic/python_inside_maya/sX-WBaQ8lak) The problem seems to be this: _If the window is floating and not docked, the closeEvent code executes. But if the window is docked and closed (via the [x] in the corner), the closeEvent doesn't execute._ – DrHaze Nov 06 '15 at 10:55
  • `AK Eric`'s solution: _Tracked it down: The MayaQWidgetDockableMixin class has a dockCloseEventTriggered method: I can call to my cleanup code in there, and all is happy now._ – DrHaze Nov 06 '15 at 10:57
  • But he already does it! – Ale_32 Nov 06 '15 at 11:38
  • @Ale_32 Yes that's as far as I could get. I can only delete the window's content, but it feels like this should be something done with `cmds.deleteUI` so it doesn't appear anymore. @DrHaze I'll look at that link some more. `dockCloseEventTriggered` runs fine for me but it's still leaving stuff behind. Check out my response to Ale_32 above if you need a clearer explanation. Thanks for checking on this guys. – Green Cell Nov 06 '15 at 12:20
  • I try everything I know to close, delete, destroy a instance of a class, but no one work. I look into the *mayaMixin.py* and I try to override all closed event, but that QDockWidget still appear there. Hope someone can help us, it's a problem for me too. – Ale_32 Nov 06 '15 at 14:34
  • https://knowledge.autodesk.com/search-result/caas/CloudHelp/cloudhelp/2016/ENU/Maya-SDK/files/GUID-66ADA1FF-3E0F-469C-84C7-74CEB36D42EC-htm.html – Ale_32 Nov 06 '15 at 14:35

1 Answers1

5

After digging around mayaMixin.py I managed to get a working solution with the behavior I'm after! The idea is that you need to dig through Maya's main window and delete any instances of it there.

The example below will cleanly delete any instances once the window is closed or a new instance is created. It doesn't matter if it's docked or floating, it seems to work ok. You could always tweak the behavior too if you don't want it to delete if it's closed while being docked. I left many comments in the code as there was a lot of "gotchas".

from shiboken import wrapInstance
from PySide import QtGui, QtCore
from maya import OpenMayaUI as OpenMayaUI
from maya.app.general.mayaMixin import MayaQWidgetDockableMixin
from maya.OpenMayaUI import MQtUtil

class MyWindow(MayaQWidgetDockableMixin, QtGui.QDialog):
    toolName = 'myToolWidget'

    def __init__(self, parent = None):
        # Delete any previous instances that is detected. Do this before parenting self to main window!
        self.deleteInstances()

        super(self.__class__, self).__init__(parent = parent)
        mayaMainWindowPtr = OpenMayaUI.MQtUtil.mainWindow() 
        self.mayaMainWindow = wrapInstance(long(mayaMainWindowPtr), QtGui.QMainWindow)
        self.setObjectName(self.__class__.toolName) # Make this unique enough if using it to clear previous instance!

        # Setup window's properties
        self.setWindowFlags(QtCore.Qt.Window)
        self.setWindowTitle('My tool')
        self.resize(200, 200)

        # Create a button and stuff it in a layout
        self.myButton = QtGui.QPushButton('My awesome button!!')
        self.mainLayout = QtGui.QVBoxLayout()
        self.mainLayout.addWidget(self.myButton)
        self.setLayout(self.mainLayout)

    # If it's floating or docked, this will run and delete it self when it closes.
    # You can choose not to delete it here so that you can still re-open it through the right-click menu, but do disable any callbacks/timers that will eat memory
    def dockCloseEventTriggered(self):
        self.deleteInstances()

    # Delete any instances of this class
    def deleteInstances(self):
        mayaMainWindowPtr = OpenMayaUI.MQtUtil.mainWindow() 
        mayaMainWindow = wrapInstance(long(mayaMainWindowPtr), QtGui.QMainWindow) # Important that it's QMainWindow, and not QWidget/QDialog

        # Go through main window's children to find any previous instances
        for obj in mayaMainWindow.children():
            if type( obj ) == maya.app.general.mayaMixin.MayaQDockWidget:
                #if obj.widget().__class__ == self.__class__: # Alternatively we can check with this, but it will fail if we re-evaluate the class
                if obj.widget().objectName() == self.__class__.toolName: # Compare object names
                    # If they share the same name then remove it
                    print 'Deleting instance {0}'.format(obj)
                    mayaMainWindow.removeDockWidget(obj) # This will remove from right-click menu, but won't actually delete it! ( still under mainWindow.children() )
                    # Delete it for good
                    obj.setParent(None)
                    obj.deleteLater()        

    # Show window with docking ability
    def run(self):
        self.show(dockable = True)

myWin = MyWindow()
myWin.run()

With this it's no longer polluting Maya's environment anymore. Hope it helps!

Green Cell
  • 4,677
  • 2
  • 18
  • 49