theodox / mGui

Python module for cleaner maya GUI layout syntax
MIT License
123 stars 23 forks source link

Single instance Windows #55

Closed dshlai closed 7 years ago

dshlai commented 7 years ago

HI!:

It looks like the main window is auto-named (window8, window9, etc.) when instantiated . How can I implement a single instance window? The usual way is to use window name together with deleteUI command to ensure that there is only a single instance window.

There are couple idea I am thinking about:

  1. use Maya's global variable as registry for window's name
  2. use Python module level variable to register window'name

Maybe I miss something here, can you guys help out?

Thanks.

theodox commented 7 years ago

The underlying window widget created in mGui will be stored in the widget field of the mGui Window you create. You can use Window.delete on the any instance of Window , or regular old deleteUI to delete an existing window using its widget if it does not have an mGui wrapper.

If you already have an mGui Window and you want to know if it still exists, just truth-test it:

 Window1 = Window()
 if Window1:
      print "Window1 exists, it's underlying Maya window is", Window1.widget
 cmds.deleteUI(Window1.widget)
 # Window.delete(Window1) would do the same thing...
 if Window1:
      print "I never get printed, because 'if Window1' will return False

You can also get an mGui wrapper for an existing window with Window.from_existing('your_window_string'). That will let you query and edit properties on that window but (unlike a mGui window you make the usual way) it won't have access to the window's children.

theodox commented 7 years ago

@dshlai -- did that fix your issue?

dshlai commented 7 years ago

Steve Theodore notifications@github.com於 2016年12月24日 週六,上午2:45寫道:

@dshlai https://github.com/dshlai -- did that fix your issue?

— You are receiving this because you were mentioned.

Reply to this email directly, view it on GitHub https://github.com/theodox/mGui/issues/55#issuecomment-269030574, or mute the thread https://github.com/notifications/unsubscribe-auth/AAqb3PVVI1P_X8jIi06KgyoIEePeAvHeks5rLBa9gaJpZM4LRtC9 .

Currently on Holiday, will take a look when back

theodox commented 7 years ago

Happy new year!

dshlai commented 7 years ago

Hi! Following the suggestion this following code snippet works.

I had to use MEL variable to store the UI name because Python variable gets garbage collected after the script is run.

Each time the script is run it will delete the old Window UI instance and create a new one.

win = None
win_name = ""

# get window UI name if is was stored
try:
    win_name = mel.eval("$temp=$myWindow")
except RuntimeError as e:
    pass

# sometimes the window is closed by user
if win_name:
    try:
        pm.deleteUI(win_name)
    except RuntimeError as e:
        pass

try:
    win = BoundCollectionWindow([])
    selected = [item.fullPath() for item in pm.ls(selection=True, long=True)]
    win.collection.add(*selected)
    win.show()

    # store window name as mel variable
    mel.eval('$myWindow="{}"'.format(win.window.widget))

except RuntimeError as e:
    pass
theodox commented 7 years ago

The usual pattern would be to use a class variable as a keepalive, and rely on the fact that the mGui objects will test as false if they have been deleted:

from mGui.gui import *
from mGui.forms import FillForm
import pymel.core as pm

class ExampleWindow(Window):
    INSTANCE = None 
    def __init__(self, *args, **kwargs):
        super(ExampleWindow, self).__init__(*args, **kwargs)
        selected = [item.fullPath() for item in pm.ls(selection=True, long=True)]

        with FillForm() as root:
            self.main_list = TextScrollList() 

        self.main_list.items = [item.fullPath() for item in pm.ls(selection=True, long=True)]

    @classmethod
    def get(cls):
        if cls.INSTANCE:
            cls.delete(cls.INSTANCE)
        cls.INSTANCE = cls()
        return cls.INSTANCE

b = ExampleWindow.get()
b.show()
dshlai commented 7 years ago

Hi!:

I found another singleton pattern that works as well.

I think my issue is mainly due to that the calling code currently does reload (the module) each time before the window is initialized, this action seems to destroy the class along with Singleton variable it is holding. (Which make sense since the newly load code should replace the old one)

remove the reload(module) everything works fine.

Thanks

from mGui.gui import *
from mGui.forms import FillForm
import pymel.core as pm

class ExampleWindow(Window):
    INSTANCE = None 

    def __new__(cls):
        # check against type to ensure subclass can be used as Singleton well
        if cls == type(cls.INSTANCE):
            cls.delete(cls.INSTANCE)
            cls.INSTANCE = object.__new__(cls)
            return cls.INSTANCE
        else:
            cls.INSTANCE = object.__new__(cls)
            return cls.INSTANCE

    def __init__(self, *args, **kwargs):
        super(ExampleWindow, self).__init__(*args, **kwargs)
        selected = [item.fullPath() for item in pm.ls(selection=True, long=True)]

        with FillForm() as root:
            self.main_list = TextScrollList() 

        self.main_list.items = [item.fullPath() for item in pm.ls(selection=True, long=True)]

b = ExampleWindow()
b.show()