Detects window exit in another function

1,357 views
Skip to first unread message

yann19

unread,
Jan 30, 2015, 5:09:46 AM1/30/15
to python_in...@googlegroups.com
I am trying to write a simple function in where a function will detect if the window (from another function is closed)
In the example below, say function a() runs and open a window, if it is closed, this is where function b() picks up and prints that "windows from a() is closed"

I am getting some errors such as "# AttributeError: 'unicode' object has no attribute 'close' # " so I suppose that the command I used is wrong

def a():
    window1
= cmds.window( title="Long Name")
    cmds
.showWindow(window1)
   
def b():
   
if window1.close():
       
print "windows from a() is closed"

def main():
    a
()
    b
()

main
()

Even so, is this possible? Though I am using the maya cmds.window but can this applies to other import modules where it open a window (like its own import dialog)?

Marcus Ottosson

unread,
Jan 30, 2015, 6:12:27 AM1/30/15
to python_in...@googlegroups.com

The code in your example actually throws a NameError.

# Error: NameError: file <maya console> line 6: global name 'window1' is not defined #

Scope

That’s because of something known as scope.
https://docs.python.org/2/tutorial/classes.html#python-scopes-and-namespaces

Have a quick skim through this when you find the time to understand a little more about it. Being a bit dry, it might also be helpful to learn by example.
http://stackoverflow.com/questions/291978/short-description-of-python-scoping-rules

In a nutshell, it’s due to the variable window1 being defined in one function, whilst accessed in another. That variable is actually deleted as soon as the function completes. The reason it might have worked for you when posting this question might have been due to you first declaring it outside of any function, thus making it accessible everywhere.

You can think of it as a hierarchy, where a inner function have access to variables declared outside of it, including outside of any function at all.

AttributeError

Now, why does window1.close() throw a “AttributeError”?

type() and dir() might be able to make things a little more clear about what’s happening here.

>>> window1 = cmds.window( title="Long Name")
>>> window1.close()

# Error: AttributeError: file <maya console> line 2: 'unicode' object has no attribute 'close' # 

>>> type(window1)
# Result: <type 'unicode'> #

What Python tells you here is that window1 is an instance of a class called “unicode”. As it happens, this class doesn’t have the close member you asked for.

>>> dir(window1)
# Result: ['__add__',
 '__class__',
 '__contains__',
 '__delattr__',
 '__doc__',
 '__eq__',
 '__format__',

...

# Discarded for brievety

Hint: Use type() when you need to know more about what a particular variable actually represents, and dir() to look inside of it and find out what it can do for you. In addition, help() will output some help for you.

>>> help(unicode)
help(unicode)
Help on class unicode in module __builtin__:

class unicode(basestring)
 |  unicode(string [, encoding[, errors]]) -> object
 |  
 |  Create a new Unicode object from the given encoded string.
 |  encoding defaults to the current default string encoding.
 |  errors can be 'strict', 'replace' or 'ignore' and defaults to 'strict'.
 |  
 |  Method resolution order:
 |      unicode
 |      basestring
 |      object
 |  
 |  Methods defined here:

...

# Discarded for brievety

cmds.window is not object-oriented

You might have expected your window1 variable to work similar to this?

>>> import PySide.QtGui
>>> window = PySide.QtGui.QWidget()
>>> window.show()
>>> window.close()

As it turns out, even though Maya’s GUI may be all PyQt/PySide, the cmds.window command won’t actually return you a Qt instance. It instead returns a unicode object. Just a string.

>>> print window1
'window1'

To make matters a little more confusing, the name you’ve chosen for your variable just so happens to be the exact same name as Maya decided to name the physical window internally, thus returning what looks like the name of your variable. To visualise what’s actually going on, we can create a number of windows, and look at their names.

>>> window1 = cmds.window(title="Long Name")
>>> print window1
'window2'
.>>> window1 = cmds.window(title="Long Name")
>>> print window1
'window3'

To make things a little easier to understand, let’s give it a name ourselves; myWindow. Now we can give cmds.showWindow this string, instead of a variable.

>>> my_window = cmds.window("myWindow", title="Long Name")
>>> cmds.showWindow("myWindow")  # This is the same thing

Grand finale

