Problems with MayaMixin and PySide events

Hey folk,
Here is the thing

  • I’m working with maya 2017 update 3 and I’ve created a docked QT window via MayaQWidgetDockableMixin.
  • I’ve overriden a QWidget class with it’s own sizes, color, events etc. (I’ve changed it’s color with self.setPalette() method)
  • I’ve added the widget to the Main Docked Window.

The problem is:

  • When I override enterEvent and leaveEvent where I set another cursor and background changes - they have no effect. If I just print smth like event.type() or whatever - it works. I don’t know why it happens. I’ve already tested it out on my Linux OS with the same Maya 2017 Upd3 - the same issue. It’s so stupid problem and really makes me crazy.

Here is my source code, if you have the same version of Maya - try it and tell me how it worked, I have Maya2017 without any updates on my mac and this code works perfect, I’m expecting it happens because of some glitches in update 3 or may be I’m wrong

`from maya.app.general.mayaMixin import MayaQWidgetDockableMixin
 from PySide2 import QtWidgets, QtGui, QtCore
 import maya.cmds as cmds

def deleteControl(control):
    if cmds.workspaceControl(control, q=True, exists=True):
        cmds.workspaceControl(control, e=True, close=True)
        cmds.deleteUI(control, control=True)

class MyWid(QtWidgets.QWidget):
    def __init__(self, parent = None):
        super(MyWid, self).__init__(parent)

        self.setMinimumSize(50,50)
        self.setMaximumSize(50,50)

        self.bg_color = 49

        self.setAutoFillBackground(True)
        self.p = self.palette()
        self.p.setColor(self.backgroundRole(), QtGui.QColor(self.bg_color,self.bg_color,self.bg_color))
        self.setPalette(self.p)


   def enterEvent(self, event):
        self.setCursor(QtCore.Qt.PointingHandCursor)
        self.p.setColor(self.backgroundRole(), QtGui.QColor(self.bg_color+70,self.bg_color,self.bg_color))
        self.setPalette(self.p)
        return QtWidgets.QWidget.enterEvent(self, event)

    def leaveEvent(self, event):
        self.setCursor(QtCore.Qt.ArrowCursor)
        self.p.setColor(self.backgroundRole(), QtGui.QColor(self.bg_color,self.bg_color,self.bg_color))
        self.setPalette(self.p)
        return QtWidgets.QWidget.leaveEvent(self, event)

    def mouseMoveEvent(self, event):
        self.setCursor(QtCore.Qt.PointingHandCursor)
        return QtWidgets.QWidget.mouseMoveEvent(self, event)




class DialogWidget(MayaQWidgetDockableMixin, QtWidgets.QDialog):
    def __init__(self, parent=None):
        super(DialogWidget, self).__init__(parent=parent)
        self.setObjectName('test_asadsgfasf')
        self.setWindowTitle('test_asadsgfasf')

        self.setLayout(self.createLayout())

    def createLayout(self):

        self.main_layout = QtWidgets.QVBoxLayout()

        self.button = QtWidgets.QPushButton("ASDASDD")
        self.test = MyWid()


        self.main_layout.addWidget(self.button)
        self.main_layout.addWidget(self.test)

        return self.main_layout



def main():
    deleteControl("test_asadsgfasfWorkspaceControl")
    dialogValidatorX = DialogWidget()
    dialogValidatorX.show(dockable=True, area='right', allowedArea = "right",  floating=False)
    cmds.workspaceControl("test_asadsgfasfWorkspaceControl", e=True, ttc=["AttributeEditor", 0], wp="preferred", mw=420)
    dialogValidatorX.raise_()`

I tried it on Maya 2017 Update 3 on Windows 7. The widget changed color when the dialog was not docked. But whenever it was docked, the widget wouldn’t change colors anymore.

That’s the thing ! ))

I noticed that one of my custom widgets also no longer updates when it is docked. Works fine un-docked, but stops updating while docked. I suspect something has changed under the hood with the paint events, or refreshing in PySide2.

The Widgets will update when I click in the Viewport.

Ok, it has been fixed by getting Update 4 for maya 2017 )))

Update 4 did not fix this issue for me. I ended up implementing a quick solution by connecting every signal on my widget to a call to QApplication.processEvents(). Its not exactly elegant but it got the job done.
Here’s the basic code:

from Qt import QtWidgets
from Qt.QtCore import Signal, Slot

def connectToReload(MyWindow):
    cls = MyWindow if isinstance(MyWindow, type) else type(MyWindow)
    signal = type(Signal())
    signals = [name for name in dir(MyWindow) if isinstance(getattr(cls, name), signal)]

    for signal in signals:
        getattr(MyWindow, signal).connect(reloadApp)

@Slot()
def reloadApp(*args, **kwargs):
    app = QtWidgets.QApplication.instance()
    app.processEvents()

Hi, after a day of struggling with the same issue in maya 2018.3, I found a solution.
The problem was that when I manually dock my window, the UI becomes dead,
but a little buggy area of my maya layout was showing updates (top left corner).
So I guessed parenting was not good.

The solution is simple, it has to do with initialisation and class inheritance order.
Just put MayaQWidgetDockableMixin after other main inherited classes:

import shiboken2
from PySide2 import QtWidgets
from maya import OpenMayaUI


