Load options, implementing #346#482
Conversation
The spacing between `OptionalAction` menu action icon and label may differ between hosts. Make them all the same.
|
The idea and implementation look very good. Not in the way when not needed and quick to use when it is. I like it. |
|
Yes! I would write it in much the same way; API is declarative, handling of options is non-obtrusive, implementation is miminal and clean. Very well done. My only question is whether the name shouldn't be Ps. I in fact implemented something very similar not too long ago that was private until just now. :) - https://github.com/mottosso/qargparse.py |
I did think of just named it I found that the action widget's label got an ugly drop shadow when it's been hovered in Nuke. Could not find any reason or solution but this https://stackoverflow.com/q/52838690/4145300. So it looks like some how Avalon's style has lost or incomplete in action widget so the Nuke's default style pops up, not sure. :/ Anyway, I have added a commit to fix this. |
tokejepsen
left a comment
There was a problem hiding this comment.
Amazing work @davidlatwe !
| return self.input.text() | ||
|
|
||
|
|
||
| class GetOne(InputBase): |
There was a problem hiding this comment.
Would it be better to name this ComboBox to align with Qt?
There was a problem hiding this comment.
Hmmm, I am not sure. At the time I thought ComboBox wasn't straight forward enough to express "Select one from the list", maybe Enum ?
There was a problem hiding this comment.
Right, I see what you are going for. I thought it was more related to the graphic, cause I was also confused about not using radio buttons instead of combo list.
GetOne does seem to stand out as a name. Enum might fall better into the mix of naming.
There was a problem hiding this comment.
Ah yes, it's actually based on what type of value to be inputted and how it will returned.
And yeah, radio buttons could also fit the case of "Select one", so I even thought about implementing both and make an argument to decide which type of widget to use. But I gave up on that because of my laziness :P
Anyway, other potential needs of visualizing key-value pairs into different kinds widgets, can still be added ! See how this go
There was a problem hiding this comment.
I just remembered that qargparse was used in Allzpark; it's used for all options in that GUI, most prominently in the Preferences tab, and as a single-file library it's embedded into vendor/. We could do something like that for Avalon too; it's worked well so far, and there's lots of room to grow.
The only crux is that I'm on other things currently and wouldn't be of much help. So it's up to you to determine whether your time is best spent on a library you've built and fully understand, or whether you'd be happy diving into and progressing qargparse - something I intend on returning to once I'm back doing pipeline.
|
Sorry for the late reply. First off, this looks very nice! Very good work. I did have a question and a note. One more abstraction layer, allow to define your own QWidget completelyShould we maybe add a single more abstraction layer so that if one wanted, they could provide their own Widget completely as Option dialog as opposed to these "predefined option widgets". Overall I like the fact that, with a bit of documentation, we seem to be able to make it easy for anyone with little understanding of programming to produce these widgets. However, some might want more control over the full feel of the widget. So I was thinking, what if we did it like this:
# psuedocode
class OptionsWidget(inputs.OptionsWidget):
inputs = [
inputs.Bool("iAmHuman", help="Are you a human ?"),
inputs.Int("age", default=15, min=0, help="How old are you ?"),
inputs.Float("height", default=170, help="How tall are you ?"),
inputs.Double3("position", help="You're location cord."),
inputs.GetOne("sex", ["Male", "Female"], as_string=True),
inputs.String("message",
placeholder="Say something",
help="Leave a message."),
]
class Loader(api.Loader):
options = OptionsWidget
# EtceteraSo that if someone wanted to attach fully their own widget they could inherit from Plus it would also allow to re-use a single OptionsWidget across mutiple loaders if you wanted to. E.g. class BaseOptionsWidgets(QtWidgets.QWidget):
def initialize(loader, representation):
# Implement this in subclass if you want to do customization
# for specific loaders or representations and you build your own
# widget
pass
def options():
# Return the output values of your options dialog
raise NotImplementedError("Must be implemented by subclass.")And we would have the Widget you have now as a Helper widget to make it easy to quickly throw together some options if you don't want to write full Qt Widgets. What do you all think? Styling/spacingIs there any particular reason the spacing seems to be so big in the preview screenshot? There seems to be an awfully lot of space between the inputs. Or is that just my personal taste that's different than yours? I wondered if there was any particular reason for it. @davidlatwe you're on fire again! Lovely work. 🚀 |
I like it! |
Eeek! Less abstractions = better. I didn't get what the problem this would solve? KISS, resist the urge to overengineer. I would suggest putting that on the shelf until there's an actual problem or usecase it could be used to solve; although ideally we wouldn't fit a solution to a problem, but rather figure out a solution starting from the problem. |
Totally see what you mean, and I agree. Preferably the implementations gives the barebones of what everyone needs and allows for the studio configurability we all want. In that case I'd simplify this and allow to pass solely a Maybe saying "more abstraction" for what I tried to propose described it wrong. |
|
Not sure what to make of this to be honest. The current code solve a specific issue, and nothing more. That's how I would argue code should get written. Keeping it limited in terms of an API makes it more predictable what people do with it and enable us to build off of those assumptions, much like how Avalon "limits" the user in what they are able to do in terms of Python or Qt. We let users add any widget, and suddenly we'll have request about why their media player isn't contacting Amazon Cloud properly when the window is minimized, because they need their deep learning web camera tracking software to toggle some option on/off. I think your point is too theoretical. xD It's open source and Python so, if someone familiar with Python wanted to customise things further, they already could. |
I think the main convenience was when retrieving the user inputs, but I didn't really comparing both ways. Just my guts feeling 😬 What about this 👇 if isinstance(loader.options, QtWidgets.QWidget):
dialog = loader.options(parent=self)
else:
dialog = OptionDialog(parent=self)
dialog.setWindowTitle(action.label + " Options")
dialog.create(loader.options)
if not dialog.exec_():
return
# Get option
options = dialog.parse()So if anyone prefer or need to use self-implemented dialog, just plug it into Also, I have put
Here is the result after adopting |
Do you mean for the code in Anyway, I feel this extra if/else statement is more confusing things than streamlining the API for the Options on the loader. If no one feels anything for being able to define any custom Widget I'd be happy to drop this conversation and pick it up later. Still feel it's slightly odd design but nothing that I can't get past. :) However, if we later ever want to push this in cleanly it's only possible with this if/else statement to make sure loaders remain backwards compatible. |
Yeah, I was thinking about that too! But then I thought adding an if/else statement wasn't a big issue for remaining backward compatible in this case, so I've decided to stick to the current implementation for this PR. :) Will merge this tomorrow if no other objections 🚀 Edit:
I meant for a developer designing the options interface. :) |
|
Sorry, just spotted another issue while looking at @iLLiCiTiT's proposal in above review comment. Turns out the action highlighting will stop changing while mouse pressed and hold, see GIF below 👇 It look's like the widget region of each action for After hours of googling and messing around, finally realized that the menu isn't a list of widgets but just pointers, so after mouse pressed, those So instead of re-implementing action widget's Now our menu will keep highlighting active action no matter mouse was pressed or not. 👇 Okay, I think this PR is really good to go |
|
Looking good! :D |
|
Merge ! |




What's changed
OptionalAction, a subclass ofQtWidgets.QWidgetActionSubsetWidgetmenu item fromQActiontoOptionalActionOptional loading action
After revisit issue #346, I've decided to implement a dialog that pop-up from context menu to display the option controls, so the layout of current Loader GUI won't have to change much.
The dialog implementation was referenced from Maya's menu option box, and the option box will be shown on menu when the loader plulgin has
optionsattribute. And since supporting multiple or even different subsets could lead to unnecessary complexity, the option box only visible when there's only one subset being selected.The loader
optionsshould be a list of pre-defined input widgets which implemented for certain type of value and the input value will be collected and parsed intoapi.load(options=options).Example usage
In Production (Testing)
Any feedbacks are welcome☺️