Dumb Question - Docking Qt UIs inside Maya

So I know about the dockControl command inside maya to dock a UI, but i want to go a bit deeper and dock my UI’s at a lower level using the addDockWidget method on QMainWindows.

I can create a QDockWidget and it will Dock to the Maya UI - however, it’s docking below the existing UI, instead of replacing the UI in the dock. In other words, when I dock my new UI, it’s splitting the dockable region into an upper and lower half.

Basically it looks like when I add my new widget to the region, I am too high up in Maya’s UI heirarchy - I need to walk down a few layouts until I get to the attribute editor/channel box area.

I am looking for advice on walking the Maya Qt Heirarchy until I find the right place to dock my widget.

Thanks!

For docking: http://stackoverflow.com/questions/19086090/how-to-dock-a-pyqt-window-in-maya/19089528#19089528

Here is a snippet that prints a complete layout of the maya interface (warning: quite huge, around 15k lines)

import maya.OpenMayaUI as mapiui
import PyQt4.QtGui as QtGui
import PyQt4.QtCore as QtCore
import sip
        
def uiRec(p_parent, p_string):
    for child in p_parent.children():
        tmpStr = '	' + p_string
        print tmpStr, type(child), child.objectName()
        uiRec(child, tmpStr)
        
main = mapiui.MQtUtil.mainWindow()
main = sip.wrapinstance(long(main), QtCore.QObject) 

uiRec(main,'')

Thanks for the reply. The pymel/dockControl stuff is useful, but I am intentionally, (and perhaps foolishly), going deeper to the Qt level to perform the docking operations.

it seems the problem I am running into is the Attribute Editor/Channel Box/ etc. is a Tabbed Layout with Dock Widget, and when I add my new Dock Widget it is not going into the Tabbed Layout, but instead splitting the RightDock Area into two vertical sub sections.

seems like i need to get my new dock widget into the tabbed widget.

Did you ever manage to solve this? I’ve gotten pretty far with the core Qt stuff, but I’m stuck with trying to force dock my window. Calling addDockWidget on the MayaWindow for my dockwidget does nothing. I can dock it manually, just not through code.

Also, that code from Jason, that nezus linked is a bit ‘dirty’ if you ask me; mixing the pm controls with Qt controls and doing a bunch of workarounds isn’t great. It’s also much harder to use that code in a clean system that doesn’t delete and recreate your UI every time (but rather shows and hides it).

I was able to get it working, here is a code snippet


class MyToolbox(QtGui.QMainWindow):
    def __init__(self, parent=getMayaMainWindow()):
        super(MyToolbox, self).__init__(parent)

        self.setObjectName('MyToolbox')

        self.dockCtrl = cmds.dockControl(aa=['right', 'left'], 
                                         a='right', 
                                         content=self.objectName(), 
                                         w=370, 
                                         label='My Tool Box')

just make sure to delete the dockControl when you close the UI


    def closeEvent(self, event):
        cmds.deleteUI(self.dockCtrl)
        try:
            QtGui.QWidget.closeEvent(self, event)
        except: pass

Hey, well that’s kind of what i want to avoid, having to use things like cmds.dockControl.

We looked into it a bit further, and it seems like it’s a bug within Pyside:

passing in a pure QdockWidget with mayawindow.addDockwidget() works fine.
My class however, inherits from Qdockwidget and expands it. passing that into the same function, does not work. Doesn’t make any sense!

Here’s some code to test if anybody cares:

UI file to load/inherit (save as Windowtest.ui on C: if you don’t want to modify the other code):

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>WindowTest</class>
 <widget class="QDockWidget" name="WindowTest">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>320</width>
    <height>240</height>
   </rect>
  </property>
  <property name="floating">
   <bool>true</bool>
  </property>
  <property name="windowTitle">
   <string>DockWidget</string>
  </property>
  <widget class="QWidget" name="dockWidgetContents">
   <layout class="QVBoxLayout" name="verticalLayout">
    <item>
     <widget class="QListView" name="listView"/>
    </item>
    <item>
     <widget class="QPushButton" name="pushButton">
      <property name="text">
       <string>PushButton</string>
      </property>
     </widget>
    </item>
   </layout>
  </widget>
 </widget>
 <resources/>
 <connections/>
</ui>

Use this code to test it:

from PySide import QtGui, QtCore
import shiboken as qtBindingGenerator
from cStringIO import StringIO
import xml.etree.ElementTree as xml


def loadUiType(uiFile):
    import pysideuic

    #Pyside lacks the "loadUiType" command, so we have to convert the ui file to py code in-memory first
    #and then execute it in a special frame to retrieve the form_class.

    parsed = xml.parse(uiFile)
    widget_class = parsed.find('widget').get('class')
    form_class = parsed.find('class').text

    with open(uiFile, 'r') as f:
        o = StringIO()
        frame = {}

        pysideuic.compileUi(f, o, indent=0)
        pyc = compile(o.getvalue(), '<string>', 'exec')
        exec pyc in frame

        #Fetch the base_class and form class based on their type in the xml from designer
        form_class = frame['Ui_%s'%form_class]
        base_class = eval('QtGui.%s'%widget_class)
    return form_class, base_class

def getTopWindow():
    '''
    Gets maya top window
    '''
    import maya.OpenMayaUI as apiUI

    ptr = apiUI.MQtUtil.mainWindow()
    if ptr is not None:
        return qtBindingGenerator.wrapInstance(long(ptr), QtGui.QMainWindow)

    else:
        print "No window found"


#UI File is defined and loaded here
uiFile = "C:/WindowTest.ui" #change to wherever your UI file is located.
pform, pbase = loadUiType(uiFile)



class WindowTest(pform, pbase):
    def __init__(self, parent=getTopWindow()):
        super(WindowTest, self).__init__(parent)

        self.setupUi(self)
        self.show()

def TestDockingCustom():
    '''
    This creates the custom UI element and tries to dock it
    '''
    docktest = WindowTest()
    getTopWindow().addDockWidget(QtCore.Qt.LeftDockWidgetArea , docktest)

def TestDockingStandard():
    '''
    This creates a vanilla QDockWidget and tries to dock it
    '''
    docktest = QtGui.QDockWidget()
    getTopWindow().addDockWidget(QtCore.Qt.LeftDockWidgetArea , docktest)
    
TestDockingCustom()
TestDockingStandard()