Adding controls to the Hypershade

372 views
Skip to first unread message

Michael Boon

unread,
Nov 2, 2014, 7:29:36 PM11/2/14
to python_in...@googlegroups.com
Hi,

four questions below...

I'm looking to add some buttons and menus to the Hypershade. I'm new to working with the existing Maya UI so maybe I'm doing this all wrong. 

This is the best way I've found to get the Hypershade window:
    p = pm.getPanel(scriptType='hyperShadePanel')
    assert len(p) == 1
    hypershade
= p[0].getControl()

I'd read that the Maya UI was all Qt now, but this method gets me a pymel.core.uitypes.Panel. Is Hypershade built in Qt? 

To get individual layouts, the only way I've found is to know the structure in advance. For example:
    mainForm = pm.layout(hypershade, query=True, childArray=True)[0]
    assert mainForm.lower() == 'mainform'

That all seems pretty delicate. Is there a more robust way to get (for example) the toolbar at the top of Hypershade?

To make all that actually work, I would need to do it each time the Hypershade opens. Can I get a callback when the Hypershade window is opened? I can't see any MMessage or callback that would tell me.

And finally, is there any good help on MMessage anywhere? The Autodesk help is a decent reference, if you already know what you can do.

Thanks,

Boon

Justin Israel

unread,
Nov 2, 2014, 8:12:08 PM11/2/14
to python_in...@googlegroups.com
On Mon, Nov 3, 2014 at 1:29 PM, Michael Boon <boon...@gmail.com> wrote:
Hi,

four questions below...

I'm looking to add some buttons and menus to the Hypershade. I'm new to working with the existing Maya UI so maybe I'm doing this all wrong. 

This is the best way I've found to get the Hypershade window:
    p = pm.getPanel(scriptType='hyperShadePanel')
    assert len(p) == 1
    hypershade
= p[0].getControl()

I'd read that the Maya UI was all Qt now, but this method gets me a pymel.core.uitypes.Panel. Is Hypershade built in Qt? 

Correct. The whole UI is a bit Qt application. 
 

To get individual layouts, the only way I've found is to know the structure in advance. For example:
    mainForm = pm.layout(hypershade, query=True, childArray=True)[0]
    assert mainForm.lower() == 'mainform'

That all seems pretty delicate. Is there a more robust way to get (for example) the toolbar at the top of Hypershade?

Either way, this whole thing will be delicate, since playing with and manipulating Maya's own application structure can be unstable. Some of the safer things you can do are docking your own widgets, or adding to the main menu. But if you modify temporary windows or try to rely on finding child widgets, you are at the mercy of whatever changes Autodesk makes in the future, or for any undocumented behaviour to occur and undo or mix up what you do. 

That being said, you can get the Qt reference to components within the application. What you are getting so far is just the Maya native UI stuff, that aligns with the commands/mel api. So even though the UI is in Qt, they have an abstraction over it and maintains the compatibility of the native UI interface, which has a more limited exposure. 

Something like this would allow you to get the reference to the hypershade dialog, and to inspect its children:
import maya.OpenMayaUI as mui
from PySide import QtGui, QtCore
import shiboken

p = cmds.getPanel(scriptType='hyperShadePanel')
ptr = mui.MQtUtil.findControl(p[0])
dlg = shiboken.wrapInstance(ptr, QtGui.QDialog)
dlg.findChildren(QtGui.QMenuBar)
 


To make all that actually work, I would need to do it each time the Hypershade opens. Can I get a callback when the Hypershade window is opened? I can't see any MMessage or callback that would tell me.

I think a similar question was asked in the last week or two? Someone was asking for a way to know when some other window was opened, to be able to hook into it. As far as I know, there isn't an official API approach to knowing when a specific window is opened or closed, and it might require an event filter on the QApplication itself, to watch for QShowEvents, and then checking the objectName() 
 

And finally, is there any good help on MMessage anywhere? The Autodesk help is a decent reference, if you already know what you can do.

Thanks,

Boon

--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/460c6e34-1e31-45d2-913f-31d1dde1f880%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Michael Boon

unread,
Nov 3, 2014, 10:43:41 PM11/3/14
to python_in...@googlegroups.com
Thanks very much.

I think I have the basic concept working now.

To get a callback when the Hypershade is opened and closed, I've installed an eventFilter on the Maya main window, something like this:
    class HyperShadeEventFilter(QtCore.QObject):
        def eventFilter(self, object, event):
            if event.type() == QtCore.QEvent.Type.ChildPolished:
                child = event.child()
                if 'hyperShadePanel' in child.objectName():
                    doStuff()
            elif event.type() == QtCore.QEvent.Type.ChildRemoved:
                child = event.child()
                if 'hyperShadePanel' in child.objectName():
                    doStuff()

(I didn't see any events occurring on the QApplication.)
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+unsub...@googlegroups.com.

Arvid Schneider

unread,
Nov 5, 2014, 7:44:56 PM11/5/14
to python_in...@googlegroups.com
Hey Michael, 

how did you install the eventFilter on the MayaMainWindow?

Michael Boon

unread,
Nov 5, 2014, 8:25:13 PM11/5/14
to python_in...@googlegroups.com
In the __init__ of that class, something like this:
    ptr = mui.MQtUtil.mainWindow()
    mainWin
= shiboken.wrapInstance(long(ptr), QtGui.QWidget)
   
mainWin.installEventFilter(self)

Arvid Schneider

unread,
Nov 6, 2014, 5:45:51 AM11/6/14
to python_in...@googlegroups.com
Weird, thats what I did. I ll check into that later.
Thanks though :)

Arvid Schneider

unread,
Nov 9, 2014, 7:11:12 PM11/9/14
to python_in...@googlegroups.com
Could you please help me out further. I cant even install the event filter with the hypershade. 

import maya.OpenMayaUI as mui
import shiboken
from PySide import QtGui
from PySide import QtCore

class HyperShadeEventFilter(QtCore.QObject):
   
def __init__(self):

        ptr
= mui.MQtUtil.mainWindow()
        mainWin
= shiboken.wrapInstance(long(ptr), QtGui.QWidget)
        mainWin
.installEventFilter(self)

       
   
def eventFilter(self, object, event):

       
if event.type() == QtCore.QEvent.Type.ChildPolished:
            child
= event.child()

           
if 'renderViewWindow' in child.objectName():
               
print 'OPEN'

       
elif event.type() == QtCore.QEvent.Type.ChildRemoved:
            child
= event.child()

           
if 'renderViewWindow' in child.objectName():
               
print 'CLOSE'
HyperShadeEventFilter()

Justin Israel

unread,
Nov 9, 2014, 11:16:16 PM11/9/14
to python_in...@googlegroups.com
Replace: QtCore.QEvent.Type.ChildPolished
with : event.ChildPolished

Replace: QtCore.QEvent.Type.ChildRemoved
with: event.ChildRemoved

To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/78a82075-e02f-48a4-8c3c-547c27ba4a77%40googlegroups.com.

Arvid Schneider

unread,
Nov 10, 2014, 2:10:13 AM11/10/14
to python_in...@googlegroups.com
Still getting the same error-. 

// Error: '__init__' method of object's base class (HyperShadeEventFilter) not called.

# Traceback (most recent call last):
# File "<maya console>", line 22, in <module>
# File "<maya console>", line 10, in __init__
# RuntimeError: '__init__' method of object's base class (HyperShadeEventFilter) not called. //
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+unsubscribe@googlegroups.com.

--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+unsub...@googlegroups.com.

Marcus Ottosson

unread,
Nov 10, 2014, 2:24:23 AM11/10/14
to python_in...@googlegroups.com

