PyQt MainWindow as ScriptJob Parent

690 views
Skip to first unread message

Christopher Evans

unread,
Apr 20, 2012, 7:25:35 PM4/20/12
to python_in...@googlegroups.com
First lemme start with: Is it possible to use closeEvent() in Maya? I
cannot get that to work.

Can someone tell me how to feed a custom pyqt mainWindow tool into the
parent flag of a scriptjob?

I have been looking all over teh internets, I think it involves SIP
somehow.. all examples I have seen show QDialog, or mel/python
commands UIs.

Thanks,

--
CE

Chris Gardner

unread,
Apr 20, 2012, 7:56:50 PM4/20/12
to python_in...@googlegroups.com
this works for me:

class tagUi(base_class, form_class):
def __init__(self, parent=getMayaWindow()):

# a bunch of crap here


self.scriptJobId = cmds.scriptJob( e= ["SelectionChanged",
self.selectionCallback], protected=True)
self.show()


def closeEvent(self, e):
"""
Kills the selectionChanged scriptjob and any other gui closed
event stuff
"""

if self.scriptJobId:
print 'killing scriptjob'
cmds.scriptJob(kill=self.scriptJobId, force=True)

super(tagUi, self).closeEvent(e)


can't remember where i found the basis for this code, but it's out
there somewhere on teh interwebs.

cheers,
chrisg

> --
> view archives: http://groups.google.com/group/python_inside_maya
> change your subscription settings: http://groups.google.com/group/python_inside_maya/subscribe

Justin Israel

unread,
Apr 20, 2012, 8:05:37 PM4/20/12
to python_in...@googlegroups.com
On Fri, Apr 20, 2012 at 4:25 PM, Christopher Evans <chris...@gmail.com> wrote:
First lemme start with: Is it possible to use closeEvent() in Maya? I
cannot get that to work.

Can you be more specific about what is giving you trouble? 
This works just fine:

class Window(QtGui.QMainWindow):
    def closeEvent(self, e):
        print "HIT"
 

Can someone tell me how to feed a custom pyqt mainWindow tool into the
parent flag of a scriptjob? 

I have been looking all over teh internets, I think it involves SIP
somehow.. all examples I have seen show QDialog, or mel/python
commands UIs.

The part about getting the maya UI name from a pyqt widget has been asked before.
It goes like this:

import sip
import maya.OpenMayaUI as mui

win = # some PyQt widget
mayaName = mui.MQtUtil.fullName(long(sip.unwrapinstance(win)))

You can also check out 

Justin Israel

unread,
Apr 20, 2012, 8:06:18 PM4/20/12
to python_in...@googlegroups.com
I hit send to fast. Ignore that silly line at the end of my last message :-)

Christopher Evans

unread,
Apr 20, 2012, 8:56:11 PM4/20/12
to python_in...@googlegroups.com
class skinWrangler(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)

Looks like I am creating the window differently than you guys.

I was going to give this skinning tool away free anyways, here's that
few surrounding chunks of code: http://pastebin.com/5tJfvSB7

Thanks,

CE

--
CE

John Patrick

unread,
Apr 20, 2012, 11:16:27 PM4/20/12
to python_in...@googlegroups.com
You need to set the object name to be able to find it in Maya:  http://pastebin.com/T4tJ4GjE
John Patrick
404-242-2675
jspa...@gmail.com
http://www.canyourigit.com

Justin Israel

unread,
Apr 21, 2012, 6:03:10 PM4/21/12
to python_in...@googlegroups.com
You actually don't need to name the window for it to work. Maya will use a default name.

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

def getMayaWindow():
    ptr = mui.MQtUtil.mainWindow()
    return sip.wrapinstance(long(ptr), QtCore.QObject)
        
class Window(QtGui.QMainWindow):    
    def __init__(self, parent=None):
        p = parent or getMayaWindow()
        super(Window, self).__init__(parent=p)
                        
        name = mui.MQtUtil.fullName(long(sip.unwrapinstance(self)))            
        print "Window name", name
        
    def closeEvent(self, e):
        print "TEST"

John Patrick

