1. """
  2. This is what you need to do in order to get a qt window to dock next to maya channel box,
  3. In all maya versions, including 2017 with PySide2
  4. """
  5. __author__ = "liorbenhorin@gmail.com"
  6. import sys
  7. import os
  8. import logging
  9. import xml.etree.ElementTree as xml
  10. from cStringIO import StringIO
  11. # Qt is a project by Marcus Ottosson ---> https://github.com/mottosso/Qt.py
  12. from Qt import QtGui, QtWidgets, QtCore, QtCompat
  13. try:
  14. import pysideuic
  15. from shiboken import wrapInstance
  16. logging.Logger.manager.loggerDict["pysideuic.uiparser"].setLevel(logging.CRITICAL)
  17. logging.Logger.manager.loggerDict["pysideuic.properties"].setLevel(logging.CRITICAL)
  18. except ImportError:
  19. import pyside2uic as pysideuic
  20. from shiboken2 import wrapInstance
  21. logging.Logger.manager.loggerDict["pyside2uic.uiparser"].setLevel(logging.CRITICAL)
  22. logging.Logger.manager.loggerDict["pyside2uic.properties"].setLevel(logging.CRITICAL)
  23. import maya.OpenMayaUI as omui
  24. from maya.app.general.mayaMixin import MayaQWidgetDockableMixin
  25. import maya.cmds as cmds
  26. def loadUiType(uiFile):
  27. """
  28. :author: Jason Parks
  29. Pyside lacks the "loadUiType" command, so we have to convert the ui file to py code in-memory first
  30. and then execute it in a special frame to retrieve the form_class.
  31. """
  32. parsed = xml.parse(uiFile)
  33. widget_class = parsed.find('widget').get('class')
  34. form_class = parsed.find('class').text
  35. with open(uiFile, 'r') as f:
  36. o = StringIO()
  37. frame = {}
  38. pysideuic.compileUi(f, o, indent=0)
  39. pyc = compile(o.getvalue(), '<string>', 'exec')
  40. exec pyc in frame
  41. # Fetch the base_class and form class based on their type in the xml from designer
  42. form_class = frame['Ui_%s' % form_class]
  43. base_class = getattr(QtWidgets, widget_class)
  44. return form_class, base_class
  45. def maya_main_window():
  46. main_window_ptr = omui.MQtUtil.mainWindow()
  47. return wrapInstance(long(main_window_ptr), QtWidgets.QWidget)
  48. def maya_api_version():
  49. return int(cmds.about(api=True))
  50. class MyDockingWindow(MayaQWidgetDockableMixin, QtWidgets.QMainWindow):
  51. MAYA2014 = 201400
  52. MAYA2015 = 201500
  53. MAYA2016 = 201600
  54. MAYA2016_5 = 201650
  55. MAYA2017 = 201700
  56. def __init__(self, parent=None):
  57. self.deleteInstances() # remove any instance of this window before starting
  58. super(MyDockingWindow, self).__init__(parent)
  59. self.setWindowFlags(QtCore.Qt.Tool)
  60. """
  61. compile the .ui file on loadUiType(), a function that uses pysideuic / pyside2uic to compile .ui files
  62. """
  63. uiFile = os.path.join(os.path.dirname(__file__), 'MyDockingWindow_ui.ui')
  64. form_class, base_class = loadUiType(uiFile)
  65. self.ui = form_class()
  66. self.ui.setupUi(self)
  67. self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
  68. def dockCloseEventTriggered(self):
  69. self.deleteInstances()
  70. # Delete any instances of this class
  71. def deleteInstances(self):
  72. def delete2016():
  73. # Go through main window's children to find any previous instances
  74. for obj in maya_main_window().children():
  75. if str(type(
  76. obj)) == "<class 'maya.app.general.mayaMixin.MayaQDockWidget'>": # ""<class 'maya.app.general.mayaMixin.MayaQDockWidget'>":
  77. if obj.widget().__class__.__name__ == "MyDockingWindow": # Compare object names
  78. obj.setParent(None)
  79. obj.deleteLater()
  80. def delete2017():
  81. '''
  82. Look like on 2017 this needs to be a little diffrents, like in this function,
  83. However, i might be missing something since ive done this very late at night :)
  84. '''
  85. for obj in maya_main_window().children():
  86. if str(type(obj)) == "<class '{}.MyDockingWindow'>".format(os.path.splitext(
  87. os.path.basename(__file__)[0])): # ""<class 'moduleName.mayaMixin.MyDockingWindow'>":
  88. if obj.__class__.__name__ == "MyDockingWindow": # Compare object names
  89. obj.setParent(None)
  90. obj.deleteLater()
  91. if maya_api_version() < MyDockingWindow.MAYA2017:
  92. delete2016()
  93. else:
  94. delete2017()
  95. def deleteControl(self, control):
  96. if cmds.workspaceControl(control, q=True, exists=True):
  97. cmds.workspaceControl(control, e=True, close=True)
  98. cmds.deleteUI(control, control=True)
  99. # Show window with docking ability
  100. def run(self):
  101. '''
  102. 2017 docking is a little different...
  103. '''
  104. def run2017():
  105. self.setObjectName("MyMainDockingWindow")
  106. # The deleteInstances() dose not remove the workspace control, and we need to remove it manually
  107. workspaceControlName = self.objectName() + 'WorkspaceControl'
  108. self.deleteControl(workspaceControlName)
  109. # this class is inheriting MayaQWidgetDockableMixin.show(), which will eventually call maya.cmds.workspaceControl.
  110. # I'm calling it again, since the MayaQWidgetDockableMixin dose not have the option to use the "tabToControl" flag,
  111. # which was the only way i found i can dock my window next to the channel controls, attributes editor and modelling toolkit.
  112. self.show(dockable=True, area='right', floating=False)
  113. cmds.workspaceControl(workspaceControlName, e=True, ttc=["AttributeEditor", -1], wp="preferred", mw=420)
  114. self.raise_()
  115. # size can be adjusted, of course
  116. self.setDockableParameters(width=420)
  117. def run2016():
  118. self.setObjectName("MyMainDockingWindow")
  119. # on maya < 2017, the MayaQWidgetDockableMixin.show() magiclly docks the window next
  120. # to the channel controls, attributes editor and modelling toolkit.
  121. self.show(dockable=True, area='right', floating=False)
  122. self.raise_()
  123. # size can be adjusted, of course
  124. self.setDockableParameters(width=420)
  125. self.setSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred)
  126. self.setMinimumWidth(420)
  127. self.setMaximumWidth(600)
  128. if maya_api_version() < MyDockingWindow.MAYA2017:
  129. run2016()
  130. else:
  131. run2017()
  132. def show():
  133. '''
  134. this is the funciton that start things up
  135. '''
  136. global MyDockingWindow
  137. MyDockingWindow = MyDockingWindow(parent=maya_main_window())
  138. MyDockingWindow.run()
  139. return MyDockingWindow
文档更新时间: 2018-06-30 17:42   作者:刘电