theodox / mGui

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

Shelf builder #67

Closed bob-white closed 7 years ago

bob-white commented 7 years ago

Initial pass at #64. Besides the shelf_loader module itself, it includes a change to the behavior of:

Currently the shelf_loader does take advantage of those changes, they aren't strictly necessary and could be refactored out if needed rather simply.

bob-white commented 7 years ago

One thing I forgot to mention, this allows for in-place updating. Because Maya already serializes shelves, our only other option would be to completely delete and rebuild each time, but that creates the counter-intuitive behavior of deleting any user added buttons.

theodox commented 7 years ago

the delete-resave thing is definitely part of the reason I have not done this before. If we could get it to work like menuloader, which appends to existing gui, that would be more predictable.

theodox commented 7 years ago

Unrelated, but relevant question: Maybe swap JSON for YAML? That would remove an external dependency. I've also got some code to handle the bitsquid SJSON format, which is less punctuation heavy than JSON but is a purely syntactic transformation

bob-white commented 7 years ago

Problem with latebound property is that it caches the value when its done, and we'd want to recreate the array in case a new menu was added. I tried adding a PopupMenuArrayProperty, but that just expanded the circle, because core needs to import properties, and properties would need to import PopupMenu.

bob-white commented 7 years ago

Another idea I had was to move a lighter weight version of gui.derive to the MetaControl class. The metaclass would keep weakrefs to each class (WeakValueDictionary), and using cmds.objectTypeUI could identify which wrap method to call.

class ControlMeta(type):
    """
    Metaclass which creates CtlProperty and CallbackProperty objects for Control classes
    """
    __lookup = WeakValueDictionary()
    __lookup_remap = {
        'window': 'floatingWindow',
        'menuItem': 'commandMenuItem'
    }
    def __new__(mcs, name, parents, kwargs):

        maya_cmd = kwargs.get('CMD', None)
        _READ_ONLY = kwargs.get('_READ_ONLY', [])
        _ATTRIBS = kwargs.get('_ATTRIBS', [])
        _CALLBACKS = kwargs.get('_CALLBACKS', [])
        if not kwargs.get('CMD'):
            maya_cmd = parents[0].CMD

        _overridden = ('parent', 'popupMenuArray')
        for item in _READ_ONLY:
            if item not in _overridden:
                kwargs[item] = CtlProperty(item, maya_cmd, writeable=False)
        for item in _ATTRIBS:
            if item not in _overridden:
                kwargs[item] = CtlProperty(item, maya_cmd)
        for item in _CALLBACKS:
            kwargs[item] = CallbackProperty(item)

        kwargs['__bases__'] = parents
        cmd_name = maya_cmd.__name__
        cmd_name = mcs.__lookup_remap.get(cmd_name, cmd_name)

        cls = super(ControlMeta, mcs).__new__(mcs, name, parents, kwargs)
        mcs.__lookup[cmd_name] = cls
        return cls

    def derive(cls, widget):
        return cls.__lookup[cmds.objectTypeUI(widget)].wrap(widget)

Then it would just be:

def popupMenuArray(self):
        return [Control.derive(self.fullPathName + '|' + menu) for menu in self.CMD(self, q=True, popupMenuArray=True) or []]

Ends up being a bit more magical that way though.

bob-white commented 7 years ago

This will append to an existing shelf, as long as its not one of the weirdly dynamic builtin ones. For some reason their contents are lazy-loaded, and adding new buttons seems to break the that. Once the shelf has been loaded though this will append no problem. User created shelves don't seem to suffer the same problem. Probably because they're serialized out separately.

theodox commented 7 years ago

What I do with menuloader is kick off a runonce, idletime scriptjob to post-edit the main Maya menus after they are loaded. Would that work here?

bob-white commented 7 years ago

Yeah, I do a similar thing. Doesn't seem to help with stuff like this. Its similar to how the file menu doesn't exist until the first time its opened. You can test it by opening maya, and just running cmds.shelfLayout('Polygons', q=True, childArray=True). Once you click on the shelf though, it will return the actual array of children.

bob-white commented 7 years ago

Figured out a method for handling this particular edge case. We just walk through all the tabs, and select them, before adding / updating any new shelves or buttons. This lets the loader append buttons to existing shelves, and users can add buttons to loader defined shelves.

bob-white commented 7 years ago

I pulled the popupMenuArray property off of Control. Seemed easier to just refactor the builder a little bit as I was only using the property in one place. Can always change it back if we come up with some other method in the future.

bob-white commented 7 years ago

Updated to no longer rely on yaml. Instead we hand it a python dictionary, that in this case happens to be loaded from a json file.

bob-white commented 7 years ago

I think I've gotten all the kinks out of this. At least it hasn't been giving me any issues for a few weeks.

Any objection to me merging?

theodox commented 7 years ago

Go ahead. I'm very heads down at work so I haven't gotten time to do a lot of careful investigations