So, to answer your question, to detect if an existing window is already open before opening a new one, you can decide upon a name and check for it.

if not cmds.window("myWindow", query=True, exists=True):
    my_window = cmds.window("myWindow", title="Long Name")
    cmds.showWindow(my_window)
else:
    print "Window was already open."

There are of course many ways of achieving this, but the general idea remains the same even in PySide-land and even standalone apps.

Hope it helps.

Best,
Marcus

Justin Israel

unread,
Jan 30, 2015, 2:37:48 PM1/30/15
to python_in...@googlegroups.com

On Sat Jan 31 2015 at 12:12:24 AM Marcus Ottosson <konstr...@gmail.com> wrote:

cmds.window is not object-oriented

Technically... cmds.window is object-oriented. It is just a unicode object instead of an object representing a "window". ;-) Just messing with ya.

 If I interpret the question properly, and you want to detect the closing of the window as opposed to running another function and actively checking, maybe the uiDeleted scriptJob would do what you want?


That would attach an event between the window closing, and a callback, which would fire when the window is deleted. Windows are deleted by default when they are closed unless they are created with retain=True


AK Eric

unread,
Jan 30, 2015, 3:02:34 PM1/30/15
to python_in...@googlegroups.com
You can also use the  API's MUiMessage_addUiDeletedCallback.  Example of that + scriptJob here:

If the window is closed, that callback is triggered, and then you can do anything you want based on that condition.

The difference I've found is, the api callback runs just before the window is deleted (allowing you to grab data from the window before it dies), the scriptJob runs just after.

yann19

unread,
Jan 31, 2015, 9:39:27 AM1/31/15
to python_in...@googlegroups.com
Hi all, thank you for the replies.

I am actually using cmds.windows as an example. In actual fact, on hand, I have a function that was calling an import dialog (It is not maya module, i suppose but rather it came from my company's own modules and I thought using cmds.window will be the same, guess i was very wrong) and I was thinking if it will then be possible to close that particular import dialog..

Sadly, I can only thought of .close() as one of the way but it is not working as well..
Unless there is some sort of way to 'detect' that the dialog was close in that function before proceeding to the next function..


Marcus Ottosson

unread,
Jan 31, 2015, 10:12:22 AM1/31/15
to python_in...@googlegroups.com

If you have the code handy, we could try and figure out exactly what it is and maybe that way figure out how to use it properly.

​In addition to the dir() and type() I mentioned above, there is a member variable on uninstantiated objects in Python that will show you what it consists of called __mro__.

>>> print MySpecialClass.__mro__
(<class '__main__.MySpecialClass'>, <type 'unicode'>, <type 'basestring'>, <type 'object'>)

For example, here we can see that MySpecialClass is actually a subclass of unicode which is another subclass of basestring. MySpecialClass might then look something like this.

class MySpecialClass(unicode):
    def my_method(self):
        print "Hello, World"

Remember, the member doesn’t exist on the instantiated version of this class.

>>> my_special_instance = MySpecialClass()
>>> print my_special_instance.__mro__
AttributeError: 'MySpecialClass' object has no attribute '__mro__'

But if the instantiated version is all you have, then you can first get the class using type()

>>> print type(my_special_instance).__mro__
(<class '__main__.MySpecialClass'>, <type 'unicode'>, <type 'basestring'>, <type 'object'>)

So, for the object that you are working with, if you could post here the result of the .__mro__ member variable, we might be able to figure out whether it’s coming from cmds.window or PySide or something else.

yann19

unread,
Jan 31, 2015, 10:41:07 AM1/31/15
to python_in...@googlegroups.com
Hey Marcus,

Greatly appreciate that you get back to me :)

I will be sure to check it out when I am back to work on Monday. Hopefully that will help resolve the issue I have on hand :x and I will post back any information I can garner from it

Marcus Ottosson

unread,
Jan 31, 2015, 10:48:15 AM1/31/15
to python_in...@googlegroups.com
No problem, yann19. :)

yann19

unread,
Feb 1, 2015, 9:31:48 PM2/1/15
to python_in...@googlegroups.com
So I run they type() and __mro__ as mentioned by Marcus...

The results are as follows:
  • type() : <class 'importSceneUI.ImportScene'>
  • __mro__ : None

dir() returns the following error:

