Maya dockable windows boilerplate, with support for PySide2

maya
qt
python

#1

Hi!

I’m currently looking to find or create a boilerplate for dockable PySide window for Maya, as the title says.

Until now I was using MayaQDockWidget from MayaMixin. And it was working fine for my tool’s windows until Maya 2017 came out. It still works fine, but it only docks on far-left and far-right of the screen, while there are a lot more dock options now.

So, then I came across this liorbenhorin/Simple_MayaDockingClass.py

And it works perfectly, I was able to create a base class and base module out of this, which I can use. It docks everywhere, next to the outliner, within any tab, etc, allowing for full customization.

But, there’s one problem, now self.setMenuBar(self.menubar_thing) command from PySide wont’ work. There are no errors, menu bar just does not appear. As well as QStatusBar with self.setStatusBar() command.

I’m clearly missing something, and hope someone might help. I’m looking either for a solution for the menu bar and status bar problems with current setup (the one from GitHub), or an alternative boilerplate for Maya dockable pyside window, compatible with old and new Maya versions (well, at least starting with 2014).

So, the final goal here is to find and compile a base module and class for Dockable PySide windows in Maya, with support of menuBar and statusBar and backwards compatible with older versions of Maya (2014-2016). Backwards compatibility can, of course, be in a form of fallback to old docking methods.

Thanks for your time :slight_smile:


#2

So looking at that code, can’t test it currently, but I think I’ve got a rough idea of what it is trying to do.
What happens when you try self.ui.setMenuBar?


#3

I tried that, and it did not work.

I’m not sure right now, but I think it said that self.ui has no such function as setMenuBar()

By the way, if I use similar code in Maya 2016 with dockControl instead of workspaceControl - menubars work fine. But dockControl works quite differently, however. I’m currently also researching studiolibrary’s and Red9’s code to find out how it’s done there. In Red9’s code I found a few comments which clearly show author’s frustration with new workspaceControl :smiley:

New docking system is clearly not backwards compatible, as Maya 2016 and below don’t have workspaceControl command at all. And old dockControl or Docking class from MayaMixin have limited docking functionality.


#4

Oh, ok! Haha. Funny, StudioLibrary chose to just drop support of old versions. Their UI if focused on Maya 2017+. Okay :smiley:


#5