You’ll need to call the __init__ method of the object’s baseclass.

    def __init__(self):
        super(HyperShadeEventFilter, self).__init__()
        ptr = mui.MQtUtil.mainWindow()
        mainWin = shiboken.wrapInstance(long(ptr), QtGui.QWidget)
        mainWin.installEventFilter(self)

To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/92306db8-5990-4b51-979c-bdf5c50f5af8%40googlegroups.com.

For more options, visit https://groups.google.com/d/optout.



--
Marcus Ottosson
konstr...@gmail.com

Justin Israel

unread,
Nov 10, 2014, 2:24:56 AM11/10/14
to python_in...@googlegroups.com
Oh..

You are forgetting to call the super class __init__ in your custom QObject:

super(HyperShadeEventFilter, self).__init__()


To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/92306db8-5990-4b51-979c-bdf5c50f5af8%40googlegroups.com.

Justin Israel

unread,
Nov 10, 2014, 2:25:43 AM11/10/14
to python_in...@googlegroups.com
On Mon Nov 10 2014 at 8:24:20 PM Marcus Ottosson <konstr...@gmail.com> wrote:

You’ll need to call the __init__ method of the object’s baseclass.

Jinx. Buy me a coke.  

Arvid Schneider

unread,
Nov 10, 2014, 2:29:11 AM11/10/14
to python_in...@googlegroups.com
Thanks for all the help!
But dont get an error now. But its still not working. 


class HyperShadeEventFilter(QtCore.QObject):

   
def __init__(self):
       
super(HyperShadeEventFilter, self).__init__()
        ptr
= mui.MQtUtil.mainWindow()
        mainWin
= shiboken.wrapInstance(long(ptr), QtGui.QWidget)
        mainWin
.installEventFilter(self)

       
   
def eventFilter(self, object, event):
       
print event.type()
       
if event.type() == event.Type.ChildPolished:
            child
= event.child()
           
print child.objectName()
           
if 'hyperShadePanel' in child.objectName():
               
print 'OPEN'
       
elif event.type() == event.Type.ChildRemoved:

            child
= event.child()
           
if 'hyperShadePanel' in child.objectName():

               
print 'CLOSE'
HyperShadeEventFilter()

Marcus Ottosson

unread,
Nov 10, 2014, 2:34:22 AM11/10/14
to python_in...@googlegroups.com
Is that the full code? I can't see where you're installing the filter. Don't forget to run installEventFilter() on the target object. 

Have a look at this example.

--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.



--
Marcus Ottosson
konstr...@gmail.com

Justin Israel

unread,
Nov 10, 2014, 2:39:30 AM11/10/14
to python_in...@googlegroups.com
You seem to have only made a partial change from my other suggestion. I had suggested using event.ChildRemove, yet you have kept that extra .Type. bit in there. Isn't that giving you an error about not existing?


Marcus Ottosson

unread,
Nov 10, 2014, 2:41:52 AM11/10/14
to python_in...@googlegroups.com
I don't think he's ever installing the filter, that would explain why it isn't complaining about anything.


For more options, visit https://groups.google.com/d/optout.



--
Marcus Ottosson
konstr...@gmail.com

Arvid Schneider

unread,
Nov 10, 2014, 2:43:31 AM11/10/14
to python_in...@googlegroups.com
Hi Justin,

I ve tried both versions. No errors. 
It seems that the eventFilter does not get properly installed. 

Marcus, I am installing the eventFilter in the class's init like  you suggested. 
Arvid
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+unsub...@googlegroups.com.
--
Marcus Ottosson
konstr...@gmail.com

--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+unsub...@googlegroups.com.

Marcus Ottosson

unread,
Nov 10, 2014, 2:48:58 AM11/10/14
to python_in...@googlegroups.com

Ok, also make sure that you’re keeping the instance of the eventfilter alive at run-time.

For example, this:

def __init__(self):
    filter = MyEventFilter()
    self.installEventFilter(filter)

