From 9228508dff48bc15a670f5b24a26302851132b92 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 6 Apr 2021 15:11:00 +0200 Subject: [PATCH] Resolve: merge openpype fixes --- openpype/hosts/resolve/api/menu.py | 52 ++++--- openpype/hosts/resolve/api/pipeline.py | 48 +++++++ openpype/hosts/resolve/api/plugin.py | 35 +++-- .../plugins/create/create_shot_clip.py | 2 +- .../plugins/publish/collect_instances.py | 129 ------------------ .../plugins/publish/collect_workfile.py | 54 -------- .../resolve/utility_scripts/OTIO_export.py | 3 +- 7 files changed, 100 insertions(+), 223 deletions(-) delete mode 100644 openpype/hosts/resolve/plugins/publish/collect_instances.py delete mode 100644 openpype/hosts/resolve/plugins/publish/collect_workfile.py diff --git a/openpype/hosts/resolve/api/menu.py b/openpype/hosts/resolve/api/menu.py index 5ed7aeab34..ecd2708440 100644 --- a/openpype/hosts/resolve/api/menu.py +++ b/openpype/hosts/resolve/api/menu.py @@ -12,7 +12,8 @@ from avalon.tools import ( creator, loader, sceneinventory, - libraryloader + libraryloader, + subsetmanager ) @@ -59,19 +60,20 @@ class OpenPypeMenu(QtWidgets.QWidget): ) self.setWindowTitle("OpenPype") - workfiles_btn = QtWidgets.QPushButton("Workfiles ...", self) - create_btn = QtWidgets.QPushButton("Create ...", self) - publish_btn = QtWidgets.QPushButton("Publish ...", self) - load_btn = QtWidgets.QPushButton("Load ...", self) - inventory_btn = QtWidgets.QPushButton("Inventory ...", self) - libload_btn = QtWidgets.QPushButton("Library ...", self) - # rename_btn = QtWidgets.QPushButton("Rename ...", self) - # set_colorspace_btn = QtWidgets.QPushButton( - # "Set colorspace from presets", self - # ) - # reset_resolution_btn = QtWidgets.QPushButton( - # "Reset Resolution from peresets", self - # ) + workfiles_btn = QtWidgets.QPushButton("Workfiles", self) + create_btn = QtWidgets.QPushButton("Create", self) + publish_btn = QtWidgets.QPushButton("Publish", self) + load_btn = QtWidgets.QPushButton("Load", self) + inventory_btn = QtWidgets.QPushButton("Inventory", self) + subsetm_btn = QtWidgets.QPushButton("Subset Manager", self) + libload_btn = QtWidgets.QPushButton("Library", self) + rename_btn = QtWidgets.QPushButton("Rename", self) + set_colorspace_btn = QtWidgets.QPushButton( + "Set colorspace from presets", self + ) + reset_resolution_btn = QtWidgets.QPushButton( + "Reset Resolution from peresets", self + ) layout = QtWidgets.QVBoxLayout(self) layout.setContentsMargins(10, 20, 10, 20) @@ -81,19 +83,20 @@ class OpenPypeMenu(QtWidgets.QWidget): layout.addWidget(publish_btn) layout.addWidget(load_btn) layout.addWidget(inventory_btn) + layout.addWidget(subsetm_btn) layout.addWidget(Spacer(15, self)) layout.addWidget(libload_btn) - # layout.addWidget(Spacer(15, self)) + layout.addWidget(Spacer(15, self)) - # layout.addWidget(rename_btn) + layout.addWidget(rename_btn) - # layout.addWidget(Spacer(15, self)) + layout.addWidget(Spacer(15, self)) - # layout.addWidget(set_colorspace_btn) - # layout.addWidget(reset_resolution_btn) + layout.addWidget(set_colorspace_btn) + layout.addWidget(reset_resolution_btn) self.setLayout(layout) @@ -102,10 +105,11 @@ class OpenPypeMenu(QtWidgets.QWidget): publish_btn.clicked.connect(self.on_publish_clicked) load_btn.clicked.connect(self.on_load_clicked) inventory_btn.clicked.connect(self.on_inventory_clicked) + subsetm_btn.clicked.connect(self.on_subsetm_clicked) libload_btn.clicked.connect(self.on_libload_clicked) - # rename_btn.clicked.connect(self.on_rename_clicked) - # set_colorspace_btn.clicked.connect(self.on_set_colorspace_clicked) - # reset_resolution_btn.clicked.connect(self.on_reset_resolution_clicked) + rename_btn.clicked.connect(self.on_rename_clicked) + set_colorspace_btn.clicked.connect(self.on_set_colorspace_clicked) + reset_resolution_btn.clicked.connect(self.on_reset_resolution_clicked) def on_workfile_clicked(self): print("Clicked Workfile") @@ -127,6 +131,10 @@ class OpenPypeMenu(QtWidgets.QWidget): print("Clicked Inventory") sceneinventory.show() + def on_subsetm_clicked(self): + print("Clicked Subset Manager") + subsetmanager.show() + def on_libload_clicked(self): print("Clicked Library") libraryloader.show() diff --git a/openpype/hosts/resolve/api/pipeline.py b/openpype/hosts/resolve/api/pipeline.py index d4d928a7d9..0e6d5aff4d 100644 --- a/openpype/hosts/resolve/api/pipeline.py +++ b/openpype/hosts/resolve/api/pipeline.py @@ -258,3 +258,51 @@ def on_pyblish_instance_toggled(instance, old_value, new_value): # Whether instances should be passthrough based on new value timeline_item = instance.data["item"] set_publish_attribute(timeline_item, new_value) + + +def remove_instance(instance): + """Remove instance marker from track item.""" + instance_id = instance.get("uuid") + + selected_timeline_items = lib.get_current_timeline_items( + filter=True, selecting_color=lib.publish_clip_color) + + found_ti = None + for timeline_item_data in selected_timeline_items: + timeline_item = timeline_item_data["clip"]["item"] + + # get openpype tag data + tag_data = lib.get_timeline_item_pype_tag(timeline_item) + _ti_id = tag_data.get("uuid") + if _ti_id == instance_id: + found_ti = timeline_item + break + + if found_ti is None: + return + + # removing instance by marker color + print(f"Removing instance: {found_ti.GetName()}") + found_ti.DeleteMarkersByColor(lib.pype_marker_color) + + +def list_instances(): + """List all created instances from current workfile.""" + listed_instances = [] + selected_timeline_items = lib.get_current_timeline_items( + filter=True, selecting_color=lib.publish_clip_color) + + for timeline_item_data in selected_timeline_items: + timeline_item = timeline_item_data["clip"]["item"] + ti_name = timeline_item.GetName().split(".")[0] + + # get openpype tag data + tag_data = lib.get_timeline_item_pype_tag(timeline_item) + + if tag_data: + asset = tag_data.get("asset") + subset = tag_data.get("subset") + tag_data["label"] = f"{ti_name} [{asset}-{subset}]" + listed_instances.append(tag_data) + + return listed_instances diff --git a/openpype/hosts/resolve/api/plugin.py b/openpype/hosts/resolve/api/plugin.py index 3833795b96..4712d0a8b9 100644 --- a/openpype/hosts/resolve/api/plugin.py +++ b/openpype/hosts/resolve/api/plugin.py @@ -1,4 +1,5 @@ import re +import uuid from avalon import api import openpype.api as pype from openpype.hosts import resolve @@ -697,13 +698,13 @@ class PublishClip: Populating the tag data into internal variable self.tag_data """ # define vertical sync attributes - master_layer = True + hero_track = True self.review_layer = "" if self.vertical_sync: # check if track name is not in driving layer if self.track_name not in self.driving_layer: # if it is not then define vertical sync as None - master_layer = False + hero_track = False # increasing steps by index of rename iteration self.count_steps *= self.rename_index @@ -717,7 +718,7 @@ class PublishClip: self.tag_data[_k] = _v["value"] # driving layer is set as positive match - if master_layer or self.vertical_sync: + if hero_track or self.vertical_sync: # mark review layer if self.review_track and ( self.review_track not in self.review_track_default): @@ -751,35 +752,39 @@ class PublishClip: hierarchy_formating_data ) - tag_hierarchy_data.update({"masterLayer": True}) - if master_layer and self.vertical_sync: - # tag_hierarchy_data.update({"masterLayer": True}) + tag_hierarchy_data.update({"heroTrack": True}) + if hero_track and self.vertical_sync: self.vertical_clip_match.update({ (self.clip_in, self.clip_out): tag_hierarchy_data }) - if not master_layer and self.vertical_sync: + if not hero_track and self.vertical_sync: # driving layer is set as negative match - for (_in, _out), master_data in self.vertical_clip_match.items(): - master_data.update({"masterLayer": False}) + for (_in, _out), hero_data in self.vertical_clip_match.items(): + hero_data.update({"heroTrack": False}) if _in == self.clip_in and _out == self.clip_out: - data_subset = master_data["subset"] - # add track index in case duplicity of names in master data + data_subset = hero_data["subset"] + # add track index in case duplicity of names in hero data if self.subset in data_subset: - master_data["subset"] = self.subset + str( + hero_data["subset"] = self.subset + str( self.track_index) # in case track name and subset name is the same then add if self.subset_name == self.track_name: - master_data["subset"] = self.subset + hero_data["subset"] = self.subset # assing data to return hierarchy data to tag - tag_hierarchy_data = master_data + tag_hierarchy_data = hero_data # add data to return data dict self.tag_data.update(tag_hierarchy_data) - if master_layer and self.review_layer: + # add uuid to tag data + self.tag_data["uuid"] = str(uuid.uuid4()) + + # add review track only to hero track + if hero_track and self.review_layer: self.tag_data.update({"reviewTrack": self.review_layer}) + def _solve_tag_hierarchy_data(self, hierarchy_formating_data): """ Solve tag data from hierarchy data and templates. """ # fill up clip name and hierarchy keys diff --git a/openpype/hosts/resolve/plugins/create/create_shot_clip.py b/openpype/hosts/resolve/plugins/create/create_shot_clip.py index 2916a52298..41fdbf5c61 100644 --- a/openpype/hosts/resolve/plugins/create/create_shot_clip.py +++ b/openpype/hosts/resolve/plugins/create/create_shot_clip.py @@ -117,7 +117,7 @@ class CreateShotClip(resolve.Creator): "vSyncTrack": { "value": gui_tracks, # noqa "type": "QComboBox", - "label": "Master track", + "label": "Hero track", "target": "ui", "toolTip": "Select driving track name which should be mastering all others", # noqa "order": 1} diff --git a/openpype/hosts/resolve/plugins/publish/collect_instances.py b/openpype/hosts/resolve/plugins/publish/collect_instances.py deleted file mode 100644 index f4eeb39754..0000000000 --- a/openpype/hosts/resolve/plugins/publish/collect_instances.py +++ /dev/null @@ -1,129 +0,0 @@ -import pyblish -from openpype.hosts import resolve - -# # developer reload modules -from pprint import pformat - - -class CollectInstances(pyblish.api.ContextPlugin): - """Collect all Track items selection.""" - - order = pyblish.api.CollectorOrder - 0.59 - label = "Collect Instances" - hosts = ["resolve"] - - def process(self, context): - otio_timeline = context.data["otioTimeline"] - selected_timeline_items = resolve.get_current_timeline_items( - filter=True, selecting_color=resolve.publish_clip_color) - - self.log.info( - "Processing enabled track items: {}".format( - len(selected_timeline_items))) - - for timeline_item_data in selected_timeline_items: - - data = dict() - timeline_item = timeline_item_data["clip"]["item"] - - # get openpype tag data - tag_data = resolve.get_timeline_item_pype_tag(timeline_item) - self.log.debug(f"__ tag_data: {pformat(tag_data)}") - - if not tag_data: - continue - - if tag_data.get("id") != "pyblish.avalon.instance": - continue - - media_pool_item = timeline_item.GetMediaPoolItem() - clip_property = media_pool_item.GetClipProperty() - self.log.debug(f"clip_property: {clip_property}") - - # add tag data to instance data - data.update({ - k: v for k, v in tag_data.items() - if k not in ("id", "applieswhole", "label") - }) - - asset = tag_data["asset"] - subset = tag_data["subset"] - - # insert family into families - family = tag_data["family"] - families = [str(f) for f in tag_data["families"]] - families.insert(0, str(family)) - - data.update({ - "name": "{} {} {}".format(asset, subset, families), - "asset": asset, - "item": timeline_item, - "families": families, - "publish": resolve.get_publish_attribute(timeline_item), - "fps": context.data["fps"] - }) - - # otio clip data - otio_data = resolve.get_otio_clip_instance_data( - otio_timeline, timeline_item_data) or {} - data.update(otio_data) - - # add resolution - self.get_resolution_to_data(data, context) - - # create instance - instance = context.create_instance(**data) - - # create shot instance for shot attributes create/update - self.create_shot_instance(context, timeline_item, **data) - - self.log.info("Creating instance: {}".format(instance)) - self.log.debug( - "_ instance.data: {}".format(pformat(instance.data))) - - def get_resolution_to_data(self, data, context): - assert data.get("otioClip"), "Missing `otioClip` data" - - # solve source resolution option - if data.get("sourceResolution", None): - otio_clip_metadata = data[ - "otioClip"].media_reference.metadata - data.update({ - "resolutionWidth": otio_clip_metadata["width"], - "resolutionHeight": otio_clip_metadata["height"], - "pixelAspect": otio_clip_metadata["pixelAspect"] - }) - else: - otio_tl_metadata = context.data["otioTimeline"].metadata - data.update({ - "resolutionWidth": otio_tl_metadata["width"], - "resolutionHeight": otio_tl_metadata["height"], - "pixelAspect": otio_tl_metadata["pixelAspect"] - }) - - def create_shot_instance(self, context, timeline_item, **data): - master_layer = data.get("masterLayer") - hierarchy_data = data.get("hierarchyData") - - if not master_layer: - return - - if not hierarchy_data: - return - - asset = data["asset"] - subset = "shotMain" - - # insert family into families - family = "shot" - - data.update({ - "name": "{} {} {}".format(asset, subset, family), - "subset": subset, - "asset": asset, - "family": family, - "families": [], - "publish": resolve.get_publish_attribute(timeline_item) - }) - - context.create_instance(**data) diff --git a/openpype/hosts/resolve/plugins/publish/collect_workfile.py b/openpype/hosts/resolve/plugins/publish/collect_workfile.py deleted file mode 100644 index a66284ed02..0000000000 --- a/openpype/hosts/resolve/plugins/publish/collect_workfile.py +++ /dev/null @@ -1,54 +0,0 @@ -import pyblish.api -from openpype.hosts import resolve -from avalon import api as avalon -from pprint import pformat - -# dev -from importlib import reload -from openpype.hosts.resolve.otio import davinci_export -reload(davinci_export) - - -class CollectWorkfile(pyblish.api.ContextPlugin): - """Inject the current working file into context""" - - label = "Collect Workfile" - order = pyblish.api.CollectorOrder - 0.6 - - def process(self, context): - - asset = avalon.Session["AVALON_ASSET"] - subset = "workfile" - project = resolve.get_current_project() - fps = project.GetSetting("timelineFrameRate") - - active_timeline = resolve.get_current_timeline() - video_tracks = resolve.get_video_track_names() - - # adding otio timeline to context - otio_timeline = davinci_export.create_otio_timeline(project) - - instance_data = { - "name": "{}_{}".format(asset, subset), - "asset": asset, - "subset": "{}{}".format(asset, subset.capitalize()), - "item": project, - "family": "workfile" - } - - # create instance with workfile - instance = context.create_instance(**instance_data) - - # update context with main project attributes - context_data = { - "activeProject": project, - "otioTimeline": otio_timeline, - "videoTracks": video_tracks, - "currentFile": project.GetName(), - "fps": fps, - } - context.data.update(context_data) - - self.log.info("Creating instance: {}".format(instance)) - self.log.debug("__ instance.data: {}".format(pformat(instance.data))) - self.log.debug("__ context_data: {}".format(pformat(context_data))) diff --git a/openpype/hosts/resolve/utility_scripts/OTIO_export.py b/openpype/hosts/resolve/utility_scripts/OTIO_export.py index 91bc2c5700..0431eb7daa 100644 --- a/openpype/hosts/resolve/utility_scripts/OTIO_export.py +++ b/openpype/hosts/resolve/utility_scripts/OTIO_export.py @@ -58,9 +58,8 @@ def _close_window(event): def _export_button(event): pm = resolve.GetProjectManager() project = pm.GetCurrentProject() - fps = project.GetSetting("timelineFrameRate") timeline = project.GetCurrentTimeline() - otio_timeline = otio_export.create_otio_timeline(timeline, fps) + otio_timeline = otio_export.create_otio_timeline(project) otio_path = os.path.join( itm["exportfilebttn"].Text, timeline.GetName() + ".otio")