unread,
Apr 21, 2012, 6:22:51 PM4/21/12
to python_in...@googlegroups.com
I'm not sure if that works on your system, but it doesn't work under Maya2012 in Fedora.

It will print:
Window name MayaWindow|

AFAIK, the most reliable way to get the name of the window is to set it explicitly.

Justin Israel

unread,
Apr 21, 2012, 6:32:00 PM4/21/12
to python_in...@googlegroups.com
You might be right actually. I may have spoke too soon.

Christopher Evans

unread,
Apr 22, 2012, 6:51:12 PM4/22/12
to python_in...@googlegroups.com
If I pipe that 'name' into the 'p' parent flag of the sciptjob, it
lives on after i close the window. Second, integrating your code, the
closeEvent runs at the time I execute the script and the window opens
(weird?) it doesn't run on close.

Justin Israel

unread,
Apr 22, 2012, 7:11:31 PM4/22/12
to python_in...@googlegroups.com
Post your latest code? Maybe you have a strange indent. No idea why the closeEvent would happen right away unless maybe you are letting it get garbage collected immediately. 

Christopher Evans

unread,
Apr 22, 2012, 8:21:28 PM4/22/12
to python_in...@googlegroups.com

Justin Israel

unread,
Apr 22, 2012, 9:57:36 PM4/22/12
to python_in...@googlegroups.com
You have some issues with the way you are loading and using your UI.
Notice that you are loading the UI into self.ui, but then implementing events on self. Your end up calling self.ui.show() from your init which is showing a completely different object than your main window class.

Normally, I would recommend either precompiling your UIC via pyuic4, or, loading it in on the fly and then inheriting from it:

class SkinWrangler(QtGui.QMainWindow, Ui_skinWrangler):
    def __init__(self, parent=None):
        super(SkinWrangler, self).__init__(parent)
        self.setupUi(self)

There are variations of that approach, where some people will store the ui as an object:
self.ui = Ui_skinWrangler()
self.ui.setupUi(self)

The immediate fix for how you are using loadUi is to tell it to set it up on your main window (similar to what setupUi does):

class skinWrangler(QtGui.QMainWindow):
    def __init__(self):

        # other stuff here #
        
        uiPath = "/path/to/ui/test.ui"
        uic.loadUi(uiPath, self)
    
    def closeEvent(self, e):
        print 'CLOSED'

win = skinWrangler()
win.show()  # instead of win.ui.show()

Don't call show() from your init. It should be called by the entry-point function that is launching your UI. 
Now you will see your closeEvent get called when the window is closed.
The reason you were seeing it look like it was occurring during init each time, is that what it was really doing was deleting your previous instance with cmds.deleteUI() which triggers the closeEvent for that previous widget.

Christopher Evans

unread,
Apr 23, 2012, 5:41:43 PM4/23/12
to python_in...@googlegroups.com
Thanks for your help, you cleared some things up for me. I had used
this self.ui.blah approach because I saw that at a company I worked
for, it's also the stated 'single inheritance' approach in the QT docs
here: http://www.riverbankcomputing.co.uk/static/Docs/PyQt4/html/designer.html

In your example I would get this:
TypeError: ('Wrong base class of toplevel widget', (<class
'__main__.skinWrangler'>, 'QDialog'))

So I made it a QDialog and added this string from the docs:

def __init__(self):
>>>QtGui.QDialog.__init__(self)

Now it works fine! (the onClose)

CE

Justin Israel

unread,
Apr 23, 2012, 6:45:29 PM4/23/12
to python_in...@googlegroups.com
In the single inheritance approach, the key line you woud have been missing was:

    self.ui.setupUi(self)

.. which would set up the ui onto the current class.

As for the TypeError, your Ui class should be a generic class if it came straight from designer. Looking something like this:

class Ui_skinWrangler(object):
    def setupUi(self, skinWrangler):

If you created that file by hand or modified it, and it was a QMainWindow subclass, then yea this would not work for you. Usually you just generate them from designer and use them directly in your implementation, unmodified. 
Reply all
Reply to author
Forward
0 new messages