Will cause filter to get picked up by garbage collection before it gets a chance to do anything. One way is to add it as a member to the class.

def __init__(self):
    self.filter = MyEventFilter()
    self.installEventFilter(self.filter)

Do post the entire code if you can, otherwise I can only guess.


To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/c53c462e-d967-425c-97a3-e4fd15a1ce5f%40googlegroups.com.

For more options, visit https://groups.google.com/d/optout.



--
Marcus Ottosson
konstr...@gmail.com

Arvid Schneider

unread,
Nov 10, 2014, 3:05:06 AM11/10/14
to python_in...@googlegroups.com
SO.. finally. 
Got it to works. Thanks you two for helping me (at 8 am ).
Works with this code perfectly. Thats the whole code btw. 

Arvid

import maya.OpenMayaUI as mui
import shiboken
from PySide import QtGui
from PySide import QtCore


class HyperShadeEventFilter(QtCore.QObject):
   
def __init__(self):
       
super(HyperShadeEventFilter, self).__init__()
        ptr
= mui.MQtUtil.mainWindow()

       
self.Handler = Handler()
        mainWin
= shiboken.wrapInstance(long(ptr), QtGui.QWidget)
        mainWin
.installEventFilter(self.Handler)        
   
               
class Handler(QtCore.QObject):
   
def eventFilter(self, obj, event):
       
if event.type() == event.ChildPolished:
           
print event.type()

            child
= event.child()
           
print child.objectName()
           
if 'hyperShadePanel' in child.objectName():
               
print 'OPEN'

       
elif event.type() == event.ChildRemoved:
           
print event.type()

            child
= event.child()
           
if 'hyperShadePanel' in child.objectName():
               
print 'CLOSE'

       
return super(Handler, self).eventFilter(obj, event)
x
= HyperShadeEventFilter()
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+unsubscribe@googlegroups.com.



--
Marcus Ottosson
konstr...@gmail.com

--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+unsubscribe@googlegroups.com.

--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+unsub...@googlegroups.com.



--
Marcus Ottosson
konstr...@gmail.com

Marcus Ottosson

unread,
Nov 10, 2014, 3:26:03 AM11/10/14
to python_in...@googlegroups.com

You’re very welcome, but something is not right.

Handler is the event filter, yet HyperShadeEventFilter is named “eventfilter”. What is HyperShadeEventFilter doing? You can remove it, and instead do this.

eventfilter = Handler()
mainWin = shiboken.wrapInstance(long(ptr), QtGui.QWidget)
mainWin.installEventFilter(eventfilter)

When run from Maya, Maya will keep eventfilter alive, as it does with all local variables. In a standalone application, this would probably have to be wr


To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/2ff63179-b3f7-4038-9a06-b7cb2a29c679%40googlegroups.com.

For more options, visit https://groups.google.com/d/optout.



--
Marcus Ottosson
konstr...@gmail.com

Arvid Schneider

unread,
Nov 10, 2014, 3:45:49 AM11/10/14
to python_in...@googlegroups.com
Hi Marcus,

this was just a temp name. The handler is now called as an instance in my main class. 
And it works really smooth. 
Thanks again you guys. 
This group is priceless!



--
Marcus Ottosson
konstr...@gmail.com

--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+unsub...@googlegroups.com.



--
Marcus Ottosson
konstr...@gmail.com

Michael Boon

unread,
Nov 11, 2014, 6:15:59 PM11/11/14
to python_in...@googlegroups.com
Your code is much more concise but I don't think it will change the functionality. I think that both forms refer to the same static field of the class.

For example:
from PySide import QtCore
event = QtCore.QEvent(QtCore.QEvent.ChildPolished)
QtCore.QEvent.Type.ChildPolished == QtCore.QEvent.ChildPolished # Result: True # 
event.type() == event.ChildPolished # Result: True # 
event.ChildPolished == QtCore.QEvent.ChildPolished # Result: True # 


