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)