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 
from PySide.QtCore import * 
from PySide.QtGui import * 
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
from PySide.QtGui import QPushButton

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 shiboken 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
from PySide.QtGui import QPushButton, QSizePolicy

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)