New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
For #46368: Customizable export UI, plus additional hook methods for Shot/Cut updating. #42
Conversation
…sort of release. Just testing things as I go.
Wow this is great! |
info.yml
Outdated
|
||
hook_update_cuts: | ||
type: hook | ||
description: "" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whoops. Missed this one. I'll get a description in!
Adding josh as well - his input will be very valuable here given that he has been working extensively both with the publish2 UI extensions and the hiero exporter. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added what i hope are some helpful comments! I think it would be great to be able to look at the sphinx docs for this as a next step. Having a full, practical (ideally cut n paste) example for the custom UI hook would be super awesome.
hooks/hiero_customize_export_ui.py
Outdated
built by the associated get properties hook method. | ||
""" | ||
return | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
whitespace :)
HookBaseClass = sgtk.get_hook_baseclass() | ||
|
||
|
||
class HieroCustomizeExportUI(HookBaseClass): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this seems like a great opportunity to build these hooks like we did the shotgun write node and the publisher, with sphinx docs. See:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First pass done. See the comment below for more detail on a few caveats.
hooks/hiero_customize_export_ui.py
Outdated
""" | ||
return None | ||
|
||
def get_shot_processor_ui_properties(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think we should follow the same patterns as we do in the publisher:
http://developer.shotgunsoftware.com/tk-multi-publish2/plugin.html#tk_multi_publish2.base_hooks.PublishPlugin.get_ui_settings
So how about
create_shot_processor_widget
get_shot_processor_ui_settings
set_shot_processor_ui_settings
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm...I was just doing this, and it feels odd. Everything related to this on the Hiero/NS end of things revolves around "properties" and not "settings." It's feeling really strange mixing the two nomenclatures interchangeably. Having the getter return "settings dictionaries" feels okay, generally speaking, but then having the setter provide a list of property objects from Hiero's API and calling those "settings" doesn't feel right.
Thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd say stick with Hiero/NS terms
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm leaning that way, as well.
hooks/hiero_customize_export_ui.py
Outdated
label="Create Cut:", | ||
key="sgCreateCut", | ||
value=True, | ||
type=bool, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need to pass type
? This feels wrong to me. When is this not type(value)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah...I just tried to think of an example of where it might be different than type(value), and I can't come up with anything reasonable. I'll ditch it!
hooks/hiero_customize_export_ui.py
Outdated
|
||
Example Return Data: | ||
|
||
.. code-block:: python |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we need both a syntax example and a real example. Maybe we can skip this one and keep the actual example below?
hooks/hiero_customize_export_ui.py
Outdated
return [ | ||
dict( | ||
label="Create Cut:", | ||
key="sgCreateCut", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is this sgCreateCut
- what does it signify?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I used the kwarg name from Hiero's ui property constructor, but maybe we can make it a little more obvious by calling it "property_name" or "name" instead. Label is the human-readable name that's displayed in the UI, and key is the "system" name of the property.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed it to "name"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll also change the example to use a more verbose name
:rtype: dict or None | ||
""" | ||
cut_item = self.parent.sgtk.shotgun.create("CutItem", cut_item_data) | ||
self.parent.logger.info("Created CutItem in Shotgun: %s" % cut_item) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will the fact that we don't batch these guys really slow things down?
Could the hook take a list of cut items and do a batch?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can look at the underlying logic and see what might be possible. Is it too slow now? This isn't changing the logic flow other than running it from a hook.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this new or old code? I am not sure about current performance. But if you are running say a 200 shot conform, running create 200 times will definitely be slower than running a batch. But like you say, it depends on other factors, workflow ux etc. If this is old code and we are merely updating the docs, probably better to leave things the way they are!
try: | ||
# See if we can find a poster frame for the sequence and | ||
# turn that into a usable thumbnail. | ||
thumbnail = hiero_sequence.thumbnail(hiero_sequence.posterFrame()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we provide a link to a hiero API reference here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not do it as part of the sphinx docs?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can do!
Handles creating the filesystem structure for the shot that | ||
was exported. The preset properties dictionary is provided to | ||
allow for the lookup of any custom properties that might have | ||
been defined in other hooks, like can be achieved when using |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a practical example to illustrate here would be great.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
self.parent.logger.debug( | ||
"Creating file system structure for %s %s..." % (entity_type, entity_id) | ||
) | ||
self.parent.sgtk.create_filesystem_structure(entity_type, [entity_id]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
keep in mind that the self.parent.logger
in hooks will require a fairly modern core.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tk-nuke already requires v0.18.45
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And looking deeper, tk-hiero-export requires v0.17.21 of tk-core, which looks like it contains the parent property for Hook.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we're doing the hook docs and base hooks, it's going to need a really new core anyway. :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the minimum required for that? I assume it's newer than 0.17.21, which means I should bump up the requirement in info.yml.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe 2 releases ago v0.18.124
NOTES: - Old-style hooks with execute methods must use a pass instead of raising a NotImplementedError in the base_hooks module. - The base_hooks module can't live under the tk_hiero_export module, because importing that module requires access to the hiero API, which isn't available when generating docs.
I've added a first pass of the sphinx docs setup. As noted in the commit message, there are a couple caveats when compared to publish2 and tk-nuke-quickreview:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All looks good. Biggest comment is to consider a simple, 3 method API for the custom UI hook, then connect instances of that to whatever types you want to customize via the config.
@@ -34,6 +36,84 @@ def setApp(cls, app): | |||
def app(self): | |||
return self._app | |||
|
|||
def _get_custom_properties(self, get_method): | |||
""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing docstring.
return self._custom_property_definitions[get_method] | ||
|
||
def _get_custom_widget(self, parent, create_method, get_method, set_method, properties=None): | ||
""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing docstring.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
self._displayName = "Shotgun Audio Export" | ||
self._taskType = ShotgunAudioExporter | ||
|
||
def populateUI(self, widget, exportTemplate): | ||
""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing docstring.
|
||
- Status of associated Shot entities can be updated | ||
- Nuke scripts can be written into the project's filesystem structure for each Shot that's processed | ||
- Sequence and Shot entity data can be updated in Shotgun |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be worth pointing out that other parent entity types can be used as well (Episodes for example)
hooks/hiero_customize_export_ui.py
Outdated
""" | ||
def create_shot_processor_widget(self, parent_widget): | ||
""" | ||
Builds and returns a custom widget to be embedded in the parent exporter. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Worth mentioning how it will fit into the layout?
hooks/hiero_customize_export_ui.py
Outdated
""" | ||
return | ||
|
||
def create_audio_exporter_widget(self, parent): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking about a type
argument as well, but I think you'd just end up with a big conditional that called out to other methods in the hook.
One idea that could simplify things a bunch would be to define a base hook that only has 3 methods (create widget and get/set properties). This could be used for any of the types, but you associate the type/hook at the config level. So if you need a custom audio and transcode hooks, you'd configure something like this:
custom_export_ui_plugins:
- type: audio
hook: "{config}/hooks/tk-hiero-export/custom_audio.py"
- type: transcode
hook: "{config}/hooks/tk-hiero-export/custom_transcode.py"
Now you can cut down on the boilerplate and just define the API that's used for all the various types. Would that work?
been defined in other hooks, like can be achieved when using | ||
the hiero_customize_export_ui hook. | ||
|
||
:param str entity_type: The entity type that was created or |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we're breaking this out into a hook, I'm wondering if we should deemphasize "Shot" to some extent. Maybe something as simple as renaming the hook/class/methods to not include the word "shot".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm leaning towards not changing this from "shot", simply because it's more or less paired with the existing hiero_get_shot hook. We can't easily rename that hook without potentially breaking client setups that make use of it, so I'm inclined to keep get/update shot hooks sharing nomenclature.
self.parent.logger.debug( | ||
"Creating file system structure for %s %s..." % (entity_type, entity_id) | ||
) | ||
self.parent.sgtk.create_filesystem_structure(entity_type, [entity_id]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we're doing the hook docs and base hooks, it's going to need a really new core anyway. :)
@@ -254,41 +260,61 @@ def taskStep(self): | |||
sg_shot['task_template'] = template | |||
|
|||
# commit the changes and update the thumbnail | |||
self.app.log_debug("Updating info for %s %s: %s" % (shot_type, shot_id, str(sg_shot))) | |||
self.app.tank.shotgun.update(shot_type, shot_id, sg_shot) | |||
self.app.execute_hook_method( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
|
||
# create the directory structure | ||
self.app.log_debug("Creating file system structure for %s %s..." % (shot_type, shot_id)) | ||
self.app.tank.create_filesystem_structure(shot_type, [shot_id]) | ||
self.app.execute_hook_method( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
One more thing: You should inject the base classes when calling |
Another though... sorry... Would be nice to have some sample/test implementation (looks like you've already written) that we could use for QA. The publish2 app does something similar (see here). |
Thanks for the sphinx docs pass 1! Awesome!!! Josh made some great points about the overall flow, so I'll hold off further changes for now until you have reviewed that feedback or i might confuse things :) About the old methods vs sphinx - definitely huge value to get them into sphinx even if they count have fully featured docs like the new ones - i think it would be great if we could at least get basic params and descriptions in as part of this PR - hopefully that won't take a huge amount of time? This is very exciting! |
One more thought - for the next round of review would be great if you could zip up and attach a sphinx doc build to the PR! Just in case it's tricky to build locally. (e.g. code imports hiero stuff etc). |
After discussing it with Josh and Rob, we're not going to go the full publish2-style setup for how the hooks are configured. Instead, we're keeping the single hook for the custom export ui, but cleaning it up a bit. I've gone through and set base classes pointing back to the base_hooks (even for the old hooks), and I've put the no-op implementation of each for the create/get/set hook methods for custom ui into the base hook. I've then emptied out the hooks/hiero_customize_export_ui.py file, and linked to the (eventual) sphinx docs page in a comment. @manneohrstrom -- If you have any better suggestions around that last bit, please let me know! I've also added some basic docs to the older hooks, and things look much better in sphinx docs as a result. Back to you guys! @josh-t @manneohrstrom |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 Looks awesome. I think you will need to bump the core requirement to v0.18.124
.
self.parent.logger.debug( | ||
"Creating file system structure for %s %s..." % (entity_type, entity_id) | ||
) | ||
self.parent.sgtk.create_filesystem_structure(entity_type, [entity_id]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe 2 releases ago v0.18.124
For reference, here are the built docs: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added some comments. Looks great! Where it is easy to add, it would be great with some simple doc example of any complex data structure (e.g. dicts). Also, where we are referring to hiero API stuff, it would be great with links to the hiero api if possible!
docs/index.rst
Outdated
|
||
.. py:currentmodule:: base_hooks | ||
|
||
Creating custom UI elements |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's better that we order the hooks according to frequency - start with the ones that are most likely to be used by users, and the most advanced ones at the end!
docs/index.rst
Outdated
|
||
During the export process a number things can happen: | ||
|
||
- Status of associated Shot entities can be updated |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i would ❤️ if there was a flow chart diagram showing the process and included all the hooks. I think that would be a perfect overview for TDs to understand what is possible. happy to help putting this together if you need help.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, we can chat offline about this.
@@ -0,0 +1,106 @@ | |||
Shotgun Hiero Export API reference, |release| |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is something weird about the doc build - not sure where to put the comment so adding it here - the TOC seems to contain the same items twice. I just realised that we have the same issue for the nuke review docs but not for the publish. Could you investigate and potentially update the nuke review docs too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll ping you offline about this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have an internal ticket to look at this more closely. It's not immediately obvious why the ToC is wonky.
- Nuke scripts can be written into the project's filesystem structure for each Shot that's processed | ||
- Sequence and Shot entity data can be updated in Shotgun | ||
- Cuts can be update to include new CutItems built from the exported sequences | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there good hiero API documentation? Perhaps it's worth including a link here in the intro?
app.py
Outdated
@@ -50,6 +50,9 @@ | |||
ShotgunAudioExporterUI, | |||
ShotgunHieroObjectBase, | |||
) | |||
|
|||
import base_hooks |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why the absolute import? What if this app runs next another app which also imports base hooks?
We typically very very rarely do absolute imports and if we do, we name things explicitly (e.g. the import tk_hiero_export
above is slightly better but still unconventional!)
See the nuke review app for another way to do this: https://github.com/shotgunsoftware/tk-nuke-quickreview/blob/master/app.py#L30
|
||
:param processor: Processor The is being used, in case distinguishing between | ||
differnt exports is needed. | ||
:param processor: The processor object that is about to be started. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is a processor? Can we link to hiero docs?
:rtype: dict or None | ||
""" | ||
cut_item = self.parent.sgtk.shotgun.create("CutItem", cut_item_data) | ||
self.parent.logger.info("Created CutItem in Shotgun: %s" % cut_item) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this new or old code? I am not sure about current performance. But if you are running say a 200 shot conform, running create 200 times will definitely be slower than running a batch. But like you say, it depends on other factors, workflow ux etc. If this is old code and we are merely updating the docs, probably better to leave things the way they are!
try: | ||
# See if we can find a poster frame for the sequence and | ||
# turn that into a usable thumbnail. | ||
thumbnail = hiero_sequence.thumbnail(hiero_sequence.posterFrame()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not do it as part of the sphinx docs?
# not expressly granted therein are reserved by Shotgun Software Inc. | ||
|
||
# UI Hook | ||
# =========================== |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick: maybe drop this header. We don't normally have these kinds of things.
|
||
class HieroCustomizeExportUI(HookBaseClass): | ||
""" | ||
This class defines methods that can be used to customize the UI of the various |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could we include a screenshot to illustrate as part of the docs?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. Also added some overview sphinx docs for customizing UI. Basic stuff, but it's a good, brief intro with an example screenshot.
…cessors. Various bug fixes.
This is implementing the idea outlined in #41 from CBSD. It's providing hook methods for building custom widgets for each processor we provide, plus allows for custom preset properties to be created, and those properties then being used to set/initialize the custom widget.
In addition, to meet the workflow requirements outlined by CBSD, additional hook methods have been added to allow for customization of if/how Shots, Cuts, and CutItems are created/updated during the export process.
NOTE: There are likely going to be use cases for custom UI elements/properties that we then don't have hooks for in the right places to control certain export functionality via those custom properties. The
hiero_update_shot
andhiero_update_cuts
hooks added here provide the flexibility necessary to implement CBSD's custom shot processor behavior, but doesn't go beyond that.Hooks added:
Example custom UI based on CBSD's example: