PyQt Widget in Maya Mel UI problem

Hello everybody !

I need to put a PyQt (for example a QListWidget) widget in UI which is created using MEL (i mean my tool UI… not some Maya main window place)
There is a frameLayout which is resizing with the main window, but the problem is that my QListWidget in that frameLayout does not.
It has a fixed size. Looks like my QHBoxLayout setup doesn’t work but i dont know why.
Here is the code.

import sip
from PyQt4 import QtGui, QtCore
import maya.OpenMayaUI as omui
import maya.cmds as m

UI_WIN_NAME = 'WidgetInMayaUI_win'
UI_WIN_TITLE = 'WidgetInMayaUI window'

class WidgetInMayaUI():

    def __init__(self) :
        self.ui_createUI()

    def ui_createUI(self):

        if m.window(UI_WIN_NAME, exists = True) : m.deleteUI(UI_WIN_NAME, window = True)
        self.window = m.window( UI_WIN_NAME, title = UI_WIN_TITLE, maximizeButton = False,)

        self.ui_LAY_mainForm = m.formLayout()

        self.attachLayout = m.frameLayout(labelVisible = False)

        m.setParent(self.ui_LAY_mainForm)

        self.ui_LAY_toolbar = m.columnLayout()
        m.symbolCheckBox(image = 'sortName', height = 35, width = 35)
        m.symbolCheckBox(image = 'sortName', height = 35, width = 35)
        m.symbolCheckBox(image = 'sortName', height = 35, width = 35)

        m.formLayout(self.ui_LAY_mainForm, e = True, attachForm = (self.attachLayout, 'top', 2))
        m.formLayout(self.ui_LAY_mainForm, e = True, attachControl = (self.attachLayout, 'right', 2, self.ui_LAY_toolbar))
        m.formLayout(self.ui_LAY_mainForm, e = True, attachForm = (self.attachLayout, 'bottom', 2))
        m.formLayout(self.ui_LAY_mainForm, e = True, attachForm = (self.attachLayout, 'left', 2))

        m.formLayout(self.ui_LAY_mainForm, e = True, attachForm = (self.ui_LAY_toolbar, 'top', 2))
        m.formLayout(self.ui_LAY_mainForm, e = True, attachForm = (self.ui_LAY_toolbar, 'right', 2))
        m.formLayout(self.ui_LAY_mainForm, e = True, attachForm = (self.ui_LAY_toolbar, 'bottom', 2))
        m.formLayout(self.ui_LAY_mainForm, e = True, attachNone = (self.ui_LAY_toolbar, 'left'))

        self.attachLayoutQ = sip.wrapinstance(long(omui.MQtUtil.findLayout(self.attachLayout)), QtCore.QObject)
        self.QtLayout = QtGui.QHBoxLayout()
        self.attachLayoutQ.setLayout(self.QtLayout)

        self.list = QtGui.QListWidget()
        self.list.addItem('aaa'); self.list.addItem('bbb'); self.list.addItem('ccc')
        self.QtLayout.addWidget(self.list)
        self.list.setParent(self.attachLayoutQ)

        m.showWindow(self.window)

def run():
    WidgetInMayaUI()

So how can i embed a QWidget in this UI and make it resize with frameLayout?

I haven’t tried this, so maybe someone else has a better suggestion. I might try adding a piece of UI from MEL that behaves the way you want, then hide it and have your widget pick up it’s dimensions.

Hopefully someone will have a better solution…

You shouldn’t want or need to do that (not sure if it’s your choice). Could you explain why you would want to do this and not just use PyQt? are you trying to add a new tool to a legacy tool UI? Either way I haven’t tried this either but I’m guessing you would have to do something like this:


def melui_to_qobject(melui):
    ptr = apiUI.MQtUtil.findLayout(melui)
    if ptr is not None:
        return sip.wrapinstance(long(ptr), QtCore.QObject)


this is not tested, but it would be something like that, it would return a QObject that you could use as a parent to your widget.

more info in the docs Maya API: MQtUtil Class Reference

NOTE:“The safest way to use the Qt API from within Maya is to create your own Qt window and populate it with your own controls.”

Either way I haven’t tried this either but I’m guessing you would have to do something like this:


def melui_to_qobject(melui):
    ptr = apiUI.MQtUtil.findLayout(melui)
    if ptr is not None:
        return sip.wrapinstance(long(ptr), QtCore.QObject)

As you can see in my code i’m doing it exactly like you’ve said


        self.attachLayoutQ = sip.wrapinstance(long(omui.MQtUtil.findLayout(self.attachLayout)), QtCore.QObject)
        self.QtLayout = QtGui.QHBoxLayout()
        self.attachLayoutQ.setLayout(self.QtLayout)

        self.list = QtGui.QListWidget()
        self.list.addItem('aaa'); self.list.addItem('bbb'); self.list.addItem('ccc')
        self.QtLayout.addWidget(self.list)
        self.list.setParent(self.attachLayoutQ)

then i’ve added a QHBoxLayout to wrapped instance and then i add my QListWidget.
The problem is that widget is not resizing instead of how to embed a widget.
I’ve tried to set some size policies but nothing happens (actually i’m almost sure that problem is not in size policies)

You shouldn’t want or need to do that (not sure if it’s your choice). Could you explain why you would want to do this and not just use PyQt? are you trying to add a new tool to a legacy tool UI?

Actually i’m writing some Enhanced Outliner. A standard Maya outliner with some buttons around it, some mode selectors and so on.
And its working very well. And i’ve got some search tool in it. The result of this search is placed in Maya textScrollList.
I need additional control over widget so i’ve decided to insert a Qt widget in it to display search results.
Why Qt in Maya UI. First of all there is 2 choises:

  1. Maya Outliner Panel in my Qt window
  2. Qt Widget in Maya UI window
    I’ve tried to embed Outliner in Qt but in my opinion outliner panel is not so simple construction to insert.
    There was many problems while creating it and parenting to Qt: outliner filter doesnt working, cant find window to create menus for outliner, outliner doesnt refreshing and looking dark :?:, and so on.
    Almost all of them i’ve managed to solve but still i’ve got some. Anyway i think that outliner panel is much more complicated construction to insert and to interfere to its mechanic.
    I’m almost sure that this way is leading to many possible strange behaviors of my outliner.
    So the second approach is much less painful (i think)…
    And everything working, tested and outliner is doing what is expected … except resizing this widget. QWidget is not obey resizing of layout its parented.

So that the question of this post.

well since the Qt ui is a child of the Mel ui you could try using Maya’s UI events to try and capture when a event fires and then have that run a function that manually sets the width and height of the Qt widget based on the new ui width and height. I don’t think the ui is capable of this on it’s own, hence it’s not updating the size. I’m not too familiar with Maya’s events but you might try the mouseDrag?
http://download.autodesk.com/us/maya/2009help/API/class_m_event.html

[QUOTE=mattanimation;17006]well since the Qt ui is a child of the Mel ui you could try using Maya’s UI events to try and capture when a event fires and then have that run a function that manually sets the width and height of the Qt widget based on the new ui width and height. I don’t think the ui is capable of this on it’s own, hence it’s not updating the size. I’m not too familiar with Maya’s events but you might try the mouseDrag?

Actually this is not a solution i’m looking for, and i’m sure i should not hook up some Maya API mouse events on drag and so on. It should be much much easier.
All Maya UI is Qt UI. There is widgets and there is layouts. My QListWidget worng behavior lies definitely in wrong parenting or layout assignment.
The same effect of “not resizing widgets” can be reprodused in pure Qt window just parenting widget and not using layouts:

import sip
from PyQt4 import QtGui, QtCore
import maya.OpenMayaUI as omui

def getMayaWindow():
    ptr = omui.MQtUtil.mainWindow()
    if ptr is not None:
        return sip.wrapinstance(long(ptr), QtCore.QObject)


class BadQtUI(QtGui.QMainWindow):
    def __init__(self, parent=getMayaWindow()):
        super(BadQtUI, self).__init__(parent)
        self.centralWidget = QtGui.QWidget()
        self.setCentralWidget(self.centralWidget)
        self.list = QtGui.QListWidget()
        self.list.setParent(self.centralWidget)

def run():
    myWindow = BadQtUI()
    myWindow.show()

So somehow my QListWidget (in my starting post) cannot see Qt Layout or wrong parented or something like this.
And i’m sure it can be fixed just with correct code.

Any way this topic actually can be renamed to “How to correctly insert Qt Widgets in Maya MEL UI”

Okay so here’s whats going on with this in your first example code, and here’s my fixed version below.

You are creating a new layout and trying to replace the existing layout of the frameLayout. Instead just use it’s existing layout and addWidget your item to it.

Don’t do it this way:

        self.QtLayout = QtGui.QHBoxLayout()
        self.attachLayoutQ.setLayout(self.QtLayout)

        self.list = QtGui.QListWidget()
        self.list.addItem('aaa'); self.list.addItem('bbb'); self.list.addItem('ccc')
        self.QtLayout.addWidget(self.list)

You are calling “setParent” on your widget AFTER adding it to a layout. This undoes the layout change, as setting the parent clears it’s current layout.
Don’t do this:

        self.list.setParent(self.attachLayoutQ)

You are dealing with a frameLayout, which is not a single Qt widget but rather a compound widget.
You are assuming that “findLayout” returns something usefull, sadly it doesn’t… MQtUtil.findLayout does NOT return the layout (I know right… ) but rather the FIRST “fake child layout widget” that it finds.

frameLayout comes out to something like this in actual Qt widgets:

frameLayout9 (QVBoxLayout) - window1|frameLayout9"
mayaLayoutInternalWidget (QWidget) - window1|frameLayout9|mayaLayoutInternalWidget"  (This is what MQtUtil.findLayout finds for a frameLayout, as does MQtUtil.findControl in this case)
mayaLayoutInternalWidget (QFrame) - window1|frameLayout9|mayaLayoutInternalWidget"
    mayaLayoutInternalWidget (QVBoxLayout) - window1|frameLayout9|mayaLayoutInternalWidget"  (This is the real layout you want to add your widget to) 

Which is because when you create a frameLayout there is no equivalent Qt control, so it builds one out of multiple.

The easiest way I have found to reliably parent into ANY Maya mel widget from Qt is to create a dummy widget like a button in mel in the right spot, and then query it’s layout and “addWidget” your new Qt object to that layout.
I also suggest writing a script to recurse the widget tree by calling .children() and then printing the maya name/qt widget info to learn about the internal structure of their compound widgets.

So, knowing that the QFrame we want to parent to is the last child of the QVBoxLayout that makes up the frameLayout, we just grab index -1 from it’s .children() and call .addWidget() on it’s .layout()

import sip
from PyQt4 import QtGui, QtCore
import maya.OpenMayaUI as omui
import maya.cmds as m

UI_WIN_NAME = 'WidgetInMayaUI_win'
UI_WIN_TITLE = 'WidgetInMayaUI window'

class WidgetInMayaUI():

    def __init__(self) :
        self.ui_createUI()

    def ui_createUI(self):

        if m.window(UI_WIN_NAME, exists = True) : m.deleteUI(UI_WIN_NAME, window = True)
        self.window = m.window( UI_WIN_NAME, title = UI_WIN_TITLE, maximizeButton = False,)

        self.ui_LAY_mainForm = m.formLayout()

        self.attachLayout = m.frameLayout(labelVisible = False)

        m.setParent(self.ui_LAY_mainForm)

        self.ui_LAY_toolbar = m.columnLayout()
        m.symbolCheckBox(image = 'sortName', height = 35, width = 35)
        m.symbolCheckBox(image = 'sortName', height = 35, width = 35)
        m.symbolCheckBox(image = 'sortName', height = 35, width = 35)

        m.formLayout(self.ui_LAY_mainForm, e = True, attachForm = (self.attachLayout, 'top', 2))
        m.formLayout(self.ui_LAY_mainForm, e = True, attachControl = (self.attachLayout, 'right', 2, self.ui_LAY_toolbar))
        m.formLayout(self.ui_LAY_mainForm, e = True, attachForm = (self.attachLayout, 'bottom', 2))
        m.formLayout(self.ui_LAY_mainForm, e = True, attachForm = (self.attachLayout, 'left', 2))

        m.formLayout(self.ui_LAY_mainForm, e = True, attachForm = (self.ui_LAY_toolbar, 'top', 2))
        m.formLayout(self.ui_LAY_mainForm, e = True, attachForm = (self.ui_LAY_toolbar, 'right', 2))
        m.formLayout(self.ui_LAY_mainForm, e = True, attachForm = (self.ui_LAY_toolbar, 'bottom', 2))
        m.formLayout(self.ui_LAY_mainForm, e = True, attachNone = (self.ui_LAY_toolbar, 'left'))

        self.attachLayoutQ = sip.wrapinstance(long(omui.MQtUtil.findLayout(self.attachLayout)), QtCore.QObject)
        layout = self.attachLayoutQ.children()[-1].layout()

        self.list = QtGui.QListWidget()
        self.list.addItem('aaa'); self.list.addItem('bbb'); self.list.addItem('ccc')
        layout.addWidget(self.list)

        m.showWindow(self.window)


WidgetInMayaUI()

Thank you very much Nathan !!!
This is what i’m exactly looking for - all this explanation about internal structure of Maya UI in Qt terms and my mistakes also!