PyQt and PySide Widget Best Practices

Maintain a Reference to your Widget

If you are using PyQt or PySide to customize Maya's user interface, you should make sure to parent your widget under an existing Maya widget, such as Maya's main window. Otherwise, if the widget is un-parented, it may be destroyed by the Python interpreter's garbage collector if a reference to it is not maintained.

The following code sample exemplifies this best practice. Note that this code will also work by importing the PyQt module instead of PySide.

from maya import OpenMayaUI as omui 

try:
  from PySide2.QtCore import * 
  from PySide2.QtGui import * 
  from PySide2.QtWidgets import *
  from PySide2 import __version__
  from shiboken2 import wrapInstance 
except ImportError:
  from PySide.QtCore import * 
  from PySide.QtGui import * 
  from PySide import __version__
  from shiboken import wrapInstance 

mayaMainWindowPtr = omui.MQtUtil.mainWindow() 
mayaMainWindow= wrapInstance(long(mayaMainWindowPtr), QWidget) 

# WORKS: Widget is fine 
hello = QLabel("Hello, World", parent=mayaMainWindow) 
hello.setObjectName('MyLabel') 
hello.setWindowFlags(Qt.Window) # Make this widget a standalone window even though it is parented 
hello.show() 
hello = None # the "hello" widget is parented, so it will not be destroyed. 

# BROKEN: Widget is destroyed 
hello = QLabel("Hello, World", parent=None) 
hello.setObjectName('MyLabel') 
hello.show() 
hello = None # the "hello" widget is not parented, so it will be destroyed.

If you are using the PySide mix-in classes described below, this parenting will be handled for you automatically.

Use the maya.app.general.mayaMixin Classes

The maya.app.general.mayaMixin module contains classes to help simplify the integration of PySide-based widgets into Maya's UI.

The MayaQWidgetBaseMixin class handles common actions for Maya Qt widgets during initialization such as: auto-naming a widget so that it can be looked up as a string through maya.OpenMayaUI.MQtUtil.findControl(), and parenting the widget under the main Maya window if no parent is explicitly specified, so that the window does not disappear when the instance variable goes out of scope (see Maintain a Reference to your Widget above).

To use the class, ensure that it appears in your widget's list of parent classes before the Qt class that your widget derives from.

For information on the methods and properties provided by these classes, use the Python's help function in the Python tab of the Maya Script Editor (for example, help(MayaQWidgetDockableMixin)).

from maya.app.general.mayaMixin import MayaQWidgetBaseMixin

try:
  from PySide2.QtCore import * 
  from PySide2.QtGui import * 
  from PySide2.QtWidgets import *
  from PySide2 import __version__
  from shiboken2 import wrapInstance 
except ImportError:
  from PySide.QtCore import * 
  from PySide.QtGui import * 
  from PySide import __version__
  from shiboken import wrapInstance 

class MyButton(MayaQWidgetBaseMixin, QPushButton):
    def __init__(self, parent=None):
        super(MyButton, self).__init__(parent=parent)
        self.setText('Push Me')

# Create an instance of the button and display it.
#
button = MyButton()
button.show()

# A valid Maya control name has been automatically assigned
# to the button.
#
buttonName = button.objectName()
print('# ' + buttonName)
# MyButton_368fe1d8-5bc3-4942-a1bf-597d1b5d3b83

# Losing our only reference to the button does not cause it to be
# destroyed.
#
myButton = None

# We can use the button's name to find it as a Maya control.
#
from maya.OpenMayaUI import MQtUtil
from shiboken2 import wrapInstance

ptr = MQtUtil.findControl(buttonName)
foundControl = wrapInstance(long(ptr), QPushButton)

# Print out the button's text.
#
print('# ' + foundControl.text())
# Push Me

The MayaQWidgetDockableMixin class provides support for Maya's dockable actions. The docking behavior of the widget is controlled through parameters passed to its show() method, such as: dockable to specify whether the widget should be dockable, area to specify its default dock area, and so forth.

To use the class, ensure that it appears in your widget's list of parent classes before the Qt class that your widget derives from.

from maya.app.general.mayaMixin import MayaQWidgetDockableMixin

try:
  from PySide2.QtCore import * 
  from PySide2.QtGui import * 
  from PySide2.QtWidgets import *
  from PySide2 import __version__
  from shiboken2 import wrapInstance 
except ImportError:
  from PySide.QtCore import * 
  from PySide.QtGui import * 
  from PySide import __version__
  from shiboken import wrapInstance 

class MyDockableButton(MayaQWidgetDockableMixin, QPushButton):
    def __init__(self, parent=None):
        super(MyDockableButton, self).__init__(parent=parent)
        self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred )
        self.setText('Push Me')

# Show the button as a non-dockable floating window.
#
button = MyDockableButton()
button.show(dockable=False)

# showRepr() can be used to display the current dockable settings.
#
print('# ' + button.showRepr())
# show(dockable=False, height=23, width=70, y=610, x=197, floating=True)

# Change it to a dockable floating window.
#
button.show(dockable=True)
print('# ' + button.showRepr())
# show(dockable=True, area='none', height=23, width=70, y=610, x=197, floating=True)