From ae62e79695e07c1a24865fbec32e460b9c0540ce Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 30 Mar 2021 17:56:52 +0200 Subject: [PATCH 01/16] Global: empty tasks should return empty dict --- pype/plugins/publish/collect_hierarchy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/plugins/publish/collect_hierarchy.py b/pype/plugins/publish/collect_hierarchy.py index 5c5dbf018c..27332976e9 100644 --- a/pype/plugins/publish/collect_hierarchy.py +++ b/pype/plugins/publish/collect_hierarchy.py @@ -50,7 +50,7 @@ class CollectHierarchy(pyblish.api.ContextPlugin): # suppose that all instances are Shots shot_data['entity_type'] = 'Shot' - shot_data['tasks'] = instance.data.get("tasks") or [] + shot_data['tasks'] = instance.data.get("tasks") or {} shot_data["comments"] = instance.data.get("comments", []) shot_data['custom_attributes'] = { From ee6d1db703931faaf987fda61c7388df6d3e7a29 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 30 Mar 2021 18:52:41 +0200 Subject: [PATCH 02/16] Resolve: fixing import path to new `api` format --- pype/hosts/resolve/api/plugin.py | 2 +- pype/hosts/resolve/plugins/create/create_shot_clip.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/hosts/resolve/api/plugin.py b/pype/hosts/resolve/api/plugin.py index 0423f15c2a..974f29cb33 100644 --- a/pype/hosts/resolve/api/plugin.py +++ b/pype/hosts/resolve/api/plugin.py @@ -81,7 +81,7 @@ class CreatorWidget(QtWidgets.QDialog): ok_btn.clicked.connect(self._on_ok_clicked) cancel_btn.clicked.connect(self._on_cancel_clicked) - stylesheet = resolve.menu.load_stylesheet() + stylesheet = resolve.api.menu.load_stylesheet() self.setStyleSheet(stylesheet) def _on_ok_clicked(self): diff --git a/pype/hosts/resolve/plugins/create/create_shot_clip.py b/pype/hosts/resolve/plugins/create/create_shot_clip.py index 09b2b73775..bf860194d3 100644 --- a/pype/hosts/resolve/plugins/create/create_shot_clip.py +++ b/pype/hosts/resolve/plugins/create/create_shot_clip.py @@ -1,6 +1,6 @@ # from pprint import pformat from pype.hosts import resolve -from pype.hosts.resolve import lib +from pype.hosts.resolve.api import lib class CreateShotClip(resolve.Creator): From b24bbe5a5760e055716fbf6f88dfaf8870249ba0 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 30 Mar 2021 18:53:59 +0200 Subject: [PATCH 03/16] Resolve and Global: rename `master layer` to `hero track` --- pype/hosts/resolve/api/plugin.py | 30 +++++++++---------- .../plugins/create/create_shot_clip.py | 2 +- .../plugins/publish/collect_instances.py | 4 +-- pype/plugins/publish/collect_hierarchy.py | 2 +- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/pype/hosts/resolve/api/plugin.py b/pype/hosts/resolve/api/plugin.py index 974f29cb33..ada7549b01 100644 --- a/pype/hosts/resolve/api/plugin.py +++ b/pype/hosts/resolve/api/plugin.py @@ -697,13 +697,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 +717,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,33 +751,33 @@ 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: + # tag_hierarchy_data.update({"heroTrack": True}) 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: + if hero_track and self.review_layer: self.tag_data.update({"reviewTrack": self.review_layer}) def _solve_tag_hierarchy_data(self, hierarchy_formating_data): diff --git a/pype/hosts/resolve/plugins/create/create_shot_clip.py b/pype/hosts/resolve/plugins/create/create_shot_clip.py index bf860194d3..575e9f85a9 100644 --- a/pype/hosts/resolve/plugins/create/create_shot_clip.py +++ b/pype/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/pype/hosts/resolve/plugins/publish/collect_instances.py b/pype/hosts/resolve/plugins/publish/collect_instances.py index b1eafd512e..e76da13f48 100644 --- a/pype/hosts/resolve/plugins/publish/collect_instances.py +++ b/pype/hosts/resolve/plugins/publish/collect_instances.py @@ -102,10 +102,10 @@ class CollectInstances(pyblish.api.ContextPlugin): }) def create_shot_instance(self, context, timeline_item, **data): - master_layer = data.get("masterLayer") + hero_track = data.get("heroTrack") hierarchy_data = data.get("hierarchyData") - if not master_layer: + if not hero_track: return if not hierarchy_data: diff --git a/pype/plugins/publish/collect_hierarchy.py b/pype/plugins/publish/collect_hierarchy.py index 27332976e9..390ce443b6 100644 --- a/pype/plugins/publish/collect_hierarchy.py +++ b/pype/plugins/publish/collect_hierarchy.py @@ -40,7 +40,7 @@ class CollectHierarchy(pyblish.api.ContextPlugin): continue # exclude if not masterLayer True - if not instance.data.get("masterLayer"): + if not instance.data.get("heroTrack"): continue # get asset build data if any available From ceec8340cf7def24e4305d19d2d1ee66fb1ca238 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 30 Mar 2021 18:54:25 +0200 Subject: [PATCH 04/16] Resolve: fixing renamed function name --- pype/hosts/resolve/plugins/create/create_shot_clip.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/hosts/resolve/plugins/create/create_shot_clip.py b/pype/hosts/resolve/plugins/create/create_shot_clip.py index 575e9f85a9..86851c7074 100644 --- a/pype/hosts/resolve/plugins/create/create_shot_clip.py +++ b/pype/hosts/resolve/plugins/create/create_shot_clip.py @@ -244,7 +244,7 @@ class CreateShotClip(resolve.Creator): sq_markers = self.timeline.GetMarkers() # create media bin for compound clips (trackItems) - mp_folder = resolve.create_current_sequence_media_bin(self.timeline) + mp_folder = resolve.create_bin(self.timeline.GetName()) kwargs = { "ui_inputs": widget.result, From d179e3a246c0fa99f99840b588f1428b326d775f Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 30 Mar 2021 18:54:49 +0200 Subject: [PATCH 05/16] Resolve: fixing to new function usage --- pype/hosts/resolve/utility_scripts/OTIO_export.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pype/hosts/resolve/utility_scripts/OTIO_export.py b/pype/hosts/resolve/utility_scripts/OTIO_export.py index a1142f56dd..daa51ea7ac 100644 --- a/pype/hosts/resolve/utility_scripts/OTIO_export.py +++ b/pype/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") From 7354be225584c9f319bd76a65d3287509f1edced Mon Sep 17 00:00:00 2001 From: jezscha Date: Wed, 31 Mar 2021 08:41:12 +0000 Subject: [PATCH 06/16] Create draft PR for #916 From b315df6bbfe09978d11083163008d8d5218f5523 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 31 Mar 2021 11:19:50 +0200 Subject: [PATCH 07/16] Resolve: adding subset manager to menu --- pype/hosts/resolve/api/menu.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/pype/hosts/resolve/api/menu.py b/pype/hosts/resolve/api/menu.py index 73ea937513..0b049e4433 100644 --- a/pype/hosts/resolve/api/menu.py +++ b/pype/hosts/resolve/api/menu.py @@ -12,7 +12,8 @@ from avalon.tools import ( creator, loader, sceneinventory, - libraryloader + libraryloader, + subsetmanager ) @@ -64,6 +65,7 @@ class PypeMenu(QtWidgets.QWidget): 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( @@ -81,6 +83,7 @@ class PypeMenu(QtWidgets.QWidget): layout.addWidget(publish_btn) layout.addWidget(load_btn) layout.addWidget(inventory_btn) + layout.addWidget(subsetm_btn) layout.addWidget(Spacer(15, self)) @@ -102,6 +105,7 @@ class PypeMenu(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) @@ -127,6 +131,10 @@ class PypeMenu(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() From 310c9b2b6863f35952e6bc2d9e910141cdbcf1c1 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 31 Mar 2021 11:43:16 +0200 Subject: [PATCH 08/16] Resolve: listing instances --- pype/hosts/resolve/__init__.py | 6 +++++- pype/hosts/resolve/api/pipeline.py | 23 +++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/pype/hosts/resolve/__init__.py b/pype/hosts/resolve/__init__.py index 734e0bc5df..3e49ce3b9b 100644 --- a/pype/hosts/resolve/__init__.py +++ b/pype/hosts/resolve/__init__.py @@ -11,7 +11,9 @@ from .api.pipeline import ( update_container, publish, launch_workfiles_app, - maintained_selection + maintained_selection, + remove_instance, + list_instances ) from .api.lib import ( @@ -73,6 +75,8 @@ __all__ = [ "publish", "launch_workfiles_app", "maintained_selection", + "remove_instance", + "list_instances", # utils "setup", diff --git a/pype/hosts/resolve/api/pipeline.py b/pype/hosts/resolve/api/pipeline.py index 2d08203650..1ee9743518 100644 --- a/pype/hosts/resolve/api/pipeline.py +++ b/pype/hosts/resolve/api/pipeline.py @@ -258,3 +258,26 @@ 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.""" + pass + + +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"] + + # get pype tag data + tag_data = lib.get_timeline_item_pype_tag(timeline_item) + + if tag_data: + listed_instances.append(tag_data) + + return listed_instances From b1eb67794cabb0dfad280139f6fdec3cabb4dcf9 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 31 Mar 2021 11:43:44 +0200 Subject: [PATCH 09/16] Resolve: renaming plugins to precollect --- .../{collect_instances.py => precollect_instances.py} | 4 ++-- .../{collect_workfile.py => precollect_workfile.py} | 8 +++----- 2 files changed, 5 insertions(+), 7 deletions(-) rename pype/hosts/resolve/plugins/publish/{collect_instances.py => precollect_instances.py} (97%) rename pype/hosts/resolve/plugins/publish/{collect_workfile.py => precollect_workfile.py} (88%) diff --git a/pype/hosts/resolve/plugins/publish/collect_instances.py b/pype/hosts/resolve/plugins/publish/precollect_instances.py similarity index 97% rename from pype/hosts/resolve/plugins/publish/collect_instances.py rename to pype/hosts/resolve/plugins/publish/precollect_instances.py index b1eafd512e..e54bda0a49 100644 --- a/pype/hosts/resolve/plugins/publish/collect_instances.py +++ b/pype/hosts/resolve/plugins/publish/precollect_instances.py @@ -5,11 +5,11 @@ from pype.hosts import resolve from pprint import pformat -class CollectInstances(pyblish.api.ContextPlugin): +class PrecollectInstances(pyblish.api.ContextPlugin): """Collect all Track items selection.""" order = pyblish.api.CollectorOrder - 0.59 - label = "Collect Instances" + label = "Precollect Instances" hosts = ["resolve"] def process(self, context): diff --git a/pype/hosts/resolve/plugins/publish/collect_workfile.py b/pype/hosts/resolve/plugins/publish/precollect_workfile.py similarity index 88% rename from pype/hosts/resolve/plugins/publish/collect_workfile.py rename to pype/hosts/resolve/plugins/publish/precollect_workfile.py index f7f90c9689..3e9a7f26b9 100644 --- a/pype/hosts/resolve/plugins/publish/collect_workfile.py +++ b/pype/hosts/resolve/plugins/publish/precollect_workfile.py @@ -9,10 +9,10 @@ from pype.hosts.resolve.otio import davinci_export reload(davinci_export) -class CollectWorkfile(pyblish.api.ContextPlugin): - """Inject the current working file into context""" +class PrecollectWorkfile(pyblish.api.ContextPlugin): + """Precollect the current working file into context""" - label = "Collect Workfile" + label = "Precollect Workfile" order = pyblish.api.CollectorOrder - 0.6 def process(self, context): @@ -21,8 +21,6 @@ class CollectWorkfile(pyblish.api.ContextPlugin): 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 From cd2372300a7e979066a1ab2a0fd3a5e5cf143c1b Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 31 Mar 2021 11:52:05 +0200 Subject: [PATCH 10/16] Resolve: improving subset manager labels --- pype/hosts/resolve/api/pipeline.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pype/hosts/resolve/api/pipeline.py b/pype/hosts/resolve/api/pipeline.py index 1ee9743518..d1d1b5185a 100644 --- a/pype/hosts/resolve/api/pipeline.py +++ b/pype/hosts/resolve/api/pipeline.py @@ -273,11 +273,15 @@ def list_instances(): for timeline_item_data in selected_timeline_items: timeline_item = timeline_item_data["clip"]["item"] + ti_name = timeline_item.GetName().split(".")[0] # get pype 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 From 00e3b75feb27d5f94a0a1c7daefd140f8334725a Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 31 Mar 2021 12:35:42 +0200 Subject: [PATCH 11/16] Auto stash before merge of "feature/916-resolve-instance-manager" and "3.0/bugfix/resolve-functionality-issues" --- pype/hosts/resolve/api/plugin.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pype/hosts/resolve/api/plugin.py b/pype/hosts/resolve/api/plugin.py index ada7549b01..4ed8c5bc4a 100644 --- a/pype/hosts/resolve/api/plugin.py +++ b/pype/hosts/resolve/api/plugin.py @@ -777,9 +777,11 @@ class PublishClip: # add data to return data dict self.tag_data.update(tag_hierarchy_data) + # 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 From a1b1c43d4faf32d23bb219d7ddf166c75ee35b38 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 31 Mar 2021 12:47:42 +0200 Subject: [PATCH 12/16] Resolve: adding removing instance action --- pype/hosts/resolve/api/pipeline.py | 23 ++++++++++++++++++++++- pype/hosts/resolve/api/plugin.py | 4 ++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/pype/hosts/resolve/api/pipeline.py b/pype/hosts/resolve/api/pipeline.py index d1d1b5185a..9a74e50e6f 100644 --- a/pype/hosts/resolve/api/pipeline.py +++ b/pype/hosts/resolve/api/pipeline.py @@ -262,7 +262,28 @@ def on_pyblish_instance_toggled(instance, old_value, new_value): def remove_instance(instance): """Remove instance marker from track item.""" - pass + 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 pype 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(): diff --git a/pype/hosts/resolve/api/plugin.py b/pype/hosts/resolve/api/plugin.py index 4ed8c5bc4a..0ba6c5e745 100644 --- a/pype/hosts/resolve/api/plugin.py +++ b/pype/hosts/resolve/api/plugin.py @@ -1,4 +1,5 @@ import re +import uuid from avalon import api import pype.api as pype from pype.hosts import resolve @@ -777,6 +778,9 @@ class PublishClip: # add data to return data dict self.tag_data.update(tag_hierarchy_data) + # 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}) From 9228508dff48bc15a670f5b24a26302851132b92 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 6 Apr 2021 15:11:00 +0200 Subject: [PATCH 13/16] 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") From e46c3a9fa7730e71e679138e4e7861ae3ec14afa Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 6 Apr 2021 15:13:03 +0200 Subject: [PATCH 14/16] Resolve: updating menu to disable unwritten features --- openpype/hosts/resolve/api/menu.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/openpype/hosts/resolve/api/menu.py b/openpype/hosts/resolve/api/menu.py index ecd2708440..c0471ebfbe 100644 --- a/openpype/hosts/resolve/api/menu.py +++ b/openpype/hosts/resolve/api/menu.py @@ -67,13 +67,13 @@ class OpenPypeMenu(QtWidgets.QWidget): 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 - ) + # 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) @@ -91,12 +91,12 @@ class OpenPypeMenu(QtWidgets.QWidget): 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) @@ -107,9 +107,9 @@ class OpenPypeMenu(QtWidgets.QWidget): 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") From 9a0f20ef95e18767699a624a575c84e834fd1fa1 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 6 Apr 2021 15:15:02 +0200 Subject: [PATCH 15/16] Resolve: adding correct ux convention to menu --- openpype/hosts/resolve/api/menu.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/openpype/hosts/resolve/api/menu.py b/openpype/hosts/resolve/api/menu.py index c0471ebfbe..e7be3fc963 100644 --- a/openpype/hosts/resolve/api/menu.py +++ b/openpype/hosts/resolve/api/menu.py @@ -60,13 +60,13 @@ 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) - subsetm_btn = QtWidgets.QPushButton("Subset Manager", self) - libload_btn = QtWidgets.QPushButton("Library", 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 @@ -89,7 +89,7 @@ class OpenPypeMenu(QtWidgets.QWidget): layout.addWidget(libload_btn) - layout.addWidget(Spacer(15, self)) + # layout.addWidget(Spacer(15, self)) # layout.addWidget(rename_btn) From d503395818ec8113bb0c402535e1c44aac13d61a Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 7 Apr 2021 10:43:36 +0200 Subject: [PATCH 16/16] resolve: fix transition merge --- openpype/hosts/resolve/hooks/pre_resolve_setup.py | 2 +- .../hosts/resolve/plugins/publish/precollect_instances.py | 2 +- openpype/hosts/resolve/plugins/publish/precollect_workfile.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/resolve/hooks/pre_resolve_setup.py b/openpype/hosts/resolve/hooks/pre_resolve_setup.py index 0ee55d3790..bcb27e24fc 100644 --- a/openpype/hosts/resolve/hooks/pre_resolve_setup.py +++ b/openpype/hosts/resolve/hooks/pre_resolve_setup.py @@ -44,7 +44,7 @@ class ResolvePrelaunch(PreLaunchHook): self.launch_context.env["PRE_PYTHON_SCRIPT"] = pre_py_sc self.log.debug(f"-- pre_py_sc: `{pre_py_sc}`...") try: - __import__("pype.hosts.resolve") + __import__("openpype.hosts.resolve") __import__("pyblish") except ImportError: diff --git a/openpype/hosts/resolve/plugins/publish/precollect_instances.py b/openpype/hosts/resolve/plugins/publish/precollect_instances.py index 4fabe2b8a0..c38cbc4f73 100644 --- a/openpype/hosts/resolve/plugins/publish/precollect_instances.py +++ b/openpype/hosts/resolve/plugins/publish/precollect_instances.py @@ -1,5 +1,5 @@ import pyblish -from pype.hosts import resolve +from openpype.hosts import resolve # # developer reload modules from pprint import pformat diff --git a/openpype/hosts/resolve/plugins/publish/precollect_workfile.py b/openpype/hosts/resolve/plugins/publish/precollect_workfile.py index 3e9a7f26b9..ee05fb6f13 100644 --- a/openpype/hosts/resolve/plugins/publish/precollect_workfile.py +++ b/openpype/hosts/resolve/plugins/publish/precollect_workfile.py @@ -1,11 +1,11 @@ import pyblish.api -from pype.hosts import resolve +from openpype.hosts import resolve from avalon import api as avalon from pprint import pformat # dev from importlib import reload -from pype.hosts.resolve.otio import davinci_export +from openpype.hosts.resolve.otio import davinci_export reload(davinci_export)