Search code examples
pythonqtmayapyside2

Unable to get Maya's workspaceControl window geometry in closeEvent


I've been using an adaptation of the this wrapper in Maya, so that I can parent a Qt window to workspaceControl, which makes it dockable with different parts of the user interface. Basically it creates an empty dockable window using Maya's own code, gets the C++ pointer to it, and wraps the Qt window inside of that window.

The version linked does not handle closeEvents correctly, so I adapted it so it'll always trigger upon closing down. I did this by setting Qt.WA_DeleteOnClose as an attribute on the Qt window and linking the destroyed signal to close().

However, I found that the functions like geometry, width, move, etc, don't work properly as the window is in multiple levels of parents. In fact, to get any of those values, you need to call self.parent().parent().parent().parent().parent().func(). The issue arises when the window is closed, as it seems to delete the higher level parents before closeEvent is called, resulting in an error since self.parent().parent() returns None after that.

I would like to be able to save the window position on exit, but can't figure out how to get access to the information before it's deleted. Other than just recording it each time any function is run, is there a way I could cleanly do it at the end?

Here's the most trimmed down example I could come up with:

from PySide2 import QtWidgets, QtCore
from shiboken2 import wrapInstance
import maya.OpenMayaUI as omUI
import pymel.core as pm

def dockable_window(window_class):
    main_control = pm.workspaceControl(window_class.ID)
    win_ptr = omUI.MQtUtil.findControl(window_class.ID)
    control_wrap = wrapInstance(int(win_ptr), QtWidgets.QWidget)
    control_wrap.setAttribute(QtCore.Qt.WA_DeleteOnClose)
    win = window_class(control_wrap)
    control_wrap.destroyed.connect(win.close)

class MyWindow(QtWidgets.QMainWindow):
    ID = 'testing'
    def __init__(self, parent, *args, **kwargs):
        QtWidgets.QMainWindow.__init__(self, parent)

        #Create a button that will show the geometry when clicked
        b = QtWidgets.QPushButton('test')
        b.clicked.connect(self.show_actual_geometry)
        parent.layout().addWidget(b)

    def show_actual_geometry(self):
        """This will work until it is called during closeEvent."""
        print self.parent().parent().parent().parent().parent().geometry()

    def closeEvent(self, event):
        print self.show_actual_geometry()
        QtWidgets.QMainWindow.closeEvent(self, event)

dockable_window(MyWindow)

Solution

  • The workspaceControl seems not to close and destroy it's widget correctly, at least the close event is not called. For that reason the closeCommand does got called even if the retain attribute ist set to false (tried in Maya2018). A workaround is to use the visibleChangeCommand. It gets called if the close button is clicked.