# Error: 'str' object is not callable
# Traceback (most recent call last):
#   File "<maya console>", line 19, in <module>
#   File "<maya console>", line 17, in main
#   File "<maya console>", line 8, in sceneImport
# TypeError: 'str' object is not callable #

I suppose this may be what I expected as I have mentioned earlier in my posts that the import dialog I am calling ain't exactly an UI created by Maya/PySide etc.. Upon taking a closer look in the codes, the dialog creation seems to branch off from other import modules within its file...

Justin Israel

unread,
Feb 1, 2015, 9:55:29 PM2/1/15
to python_in...@googlegroups.com

The lack of an mro value may indicate that the class is and old-style class, not inheriting from "object"

The error you are seeing when calling dir() looks like you shadowed the builtin dir() function, by assigning a string to the dir variable.


--
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/59c563ef-d0c9-4d3d-b181-b349e73aea16%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Marcus Ottosson

unread,
Feb 2, 2015, 6:57:16 AM2/2/15
to python_in...@googlegroups.com

What Justin said.

We can at least be sure it isn’t a PySide/PyQt object.

Just to clarify, based on your previous example, is this what you did?

>>> print type(window1)
<class 'importSceneUI.ImportScene'>
>>> print type(window1).__mro__
AttributeError: class ImportScene has no attribute '__mro__'

Could you elaborate a little on what role this object played in your question? How are you using it? What does it return?

yann19

unread,
Feb 3, 2015, 2:10:36 AM2/3/15
to python_in...@googlegroups.com
Yes that is what I did to find the .type and __mro__

As I have mentioned previously, I am calling out an import dialog/window from the import modules, in which the modules are my company proprietary stuff

import pubUI
pubUI
.importScene()

So when this UI is booted up, I will select the items that I required and it will imports it into the scene.
If I am reading the code right, at the end of the importScene function, it will return the UI but the UI in itself was called from another modules within the script

Am I making sense?

Marcus Ottosson

unread,
Feb 3, 2015, 2:42:53 AM2/3/15
to python_in...@googlegroups.com

Sure, that makes sense. We’ll do another type of digging to get to the bottom of this.

  1. If you run:
    print pubUI
    
    It will tell you the location of that module. You could then head inside and look for def importScene(). Once inside, you might be able to tell what it’s doing and what it is returning.
  2. Or, if for some reason you can’t open the file externally, you can print the source of the function directly.
    import inspect
    print inspect.get_source(pubUi.importScene)
    
    That will output the contents of that function into the script editor.

Just so I’m fully understanding what is happening and what you want to do, you want to:

  1. Run importScene() which will prompt you for a scene to open, in a window that blocks Maya until finished. Similar to the File|Open window.
  2. When this window is closed, you want to run another function. Let’s stick to your first example, in which the other function prints “window from importScene() is closed”

If the importScene() command truly blocks Maya (this is called a “modal”), then you can simply do this.

pubUi.importScene()
myOtherFunction()

And myOtherFunction() will run right after the UI has closed (i.e. returned control to Maya).

If it isn’t blocking, then we’ll need to do something like what Justin and AK Eric suggested above, but before we do we’ll need to know what the window is (Maya or PySide) and what it is called.

Justin Israel

unread,
Feb 3, 2015, 3:51:04 AM2/3/15
to python_in...@googlegroups.com

Is your company's proprietary module documented in any way? Are there any nice public bits available on this returned UI class? I feel like we are just trying to help you did into generic introspection techniques, when you probably have more info at your fingertips than we do.
I can appreciate that you can't share your companies code here, but there has got to be a way for you to review the code you are using to see what is exposed on this class your company has you using.

Does importScene() block when you call it? Does it return immediately, and if so, what does it return? If the call does indeed block, then it is giving you no opportunity to hook in and get a reference to its UI, before it does its task and ends. In which case, Marcus is right that you would just do whatever you want after the call returns.

Without seeing this code, we will most likely just be lobbing suggestions of how to introspect Maya's lists of UI objects.


--
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.

Marcus Ottosson

unread,
Feb 3, 2015, 4:43:57 AM2/3/15
to python_in...@googlegroups.com

Without seeing this code, we will most likely just be lobbing suggestions of how to introspect Maya’s lists of UI objects.