To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+unsub...@googlegroups.com.
--
Marcus Ottosson
konstr...@gmail.com

--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+unsub...@googlegroups.com.

Michael Boon

unread,
Nov 11, 2014, 6:18:00 PM11/11/14
to python_in...@googlegroups.com
Ugh, sorry, I assumed my response would get threaded. It was a reply to one of Justin's posts.

Thanks for everyone's help with this. This group really is priceless.

Justin Israel

unread,
Nov 12, 2014, 3:48:59 AM11/12/14
to python_in...@googlegroups.com
My mistake in not checking that the constants are also located under that Type namespace. Didn't realize that.
I knew they are all equal, and that I was just using less typing to refer to it on the instance. My mistake was trying to suggest that the Type namespace was invalid.

To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/bf5fd23f-a645-4d21-8dfa-f51c2b7e8c70%40googlegroups.com.

Simon Weck

unread,
Jun 27, 2022, 1:23:28 PM6/27/22
to Python Programming for Autodesk Maya
I want to revive this conversation.
I converted the code to PySide2 and its all working fine, but the event.child() has no object name most of the time. 
When I open the Render View for example, the ChildPolished and ChildRemoved events are triggered but the object name of the child is empty. I tried to get more information about the child but the child is a QWidget that doesnt look like the Render View at all. 
Is there another way how I can identify which window got opened or close through the event or event.child?

Here is the code I use. I hooked the eventfilter to a window to remove it when I close it.

from PySide2 import QtCore, QtWidgets
from shiboken2 import wrapInstance
import maya.OpenMayaUI as mui

def maya_main_window():
    main_window_ptr = mui.MQtUtil.mainWindow()
    return wrapInstance(int(main_window_ptr),QtWidgets.QWidget)

class RenderViewFilter(QtWidgets.QDialog):
   
    def __init__(self, parent=maya_main_window()):
        super(RenderViewFilter, self).__init__(parent)

        self.setWindowTitle("Test OnRenderViewOpen")
        self.setMinimumSize(200, 100)
   
        self.maya_main_window = parent
        self.maya_main_window.installEventFilter(self)  
       
    def closeEvent(self, event):
        self.maya_main_window.removeEventFilter(self)
   
    def eventFilter(self, obj, event):

        if obj == self.maya_main_window:

           
             if event.type() == QtCore.QEvent.Type.ChildPolished:
                 child = event.child()
                 print(child)
                 print("ChildPolished")
                 print(child.objectName())
                 if 'renderViewWindow' in child.objectName():
                     print("OPEN Render View")

                 
             if event.type() == QtCore.QEvent.Type.ChildRemoved:
                 child = event.child()
                 print(child)
                 print("ChildRemoved")
                 print(child.objectName())
                 if 'renderViewWindow' in child.objectName():
                     print("CLOSED Render View")
             
        return False
       
     
if __name__ == "__main__":
    x = RenderViewFilter()
    x.show()


I have a script that uses a scriptJob that gives me the hook to the close and open event on a window, but I also want to make it work with EventFilters. Here is the code with the scriptJob:

import maya.cmds as cmds
import pymel.core as pm

class WindowWatcher():

    def __init__(self):
        self.window_name = "renderViewWindow"
        self.window_opened = False      

    def check_for_window_open(self):        
        if not self.window_opened:
            if self.window_name in cmds.lsUI(windows=True):
                self.window_opened = True
                print("Render Window opened!")
        else:
            if not self.window_name in cmds.lsUI(windows=True):
                self.window_opened = False
                print("Render Window Closed!")


if __name__ == "__main__":
    render_window_watcher = WindowWatcher()
    cmds.scriptJob(event=["idle", pm.windows.Callback(render_window_watcher.check_for_window_open)])

Sorry if the code is not highlighted I dont really know how to format it :/
Reply all
Reply to author
Forward
0 new messages