Yes, just tried it and self.ui.setMenuBar shows PySide2.QtWidgets.QWidget object has no attribute ‘setMenuBar’`.


#6

“setMenuBar()” is a convenience method for a QMainWindow instance. I believe setMenuBar() or menuBar() will work if your subclassing QMainWindow, not a QWidget. Otherwise, you will need to create a QMenuBar() and then add it manually to your widget’s layout.


#7

@gonzalimator indeed. Yes, I did try to subclass QMainWindow instead of QWidget, and it did not work. Not with cmds.workspaceControl as a parent at least. And I’m not sure if I can somehow subclass workspaceControl and add QMainWidget into it’s inheritance?

And I also came to a conclusion that I will probably have to create my own ‘top-level’ layout to include menu and status bars in it.


#8

I threw something up here that might answer this question. https://github.com/griffinanimator/share


#9

@griffinanimator1 Hi, thanks for the snippet!

However, when I tried it, it did not work. I had to remove a few things from the code, but I don’t think they are the reason.

I removed this part:

#WINDOW_ICON = os.environ['FXS_ICON_PATH'] + '/FXS.png'
#pixmap = QtGui.QPixmap(WINDOW_ICON)
#icon = QtGui.QIcon(pixmap)

And this:

#self.ui.setWindowIcon(self.icon_img)

And it shows this error when I try to run it with win = dock_window(Test_UI):

# Error: AttributeError: file test.py line 167: 'PySide2.QtWidgets.QWidget' object has no attribute 'setCentralWidget'

I tried fiddling a bit more with this code, but it just throws more and more errors in Maya 2017


#10

Here’s the code as I have it right now.

It is working, more or less. When I adapted my BroDynamics main window to use this class as parent class, it works perfectly both in Maya 2016 and in Maya 2017. So I suppose I’m on the right track here.

But when I try this class TestChildWindow(BroDockingUI): - it works in 2017, but shows blank window in 2016. So still working on it.


#11

My apologies. I’m still at work and was hasty. I can prove a better example. Another consideration is that I’m using the custom QT package to provide backward compatibility.


#12

@griffinanimator1 No worries. What package is that? If you’re referring to Qt.py - I’m using it as well, in this case at least.

So, another update. And it’s very weird.

So, take that code from my post above - it works in Maya 2017, it sort-of worked in Maya 2016, no errors, BUT the dockControl window was blank. It was there, it was created, it just did not have any contents in it. Even though it should. So I decided to check if my widget was even a child of that control.

So I added one line here (marked with <-- This line!):

    main_control = cmds.dockControl(DOCK_CONTROL_NAME, l=dialog_class.DOCK_LABEL_NAME, a='right', con=dialog_class.CONTROL_NAME, ret=False)
    # 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()))  # <-- This line!

And guess what, it worked! But why?! :smiley:


#13

Updated code on github with latest working version. Any ideas and suggestions welcomed.


#14

I will take a look this evening. I went through this same issue and may be able to clear things up. In the interim, I updated the code on Github. It should work correctly now but I cant properly test from here.


#15

The Mixin dock only docking on the left and right is a new one to me… maybe it’s worth taking another look at your original solution, Nix? My PySide2 dock builder is:

def buildMayaDock(base):
"""Get a Maya-dockable UI from the given qwidget subclass.
This is ONLY for 2017+, as MRO demands that mDock comes first, 
but QDockWidget must come after any compiled designer class"""
try:
    assert issubclass(base, QtWidgets.QWidget)
except (TypeError, AssertionError):
    print("Base class argument {0} must inherit QWidget in "
          "order to be a Maya dock widget.".format(base))
    return

class MayaDockUi(MayaQWidgetDockableMixin, base):
    """Some dock signals are replaced by method stubs:
    - dockCloseEventTriggered
    - floatingChanged
    ALSO, .show() method has args the FIRST TIME it is called, by default:
        dockable=False, floating=True, area="left", allowedArea="all", 
        width=None, height=None, x=None, y=None"""
    def __init__(self, parent=None):
        super(MayaDockUi, self).__init__(parent=parent)

return MayaDockUi

For me they can dock anywhere. I imagine your builder was similar… was the docking limited even if you play with the arguments for the initial .show()?


#16

@clamdragon Hmm… Weird, now that you say this. I tried just this:

class DockingUI(MayaQWidgetDockableMixin, QWidget):
    def __init__(self, parent=None):
        super(DockingUI, self).__init__(parent)

my_dock = BroDockingUI().show(dockable=True)

And indeed it docks fine. I wonder why it did not work before.

But when I try to load my BroDynamics window this way Maya crashes with Fatal Error.


#18

So, here’s updated version. There we go.

https://gist.github.com/Nixellion/f28051f1b53fee8b11a2fcd36ca244c3

Seems to be working fine in Maya 2016, 2017, 2018

Any notes, comments? Something to improve?


#19

Just seen this comment, yes there are a few “frustrated” comments in the Red9 Studio Pack, Docking and workspaces are a pain!


#20

@Mark-J Hi there, yeah, I had a laugh while reading them :smiley:

Well, as a matter of fact, it turned out that I was just not calling MayaQWidgetDockableMixin correctly. Like literally I was just passing wrong arguments to the “show()” :slight_smile: Once I fixed that everything started working and docking properly in all Maya versions (well, at least those I tested with: 2015-2017).

I’ll still need to find a way to dock it next to Attribute editor on startup, however.

So I wonder, is there any specific reason you’re going with cmds.workspaceContro l instead of inheriting from MayaQWidgetDockableMixin?


#21

You should read some of the older comments in the Red9_AnimationUtils. Workspaces are a pain and whilst it’s been a while since I did that code from what I remember the dock widget no longer worked as expected in 2017 ( we took the decision in StudioPack at least to stay in cmds for the UI’s. ProPack is all QT and I think we’ll start to do migrate and upgrade the StudioPack soon when we get time)