Let’s not underestimate the value of being able to debug Python. When we’re done here, yann19 and anyone in a similar position, should be able to find answers to similar questions with very little effort.

Justin Israel

unread,
Feb 3, 2015, 5:12:35 AM2/3/15
to python_in...@googlegroups.com

I'm not underestimating the value of debugging. I'm just trying to establish if the obvious routes have been assessed first, like available documentation, or what yann19 knows up front about the code that we cannot see.


On Tue, 3 Feb 2015 10:43 PM Marcus Ottosson <konstr...@gmail.com> wrote:

Without seeing this code, we will most likely just be lobbing suggestions of how to introspect Maya’s lists of UI objects.

Let’s not underestimate the value of being able to debug Python. When we’re done here, yann19 and anyone in a similar position, should be able to find answers to similar questions with very little effort.

--
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.

yann19

unread,
Feb 10, 2015, 1:40:01 AM2/10/15
to python_in...@googlegroups.com
Hiya all, really sorry for the delay in reply.

So, I tried out the method as suggested by Marcus, but sadly it is not working as well :(
Additionally, in terms of the documentation as mentioned by Justin, there was none too. (A lot of such modules are not documented)

However, after some discussion, I have decided to approach the stuff I am doing in a different manner, avoid of using this UI but rather "took" out some of its importing codes and have it re implemented into my custom UI. While it does not seem very feasible but it is working in my cause.

Really appreciate for everyone's help and advices

yann19

unread,
Feb 10, 2015, 4:13:41 AM2/10/15
to python_in...@googlegroups.com
Even so, say after running the following:

import animTools
animApp
= animTools.launchAnimTools()
print type(animApp)
print type(animApp).__mro__

<class 'animTools.createAnimationToolsUI'>
(<class 'animTools.createAnimationToolsUI'>, <class 'PyQt4.QtGui.QMainWindow'>, <class 'PyQt4.QtGui.QWidget'>, <class 'PyQt4.QtCore.QObject'>, <type 'sip.wrapper'>, <class 'PyQt4.QtGui.QPaintDevice'>, <type 'sip.simplewrapper'>, <class 'animTools_pyAnimToolsUI.Ui_MainWindow'>, <type 'object'>)



I am still pretty interested to know how do one go on from there?

Marcus Ottosson

unread,
Feb 10, 2015, 4:40:46 AM2/10/15
to python_in...@googlegroups.com

I am still pretty interested to know how do one go on from there?​

Now that we know it’s a PyQt object, we can do something like this.

import animTools
animApp = animTools.launchAnimTools()

def myCloseEvent(event):
    print "Closing!"

widget.closeEvent = myCloseEvent

Or, since you know know the superclass, you can subclass it.

class MyAnimTools(animTools.createAnimationToolsUI):
    def closeEvent(self, event):
        print "Closing!"
        super(MyAnimTools, self).closeEvent(event)

Thus having your own “wrapper” around the original, that you can call instead.

myAnimApp = MyAnimTools()
myAnimApp.show()

However, as the animTools.createAnimationToolsUI object is returned by animTools.launchAnimTools() I can’t be sure if it’s doing anything else to the object before returning it.

To find that out, you can either open that file, or print the source code for the function and have a look.

Get file path to function

>>> import animTools
>>> print animTools
<module 'animTools' from '/somewhere/on/your/server/animTools.py'>

Or print the source code directly

>>> import animTools
>>> import inspect
>>> print inspect.get_source(animTools.launchAnimTools)

yann19

unread,
Feb 13, 2015, 3:34:48 AM2/13/15
to python_in...@googlegroups.com
Hey Marcus,

Thanks for getting back to me.
About the close event that you have mentioned, it certainly is similar to a link I have found

class mainWindow(QDialog):
   
def __init__(self, parent=None):
       
super(mainWindow, self).__init__(parent)
       
self.wantToClose = False

   
def closeEvent(self, event):
       
self.wantToClose = True
       
if self.wantToClose:
           
print "closing"
           
super(mainWindow, self).closeEvent(event)
           
#self.importAnim()
       
else:
           
print "still opened, not closing"
           
event.ignore()

Nevertheless I am very much intrigued by the world of coding :D

Reply all
Reply to author
Forward
0 new messages