def mayaMainWindow():
    shiboken2.wrapInstance(long(OpenMayaUI.MQtUtil.mainWindow()), QtWidgets.QMainWindow)


class AssetFxBrowserUI(QtWidgets.QMainWindow, AssetFxBrowser.Ui_AssetFxBrowser, MayaQWidgetDockableMixin, utils.FeedbackUI):
    def __init__(self, parent=mayaMainWindow()):
        super(AssetFxBrowserUI, self).__init__(parent)
        #... your stuff
        self.show(dockable=True)

At least this worked for me, hope it does for you

I’m also experiencing this issue, and changing the inheritance order didn’t solve it for me. The docstring of the mixin has this to say:

C:\Program Files\Autodesk\Maya2017\Python\Lib\site-packages\maya\app\general\mayaMixin.py

class MayaQWidgetDockableMixin(MayaQWidgetBaseMixin):
    '''
    Handle Maya dockable actions controlled with the show() function.
    
    Integration Notes:
        Inheritance ordering: This class must be placed *BEFORE* the Qt class for proper execution
        This is needed to workaround a bug where PyQt/PySide does not call super() in its own __init__ functions
    
    Example:
        class MyQWidget(MayaQWidgetDockableMixin, QPushButton):
            def __init__(self, parent=None):
                super(MyQWidget, self).__init__(parent=parent)
                self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred )
                self.setText('Push Me')
    '''

I did however find a “solution” here:

Which was this:


def maya2017_ui_fix():
    """Fix unresponsive GUI in Dock and Menu from Maya 2017 onwards"""
    from maya import cmds, OpenMayaUI as omui
    from Qt import QtCompat, QtWidgets

    main_window_ptr = omui.MQtUtil.mainWindow()
    mayaWid = QtCompat.wrapInstance(long(main_window_ptr), QtWidgets.QWidget)

    class _(MayaQWidgetDockableMixin, QtWidgets.QDialog):
        def __del__(self, *args, **kwargs):
            pass

        def __init__(self, parent=None):
            QtWidgets.QDialog.__init__(self, parent)

    dummy = _(mayaWid)
    dummy.setObjectName("DockingDummyWindow")
    name = dummy.objectName() + "WorkspaceControl"

    dummy.show(dockable=True, area="right", floating=False)
    cmds.workspaceControl(name, edit=True,
                          tabToControl=["AttributeEditor", -1],
                          widthProperty="preferred",
                          minimumWidth=420)

    cmds.workspaceControl(name, edit=True, close=True)
    cmds.deleteUI(name, control=True)

Which when run first tricks subsequent docs to work appropriately. It does however have the unfortunate consequence of flickering the GUI and sometimes shifting some items in the layout, so will keep an eye on this thread for less intrusive workarounds.

After a bit of frustration I found that NOT passing a parent to the ui fixes the refresh issue for me. This issue of refreshing also seems to have been fixed in 2020 as well. I can just show the ui without the extra workspace control maya cmds. If you run the code below and pass the maya parent on the first run it won’t refresh when selecting the table widget items. If you run it as is it’s fine:

from maya import cmds
from Qt_py.Qt import (QtWidgets, QtCore)
from maya.app.general.mayaMixin import MayaQWidgetDockableMixin


class UI(QtWidgets.QMainWindow):

    def __init__(self, parent=None):
        super(UI, self).__init__(parent)

        self.setWindowTitle("Test")
        self.setMinimumSize(600, 600)
        self.resize(600, 800)

        self.central_widget = QtWidgets.QWidget()
        self.setCentralWidget(self.central_widget)

        self.layout = QtWidgets.QVBoxLayout(self.central_widget)

        self.table_widget = QtWidgets.QTableWidget()
        self.table_widget.setColumnCount(1)

        self.table_widget.setAlternatingRowColors(True)
        self.table_widget.setSelectionBehavior(
            QtWidgets.QAbstractItemView.SelectRows)
        self.table_widget.setSelectionMode(
            QtWidgets.QAbstractItemView.SingleSelection)
        self.table_widget.setShowGrid(False)
        self.table_widget.setGridStyle(QtCore.Qt.NoPen)
        self.table_widget.verticalHeader().setVisible(False)
        self.table_widget.horizontalHeader().setVisible(False)
        self.table_widget.horizontalHeader().setStretchLastSection(True)

        for i in range(10):
            self.table_widget.insertRow(i)
            item = QtWidgets.QTableWidgetItem()
            item.setText("test{0}".format(i))
            self.table_widget.setItem(i, 0, item)

        self.btn = QtWidgets.QPushButton("TEST")

        self.layout.addWidget(self.table_widget)
        self.layout.addWidget(self.btn)

        self.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)

class Mix(MayaQWidgetDockableMixin, UI):

    def __init__(self, *args, **kwargs):
        super(Mix, self).__init__(*args, **kwargs)


def main():

    app = Mix()
    app.setObjectName("Test")
    name = app.objectName() + "WorkspaceControl"

    if cmds.workspaceControl(name, query=True, exists=True):
        cmds.workspaceControl(name, edit=True, close=True)
        cmds.deleteUI(name, control=True)

    app.show(dockable=True, area="right")
    cmds.workspaceControl(name, edit=True,
                          tabToControl=["AttributeEditor", -1],
                          widthProperty="preferred",
                          minimumWidth=420)


if __name__ == '__main__':
    main()