Closed davidlatwe closed 4 years ago
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 import options
as opposed to inputs
as inputs
is a little too close to input
, the protected keyword.
Ps. I in fact implemented something very similar not too long ago that was private until just now. :) - https://github.com/mottosso/qargparse.py
My only question is whether the name shouldn't be import
options
as opposed toinputs
asinputs
is a little too close toinput
, the protected keyword.
I did think of just named it options
, but potentially it could be used in other place then option box, so I thought maybe inputs
is a better fit. Or maybe controllers
? Or qparse
for shorter name ! Hehe, nice project and thanks for sharing ! @mottosso
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. :relaxed:
Sorry for the late reply. First off, this looks very nice! Very good work. I did have a question and a note.
Should 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:
options =[inputs.Bool(), inputs.Int()]
to the loader directly we would pass it an OptionsWidget, like so:# 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
# Etcetera
So that if someone wanted to attach fully their own widget they could inherit from OptionsWidgets
(or even just an OptionsBase
? which only requires to have a function options()
to be available to retrieve the chosen options in the end. That would also allow e.g. a UI that has already been built by someone to be used.
Plus it would also allow to re-use a single OptionsWidget across mutiple loaders if you wanted to. Especially if somehow the particular Loader and Representation would also be passed along to the OptionsWidget so that even more customization could be done for the options if one wanted (like if only specific metadata is present on a specific subset/version/representation).
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?
Is 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. 🚀
What do you all think?
I like it!
Should we maybe add a single more abstraction layer
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.
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 QWidget
and move the custom helper classes for inputs into its own repository, like qargparse
. Personally I feel that it is somewhat overengineering functionality that is trivial to put in a QWidget already. Less code, less to maintain in core and the same result. For developers familiar with Qt
they now also need to familiarize them with this "Inputs" API which is redundantly limiting the options for the developer. It sounds blunt in words and I definitey don't mean it that way, it looks very nice. I just wondered why we lock it in this much when it's redundant to do so? The code works perfectly fine without it and is simpler to maintain.
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.
Personally I feel that it is somewhat overengineering functionality that is trivial to put in a QWidget already.
I think the main convenience was when retrieving the user inputs, but I didn't really comparing both ways. Just my guts feeling :grimacing:
What about this :point_down:
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 Loader.options
, as long as the method parse
has implemented. :relaxed:
Also, I have put qargparse
into avalon.vendor
, and tools.inputs
was removed, changes (just a little) will be pushed soon.
@BigRoy
Is there any particular reason the spacing seems to be so big in the preview screenshot?
Here is the result after adopting qargparse
, no more redundant spacing !
I think the main convenience was when retrieving the user inputs, but I didn't really comparing both ways
Do you mean for the code in core
? Or a developer designing the options interface?
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.
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 🚀 Thank you all !
Edit:
Do you mean for the code in core? Or a developer designing the options interface?
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 :point_down:
It look's like the widget region of each action for enterEvent
and leaveEvent
has become entire menu after mouse is pressed.
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 enterEvent
and leaveEvent
implemented on each action widget has been ignored (by the menu's mousePressedEvent
).
So instead of re-implementing action widget's enterEvent
and leaveEvent
, I add a subclass of QMenu
with mouseReleaseEvent
, mouseMoveEvent
and leaveEvent
re-implemented.
Now our menu will keep highlighting active action no matter mouse was pressed or not. :point_down:
Okay, I think this PR is really good to go :relaxed:
Looking good! :D
Merge !
What's changed
OptionalAction
, a subclass ofQtWidgets.QWidgetAction
SubsetWidget
menu item fromQAction
toOptionalAction
Optional 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
options
attribute. 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
options
should 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 :relaxed: