If you have a parent (such as the main SoftImage window?), you would look at all the parent’s children to find one with that given name/window title/type whatever, and reuse or destroy it before creating the new one. This is by far the best way to do it (have a single ‘main window’ for the entire app), though destroying widgets with pyqt can be annoying. Otherwise, you’d have to store the state yourself, either on a module (unsafe, because it’d be lost if you reload the module), or somewhere in XSI (not sure where).
Thanks for asking this, I had always wondered the same thing but never actually got a round to tackling it. Makes sense to look at the main app window as the parent though.
The solution I usually employ in my Qt code is having a class variable that holds the instance of the window.
Then in constructor I check if that instance is valid and if so I can either bring up an existing window (this allows for persistent state) or I can destroy it and recreate it (make sure to call sip.delete to get rid of underlying C++ objects).
Edit:
When it comes to handling PyQt windows and applications inside 3rd party applications like Maya I have a window manager class that has the factory method for creating Qt windows and that handles a lot of setup and cleanup.
Having this infrastructure allowed other developers to write simple Qt tools and not have to worry about how to manage their clean up and leave it all to the manager instead. It also allows me to handle things like modal, dockable and other states in a consistent way.
ShadowM8 - how are you checking if the window already exists? Always creating it in a global variable, then checking if None, etc?
Is there a pyqt_in_maya equivalent of pm.window(‘Window_Name’, exists = True) ?
QtGui.QApplication.allWidgets() will return a list that includes <main.MY_UI_CLASS_NAME object at 0x0000000031F2C9D8> . Is that an appropriate way to find and interact with a window that already exists?
Well I described both solutions that I use but as a more detailed example the application framework I have does something like this:
class App (object):
_instance = None
def __new__ (cls, force = False):
if not force and App._instance: return App._instance
App._instance = super (App, cls).__new__ (cls)
return App._instance
# This will get existing window unless you pass True to force argument
app = App ()
In the second example, I always instance my windows from a manger class and it is responsible for doing a similar check rather than the app itself.
Test.setObjectName(‘yourUiName’) #----
win = Test() #实例化UI
for widget in QtGui.qApp.activePopupWidget():
if widget.objectName == ‘yourUiName’:
widget.close()
new creates the instance of the class that gets passed to init (usually). The first argument to new is the class to create the instance of. Think of it like the first argument ‘self’ of an instance method, where the instance is automatically passed as the first argument to the method you’re calling. cls is the class (in this case App) that an instance will be created of, and is automatically passed to new when it is called.
So it’s not looking for a variable ‘cls’, that variable is being passed internally and is out of your reach.
Looks like you’re trying to use the common python version of the Singleton pattern…
class Singleton(object):
_instance = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super(Singleton, cls).__new__(
cls, *args, **kwargs)
return cls._instance
In which case all you need is
the_singleton = Singleton() # in your case this is 'App'
as pointed out above, cls is the class type, automatically supplied in the same manner as ‘self’ (and, like self, the name is meaningless – it’s just the first positional argument)
If this is something you’ve been working with a while, you’re probably stuck with it. If not, bear in mind that lots of Pythonistas are anti-singleton, usally preferring to do this at the module initialization level (which is guaranteed to happen only once, barring reloads) rather than in a class initializer.
About the only time I mess around with new is if I’m inheriting a base type, like str… which is only very, very rarely a good idea…
Thanks! Still wrapping my tiny brain around this. So,
the_singleton == the_singleton._instance ?
It’s strange, the code ‘functions’, where _instance holds the object that I want, but I’m still getting multiple copies of the UI showing up (this is in Maya).
obj_a = Singleton() #new UI created
obj_b = Singleton() #another UI created
obj_a == obj_b #True
edit: thanks for the Pythonista info, as well. Currently the module is reloaded every time.
yes, you’ve got two handles to the same object in memory – at least, with the standard singleton snippet. Now it’s up to the actual instance to decide how to create the UI elements – if they were created and stored in the first one then (implementation aside) you’d still have them; OTOH if they get created by a member function on the instance, they could be separate objects.
Consider two examples:
class same_UI(Singleton):
def __init__(self):
if not self.UI:
self.UI = self._create_ui()
#pretend i've got a func here that makes a
#window and sticks the handle in here
def show(self):
self.UI.show()
class different_UI(Singleton):
# no constructor, not storing the UI object
def show():
UI = self._createUI()
UI.show()
The first example basically repeats the singleton pattern internally – it creates one-and-only-one UI and show it on demand. The second one doesn’t store state, so even though the instance object is identical it’s still spewing out different GUI objects.
PS. If you don’t need to reload the module, don’t. You’ll reset any state that’s stored at the module level. You can import the module many times but the initialization will only run once, and any state changes will be preserved. So if your module looked like this:
class UIManager(object):
def __init__(self):
self.UI = self._create_ui()
#and do other magic initialization here....
def show():
self.UI.show()
__MY_SINGLETON = UIManager()
def get_ui_manager():
return __MY_SINGLETON
You could just import the module and call get_ui_manager() when you needed access to the UIManager instance, without messing around with new. This is what the anti-Singleton forces prefer.
To elaborate on why multiple UIs were being created, the reason is that init was being called both times you called Singleton. init is called whenever new(cls) returns an instance of cls. In that Singleton example, the _instance attribute always stores a Singleton instance (or None, if it hasn’t been called), so regardless of whether a new instance is created or an existing one is returned, new is still returning an instance of Singleton and thus init will be called.
As Theodox illustrated you have to perform a check in init as well so that multiple UIs are not created. Overriding new as in Singleton just means you get the same object in memory each time you call it.
It’s a good example of the main argument against the Singleton pattern – it blurs the lines between object construction and initialization. Using a module (or a classmethod that just returns the _instance) instead of the constructor avoids these kinds of traps.
As an aside, it’s also worth thinking twice about where heavyweight initializations (such as creating GUI items) are supposed to take place. Many folks would not stick the actual creation of the GUI into the constructor of the manager class, since setting up the GUI will take a significant amount of time and memory. If you’re going to use the object right away after creating it that’s basically moot – but when you’re designing the class you may not know all the usage scenarios. For example, if the UIManager() is going to get dumped in a queue of objects for later processing you might be happier paying the GUI creation cost when the object is actually being used instead of when it’s created.
For my own part I tend to prefer a manual init method since it gives finer grained control over when the costs will be paid… I found this out the hard way when I was working on a library of XML file wrappers that read and processed files in their constructors. Worked fine one at a time – but doing batch processing involved 10 minute startup delays as every one of hundreds of files was read and processed before work actually began
Use dependency injection- you’ll naturally avoid the needs for a singleton or the initialization problems Steve describes. Then you’ll just have a couple places with module-level state, at the application level, but all your actual tools and widgets will always have their dependencies passed in.
Thanks for the insight, much appreciated!!! Sticking with singleton for now, and not reloading the module every time.
This works, hopefully looks right.
class Singleton(object):
_ui_instance = None
def __new__(cls, *args, **kwargs):
if not cls._ui_instance:
cls._ui_instance = super (Singleton, cls).__new__(cls, *args, **kwargs)
return cls._ui_instance
class UI_Wrapper(Singleton):
def __init__(self):
if not Singleton._ui_instance:
Singleton._ui_instance = The_UI()
def show_it(self):
Singleton._ui_instance.show()
class The_UI():
....
Looks like it would do the trick, but there are two changes you might want to make:
It’s best not to rely on third party classes to do initialization logic for each other. Here you’re asking UI_Wrapper to set up Singleton. That makes them unnecessarily interdependent – let Singleton take care of it’s own business.
The Docs say you should avoid accesing names with the _prefix directly. It’s better to expose this info as a method call: it lets you do any setup you need when the info is requested, and it also means you can override the behavior in subclasses if the need arises.
For the sake of lurkers, here’s an alternate implementation:
class Singleton(object):
_singleton_instance = None
def __init__(self, *args, **kwargs):
self.UI = None
def __new__(cls, *args, **kwargs):
cls._singleton_instance = cls._singleton_instance or super (Singleton, cls).__new__(cls, *args, **kwargs)
return cls._singleton_instance
# this saves a line, but is actually less clear than your version :)
def get_ui(self):
self.UI = self.UI or TheUI() # same trick as above
return self.UI
class UI_Wrapper(Singleton):
def __init__(self):
self._singleton = Singleton()
# all instances of this class have a handle to the same singleton
def show_it(self):
ui = self._singleton.get_ui()
# it's OK to use our own _variables - not other classes'
ui.show()
class The_UI(object):
def show(self):
print "instance @ %" id(self)
[QUOTE=Rob Galanakis;18530]Use dependency injection- you’ll naturally avoid the needs for a singleton or the initialization problems Steve describes. Then you’ll just have a couple places with module-level state, at the application level, but all your actual tools and widgets will always have their dependencies passed in.[/QUOTE]
I hadn’t heard of dependency injection until looking it up after this post, and while the concept makes sense, it’s another one of those ideas that I can follow but don’t really know how I’d decide when it’s right or useful and could see myself throwing it in all over the place just 'cause. How do you decide when you want to use DI? Do you use it often?
I’m also not sure how it would be used in this current example in place of a singleton or storing the instance at the module level; mind elaborating on that?
a typical example of DI is when you want to switch from mysql database to oracle or file database etc. They all have the same interface and you basically setup an IoC container at the application level to resolve construction and initing for you of the correct type.
Readup on IoC container and its use cases.
Though for python it might not be necessary but it’s still worth understanding the thoughts behind it.