Create a gist now

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Maya - Universal dockable window module and class (OLD)
'''
------------------------------------------------------------------------------------------------------------------------
# Maya 2017 PySide2 Docking Qt QMainWindow by Michael Davydov | https://github.com/Nixellion/
## https://www.michaeldavydov.com | https://www.nixes.ru
------------------------------------------------------------------------------------------------------------------------
The goal of this module is to be base module and class for Maya dockable UI windows, with support of Maya versions
before and after 2017, when Maya switched to PySide2, and workspaceControl instead of dockControl.
Code works but still WIP.
Honrable mentions of people who's work was used as part of this code, for inspiration or help:
- Lior Ben Horin https://gist.github.com/liorbenhorin/ - for starting code snippet, which showed how to start working with
workspaceControl.
- ...
------------------------------------------------------------------------------------------------------------------------
To quickly adapt this code for youserlf do the following:
- Replace `log.debug` with `print`
- Retarget Qt.py imports for your project
- Remove all `@catch_errors` lines
- Remove `from BroTools.common.debug import catch_errors` line
Should work after that.
------------------------------------------------------------------------------------------------------------------------
'''
# NOTES
# Seems to be working more or less fine right now. BroDynamics window works using this class, both in 2016 and 2017
# TODO - Why does this not work with this default template thing in Maya 2016? It just won't show contents. But it works
# fine in 2017...
# Python built ins
import weakref
from functools import partial
# Maya
import maya.cmds as cmds
import maya.OpenMayaUI as omui
# Shiboken
try:
from shiboken2 import wrapInstance
except:
from shiboken import wrapInstance
# https://github.com/mottosso/Qt.py by Marcus Ottosson
from BroTools.common.broqt.vendor.Qt import *
from BroTools.common.broqt.vendor.Qt.QtWidgets import *
from BroTools.common.broqt.vendor.Qt.QtGui import *
from BroTools.common.broqt.vendor.Qt.QtCore import *
# Debugging modules
# Custom logging fuction with support of comma-separated args and more
from BroTools.common.debug import default_logger as log
# Error catching wrapper
from BroTools.common.debug import catch_errors
# Globals
maya_version = int(cmds.about(v=True))
def dock_window(dialog_class, width=440):
'''
Dock window function. Shows and docks window to the right.
This function is top-level function, which you should call to dock a window.
This function merely makes a decision about which method to use, old prior to Maya 2017, or new, for Maya 2017+
'''
maya_version = cmds.about(v=True)
log.debug("Maya version is:", str(maya_version), int(maya_version) < 2017)
if int(maya_version) < 2017:
return dock_window_pyside(dialog_class, width=width)
else:
return dock_window_pyside2(dialog_class)
@catch_errors
def dock_window_pyside(dialog_class, width):
'''
Function that creates and docks window in Maya version prior to 2017.
:param dialog_class:
:return:
'''
DOCK_CONTROL_NAME = dialog_class.CONTROL_NAME+"_dockControl"
if cmds.dockControl(DOCK_CONTROL_NAME, ex=True):
cmds.deleteUI(DOCK_CONTROL_NAME)
log.debug("Removed dock", DOCK_CONTROL_NAME)
else:
log.debug ("No dock control found, creating new.")
win = dialog_class()
main_control = cmds.dockControl(DOCK_CONTROL_NAME, l=dialog_class.DOCK_LABEL_NAME, a='right', con=dialog_class.CONTROL_NAME, ret=False, w=width)
# now lets get a C++ pointer to it using OpenMaya
control_widget = omui.MQtUtil.findControl(main_control)
# conver the C++ pointer to Qt object we can use
control_wrap = wrapInstance(long(control_widget), QWidget)
log.debug(control_wrap.findChild(QWidget, win.objectName())) # I'm not sure why, but do not delete this line. You can replace it with print if you must. But this call has to be made.
# control_wrap is the widget of the docking window and now we can start working with it:
control_wrap.setAttribute(Qt.WA_DeleteOnClose)
# will return the class of the dock content.
return win.run()
@catch_errors
def dock_window_pyside2(dialog_class, width):
'''
A function that creates and docks window in Maya versions 2017+
:param dialog_class:
:return:
'''
######
workspaceCnt = dialog_class.CONTROL_NAME + "_workspaceControl"
try:
cmds.deleteUI(workspaceCnt)
log.info('removed workspace {}'.format(dialog_class.CONTROL_NAME))
except:
pass
windowcall = 'print "workspaceControl BUILD UI DUMMY"'
main_control = cmds.workspaceControl(workspaceCnt, ttc=["AttributeEditor", -1], iw=width, mw=True,
wp='preferred', label=dialog_class.DOCK_LABEL_NAME)
cmds.workspaceControl(workspaceCnt, e=True, vis=True)
cmds.workspaceControl(workspaceCnt, e=True, r=True) # raise it
cmds.evalDeferred(lambda *args: cmds.workspaceControl(main_control, e=True, r=True))
# now lets get a C++ pointer to it using OpenMaya
control_widget = omui.MQtUtil.findControl(main_control)
# conver the C++ pointer to Qt object we can use
control_wrap = wrapInstance(long(control_widget), QWidget)
# control_wrap is the widget of the docking window and now we can start working with it:
control_wrap.setAttribute(Qt.WA_DeleteOnClose)
win = dialog_class(control_wrap)
# will return the class of the dock content.
return win.run()
class BroDockingUI(QWidget):
DOCK_LABEL_NAME = 'no name window'
instances = list()
CONTROL_NAME = 'no_name_window'
def __init__(self, parent=None):
super(BroDockingUI, self).__init__(parent)
# let's keep track of our docks so we only have one at a time.
BroDockingUI.delete_instances()
self.__class__.instances.append(weakref.proxy(self))
self.window_name = self.CONTROL_NAME
self.setObjectName(self.window_name)
self.parent = parent
# Depending on Maya version we build this part differently, because of the changes made in 2017
# Moving to PySide2, and using workspaceControl instead of dockControl
# Let's create our own top-level layout, to add support for menuBar and statusBar
self.top_layout = QVBoxLayout()
self.setLayout(self.top_layout)
# Maya 2017 uses QWidgets in this case, so we need to add menu bar and status bar manually
self.menubar_layout = QVBoxLayout()
self.statusbar_layout = QVBoxLayout()
self.main_layout = QVBoxLayout()
self.menubar_layout.setContentsMargins(0, 0, 0, 0)
self.statusbar_layout.setContentsMargins(0, 0, 0, 0)
self.main_layout.setContentsMargins(0, 0, 0, 0)
self.top_layout.addLayout(self.menubar_layout)
self.top_layout.addLayout(self.main_layout)
self.top_layout.addLayout(self.statusbar_layout)
if maya_version >= 2017:
# Find parent and explicitly add this widget to it.
log.debug("Adding to parent layout.")
self.parent_layout = parent.layout()
self.parent_layout.addWidget(self)
self.parent_layout.setContentsMargins(0, 0, 0, 0)
log.debug("Now building actual UI for", self.DOCK_LABEL_NAME, self.CONTROL_NAME, "window.")
self.build_ui()
def setMenuBar(self, menuBar):
log.debug("Adding menu bar")
#self.clearLayout(self.menubar_layout)
self.menubar_layout.addWidget(menuBar)
def setStatusBar(self, statusBar):
log.debug ("Adding status bar")
#self.clearLayout(self.menubar_layout)
self.statusbar_layout.addStretch()
self.statusbar_layout.addWidget(statusBar)
def clearLayout(self, layout):
while layout.count():
child = layout.takeAt(0)
if child.widget() is not None:
child.widget().deleteLater()
elif child.layout() is not None:
self.clearLayout(child.layout())
@staticmethod
def delete_instances():
for ins in BroDockingUI.instances:
try:
log.debug('Delete {}'.format(ins))
except:
log.debug('Window reference seems to be removed already, ignore.')
try:
ins.setParent(None)
ins.deleteLater()
except:
# ignore the fact that the actual parent has already been deleted by Maya...
pass
try:
BroDockingUI.instances.remove(ins)
del ins
except:
# Supress error
pass
def run(self):
return self
def build_ui(self):
'''
Override this with your UI functions.
'''
self.my_label = QLabel('Bro world!')
self.main_layout.addWidget(self.my_label)
self.menuBar = QMenuBar()
self.presetsMenu = self.menuBar.addMenu(("&Presets"))
self.saveConfigAction = QAction(("&Save Settings"), self)
self.presetsMenu.addAction(self.saveConfigAction)
self.setMenuBar(self.menuBar)
self.statusBar = QStatusBar()
self.statusBar.showMessage ("Status bar ready.")
self.setStatusBar(self.statusBar)
self.statusBar.setObjectName("statusBar")
self.setStyleSheet("#statusBar {background-color:red;}")
class TestChildWindow(BroDockingUI):
DOCK_LABEL_NAME = 'TestChildWindow'
CONTROL_NAME = 'test_child_window'
instances = list()
def __init__(self, parent=None):
super(TestChildWindow, self).__init__(parent)
pass
def run(self):
return self
def show_test():
# this is how we call the window
my_dock = dock_window(TestChildWindow)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment