diff --git a/CHANGELOG.md b/CHANGELOG.md index 3cca692b68..f9820dec45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,73 @@ # Changelog +## [3.14.9](https://github.com/pypeclub/OpenPype/tree/3.14.9) + +[Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.14.8...3.14.9) + +### 📖 Documentation + +- Documentation: Testing on Deadline [\#4185](https://github.com/pypeclub/OpenPype/pull/4185) +- Consistent Python version [\#4160](https://github.com/pypeclub/OpenPype/pull/4160) + +**🆕 New features** + +- Feature/op 4397 gl tf extractor for maya [\#4192](https://github.com/pypeclub/OpenPype/pull/4192) +- Maya: Extractor for Unreal SkeletalMesh [\#4174](https://github.com/pypeclub/OpenPype/pull/4174) +- 3dsmax: integration [\#4168](https://github.com/pypeclub/OpenPype/pull/4168) +- Blender: Extract Alembic Animations [\#4128](https://github.com/pypeclub/OpenPype/pull/4128) +- Unreal: Load Alembic Animations [\#4127](https://github.com/pypeclub/OpenPype/pull/4127) + +**🚀 Enhancements** + +- Houdini: Use new interface class name for publish host [\#4220](https://github.com/pypeclub/OpenPype/pull/4220) +- General: Default command for headless mode is interactive [\#4203](https://github.com/pypeclub/OpenPype/pull/4203) +- Maya: Enhanced ASS publishing [\#4196](https://github.com/pypeclub/OpenPype/pull/4196) +- Feature/op 3924 implement ass extractor [\#4188](https://github.com/pypeclub/OpenPype/pull/4188) +- File transactions: Source path is destination path [\#4184](https://github.com/pypeclub/OpenPype/pull/4184) +- Deadline: improve environment processing [\#4182](https://github.com/pypeclub/OpenPype/pull/4182) +- General: Comment per instance in Publisher [\#4178](https://github.com/pypeclub/OpenPype/pull/4178) +- Ensure Mongo database directory exists in Windows. [\#4166](https://github.com/pypeclub/OpenPype/pull/4166) +- Note about unrestricted execution on Windows. [\#4161](https://github.com/pypeclub/OpenPype/pull/4161) +- Maya: Enable thumbnail transparency on extraction. [\#4147](https://github.com/pypeclub/OpenPype/pull/4147) +- Maya: Disable viewport Pan/Zoom on playblast extraction. [\#4146](https://github.com/pypeclub/OpenPype/pull/4146) +- Maya: Optional viewport refresh on pointcache extraction [\#4144](https://github.com/pypeclub/OpenPype/pull/4144) +- CelAction: refactory integration to current openpype [\#4140](https://github.com/pypeclub/OpenPype/pull/4140) +- Maya: create and publish bounding box geometry [\#4131](https://github.com/pypeclub/OpenPype/pull/4131) +- Changed the UOpenPypePublishInstance to use the UDataAsset class [\#4124](https://github.com/pypeclub/OpenPype/pull/4124) +- General: Collection Audio speed up [\#4110](https://github.com/pypeclub/OpenPype/pull/4110) +- Maya: keep existing AOVs when creating render instance [\#4087](https://github.com/pypeclub/OpenPype/pull/4087) +- General: Oiio conversion multipart fix [\#4060](https://github.com/pypeclub/OpenPype/pull/4060) + +**🐛 Bug fixes** + +- Publisher: Signal type issues in Python 2 DCCs [\#4230](https://github.com/pypeclub/OpenPype/pull/4230) +- Blender: Fix Layout Family Versioning [\#4228](https://github.com/pypeclub/OpenPype/pull/4228) +- Blender: Fix Create Camera "Use selection" [\#4226](https://github.com/pypeclub/OpenPype/pull/4226) +- TrayPublisher - join needs list [\#4224](https://github.com/pypeclub/OpenPype/pull/4224) +- General: Event callbacks pass event to callbacks as expected [\#4210](https://github.com/pypeclub/OpenPype/pull/4210) +- Build:Revert .toml update of Gazu [\#4207](https://github.com/pypeclub/OpenPype/pull/4207) +- Nuke: fixed imageio node overrides subset filter [\#4202](https://github.com/pypeclub/OpenPype/pull/4202) +- Maya: pointcache [\#4201](https://github.com/pypeclub/OpenPype/pull/4201) +- Unreal: Support for Unreal Engine 5.1 [\#4199](https://github.com/pypeclub/OpenPype/pull/4199) +- General: Integrate thumbnail looks for thumbnail to multiple places [\#4181](https://github.com/pypeclub/OpenPype/pull/4181) +- Various minor bugfixes [\#4172](https://github.com/pypeclub/OpenPype/pull/4172) +- Nuke/Hiero: Remove tkinter library paths before launch [\#4171](https://github.com/pypeclub/OpenPype/pull/4171) +- Flame: vertical alignment of layers [\#4169](https://github.com/pypeclub/OpenPype/pull/4169) +- Nuke: correct detection of viewer and display [\#4165](https://github.com/pypeclub/OpenPype/pull/4165) +- Settings UI: Don't create QApplication if already exists [\#4156](https://github.com/pypeclub/OpenPype/pull/4156) +- General: Extract review handle start offset of sequences [\#4152](https://github.com/pypeclub/OpenPype/pull/4152) +- Maya: Maintain time connections on Alembic update. [\#4143](https://github.com/pypeclub/OpenPype/pull/4143) + +**🔀 Refactored code** + +- General: Use qtpy in modules and hosts UIs which are running in OpenPype process [\#4225](https://github.com/pypeclub/OpenPype/pull/4225) +- Tools: Use qtpy instead of Qt in standalone tools [\#4223](https://github.com/pypeclub/OpenPype/pull/4223) +- General: Use qtpy in settings UI [\#4215](https://github.com/pypeclub/OpenPype/pull/4215) + +**Merged pull requests:** + +- layout publish more than one container issue [\#4098](https://github.com/pypeclub/OpenPype/pull/4098) + ## [3.14.8](https://github.com/pypeclub/OpenPype/tree/3.14.8) [Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.14.7...3.14.8) @@ -21,7 +89,6 @@ - Maya: Looks - add all connections [\#4135](https://github.com/pypeclub/OpenPype/pull/4135) - General: Fix variable check in collect anatomy instance data [\#4117](https://github.com/pypeclub/OpenPype/pull/4117) - ## [3.14.7](https://github.com/pypeclub/OpenPype/tree/3.14.7) [Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.14.6...3.14.7) diff --git a/HISTORY.md b/HISTORY.md index f4e132488b..f24e95b2e1 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,74 @@ # Changelog + +## [3.14.9](https://github.com/pypeclub/OpenPype/tree/3.14.9) + +[Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.14.8...3.14.9) + +### 📖 Documentation + +- Documentation: Testing on Deadline [\#4185](https://github.com/pypeclub/OpenPype/pull/4185) +- Consistent Python version [\#4160](https://github.com/pypeclub/OpenPype/pull/4160) + +**🆕 New features** + +- Feature/op 4397 gl tf extractor for maya [\#4192](https://github.com/pypeclub/OpenPype/pull/4192) +- Maya: Extractor for Unreal SkeletalMesh [\#4174](https://github.com/pypeclub/OpenPype/pull/4174) +- 3dsmax: integration [\#4168](https://github.com/pypeclub/OpenPype/pull/4168) +- Blender: Extract Alembic Animations [\#4128](https://github.com/pypeclub/OpenPype/pull/4128) +- Unreal: Load Alembic Animations [\#4127](https://github.com/pypeclub/OpenPype/pull/4127) + +**🚀 Enhancements** + +- Houdini: Use new interface class name for publish host [\#4220](https://github.com/pypeclub/OpenPype/pull/4220) +- General: Default command for headless mode is interactive [\#4203](https://github.com/pypeclub/OpenPype/pull/4203) +- Maya: Enhanced ASS publishing [\#4196](https://github.com/pypeclub/OpenPype/pull/4196) +- Feature/op 3924 implement ass extractor [\#4188](https://github.com/pypeclub/OpenPype/pull/4188) +- File transactions: Source path is destination path [\#4184](https://github.com/pypeclub/OpenPype/pull/4184) +- Deadline: improve environment processing [\#4182](https://github.com/pypeclub/OpenPype/pull/4182) +- General: Comment per instance in Publisher [\#4178](https://github.com/pypeclub/OpenPype/pull/4178) +- Ensure Mongo database directory exists in Windows. [\#4166](https://github.com/pypeclub/OpenPype/pull/4166) +- Note about unrestricted execution on Windows. [\#4161](https://github.com/pypeclub/OpenPype/pull/4161) +- Maya: Enable thumbnail transparency on extraction. [\#4147](https://github.com/pypeclub/OpenPype/pull/4147) +- Maya: Disable viewport Pan/Zoom on playblast extraction. [\#4146](https://github.com/pypeclub/OpenPype/pull/4146) +- Maya: Optional viewport refresh on pointcache extraction [\#4144](https://github.com/pypeclub/OpenPype/pull/4144) +- CelAction: refactory integration to current openpype [\#4140](https://github.com/pypeclub/OpenPype/pull/4140) +- Maya: create and publish bounding box geometry [\#4131](https://github.com/pypeclub/OpenPype/pull/4131) +- Changed the UOpenPypePublishInstance to use the UDataAsset class [\#4124](https://github.com/pypeclub/OpenPype/pull/4124) +- General: Collection Audio speed up [\#4110](https://github.com/pypeclub/OpenPype/pull/4110) +- Maya: keep existing AOVs when creating render instance [\#4087](https://github.com/pypeclub/OpenPype/pull/4087) +- General: Oiio conversion multipart fix [\#4060](https://github.com/pypeclub/OpenPype/pull/4060) + +**🐛 Bug fixes** + +- Publisher: Signal type issues in Python 2 DCCs [\#4230](https://github.com/pypeclub/OpenPype/pull/4230) +- Blender: Fix Layout Family Versioning [\#4228](https://github.com/pypeclub/OpenPype/pull/4228) +- Blender: Fix Create Camera "Use selection" [\#4226](https://github.com/pypeclub/OpenPype/pull/4226) +- TrayPublisher - join needs list [\#4224](https://github.com/pypeclub/OpenPype/pull/4224) +- General: Event callbacks pass event to callbacks as expected [\#4210](https://github.com/pypeclub/OpenPype/pull/4210) +- Build:Revert .toml update of Gazu [\#4207](https://github.com/pypeclub/OpenPype/pull/4207) +- Nuke: fixed imageio node overrides subset filter [\#4202](https://github.com/pypeclub/OpenPype/pull/4202) +- Maya: pointcache [\#4201](https://github.com/pypeclub/OpenPype/pull/4201) +- Unreal: Support for Unreal Engine 5.1 [\#4199](https://github.com/pypeclub/OpenPype/pull/4199) +- General: Integrate thumbnail looks for thumbnail to multiple places [\#4181](https://github.com/pypeclub/OpenPype/pull/4181) +- Various minor bugfixes [\#4172](https://github.com/pypeclub/OpenPype/pull/4172) +- Nuke/Hiero: Remove tkinter library paths before launch [\#4171](https://github.com/pypeclub/OpenPype/pull/4171) +- Flame: vertical alignment of layers [\#4169](https://github.com/pypeclub/OpenPype/pull/4169) +- Nuke: correct detection of viewer and display [\#4165](https://github.com/pypeclub/OpenPype/pull/4165) +- Settings UI: Don't create QApplication if already exists [\#4156](https://github.com/pypeclub/OpenPype/pull/4156) +- General: Extract review handle start offset of sequences [\#4152](https://github.com/pypeclub/OpenPype/pull/4152) +- Maya: Maintain time connections on Alembic update. [\#4143](https://github.com/pypeclub/OpenPype/pull/4143) + +**🔀 Refactored code** + +- General: Use qtpy in modules and hosts UIs which are running in OpenPype process [\#4225](https://github.com/pypeclub/OpenPype/pull/4225) +- Tools: Use qtpy instead of Qt in standalone tools [\#4223](https://github.com/pypeclub/OpenPype/pull/4223) +- General: Use qtpy in settings UI [\#4215](https://github.com/pypeclub/OpenPype/pull/4215) + +**Merged pull requests:** + +- layout publish more than one container issue [\#4098](https://github.com/pypeclub/OpenPype/pull/4098) + ## [3.14.8](https://github.com/pypeclub/OpenPype/tree/3.14.8) [Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.14.7...3.14.8) diff --git a/openpype/hosts/blender/plugins/load/import_workfile.py b/openpype/hosts/blender/plugins/load/import_workfile.py new file mode 100644 index 0000000000..618fb83e31 --- /dev/null +++ b/openpype/hosts/blender/plugins/load/import_workfile.py @@ -0,0 +1,82 @@ +import bpy + +from openpype.hosts.blender.api import plugin + + +def append_workfile(context, fname, do_import): + asset = context['asset']['name'] + subset = context['subset']['name'] + + group_name = plugin.asset_name(asset, subset) + + # We need to preserve the original names of the scenes, otherwise, + # if there are duplicate names in the current workfile, the imported + # scenes will be renamed by Blender to avoid conflicts. + original_scene_names = [] + + with bpy.data.libraries.load(fname) as (data_from, data_to): + for attr in dir(data_to): + if attr == "scenes": + for scene in data_from.scenes: + original_scene_names.append(scene) + setattr(data_to, attr, getattr(data_from, attr)) + + current_scene = bpy.context.scene + + for scene, s_name in zip(data_to.scenes, original_scene_names): + scene.name = f"{group_name}_{s_name}" + if do_import: + collection = bpy.data.collections.new(f"{group_name}_{s_name}") + for obj in scene.objects: + collection.objects.link(obj) + current_scene.collection.children.link(collection) + for coll in scene.collection.children: + collection.children.link(coll) + + +class AppendBlendLoader(plugin.AssetLoader): + """Append workfile in Blender (unmanaged) + + Warning: + The loaded content will be unmanaged and is *not* visible in the + scene inventory. It's purely intended to merge content into your scene + so you could also use it as a new base. + """ + + representations = ["blend"] + families = ["*"] + + label = "Append Workfile" + order = 9 + icon = "arrow-circle-down" + color = "#775555" + + def load(self, context, name=None, namespace=None, data=None): + append_workfile(context, self.fname, False) + + # We do not containerize imported content, it remains unmanaged + return + + +class ImportBlendLoader(plugin.AssetLoader): + """Import workfile in the current Blender scene (unmanaged) + + Warning: + The loaded content will be unmanaged and is *not* visible in the + scene inventory. It's purely intended to merge content into your scene + so you could also use it as a new base. + """ + + representations = ["blend"] + families = ["*"] + + label = "Import Workfile" + order = 9 + icon = "arrow-circle-down" + color = "#775555" + + def load(self, context, name=None, namespace=None, data=None): + append_workfile(context, self.fname, True) + + # We do not containerize imported content, it remains unmanaged + return diff --git a/openpype/hosts/flame/api/menu.py b/openpype/hosts/flame/api/menu.py index 319ed7afb6..5f9dc57a61 100644 --- a/openpype/hosts/flame/api/menu.py +++ b/openpype/hosts/flame/api/menu.py @@ -1,5 +1,5 @@ import os -from Qt import QtWidgets +from qtpy import QtWidgets from copy import deepcopy from pprint import pformat from openpype.tools.utils.host_tools import HostToolsHelper diff --git a/openpype/hosts/flame/api/plugin.py b/openpype/hosts/flame/api/plugin.py index ca113fd98a..b1db612671 100644 --- a/openpype/hosts/flame/api/plugin.py +++ b/openpype/hosts/flame/api/plugin.py @@ -5,10 +5,10 @@ from copy import deepcopy from xml.etree import ElementTree as ET import qargparse -from Qt import QtCore, QtWidgets +from qtpy import QtCore, QtWidgets from openpype import style -from openpype.lib import Logger +from openpype.lib import Logger, StringTemplate from openpype.pipeline import LegacyCreator, LoaderPlugin from openpype.settings import get_current_project_settings @@ -775,6 +775,11 @@ class OpenClipSolver(flib.MediaInfoFile): self.feed_colorspace = feed_data.get("colorspace") self.log.debug("feed_version_name: {}".format(self.feed_version_name)) + # layer rename variables + self.layer_rename_template = feed_data["layer_rename_template"] + self.layer_rename_patterns = feed_data["layer_rename_patterns"] + self.context_data = feed_data["context_data"] + # derivate other feed variables self.feed_basename = os.path.basename(feed_path) self.feed_dir = os.path.dirname(feed_path) @@ -813,9 +818,11 @@ class OpenClipSolver(flib.MediaInfoFile): def _create_new_open_clip(self): self.log.info("Building new openClip") - self.log.debug(">> self.clip_data: {}".format(self.clip_data)) for tmp_xml_track in self.clip_data.iter("track"): + # solve track (layer) name + self._rename_track_name(tmp_xml_track) + tmp_xml_feeds = tmp_xml_track.find('feeds') tmp_xml_feeds.set('currentVersion', self.feed_version_name) @@ -850,6 +857,48 @@ class OpenClipSolver(flib.MediaInfoFile): if uid == track_uid: return xml_track + def _rename_track_name(self, xml_track_data): + layer_uid = xml_track_data.get("uid") + name_obj = xml_track_data.find("name") + layer_name = name_obj.text + + if ( + self.layer_rename_patterns + and not any( + re.search(lp_.lower(), layer_name.lower()) + for lp_ in self.layer_rename_patterns + ) + ): + return + + formating_data = self._update_formating_data( + layerName=layer_name, + layerUID=layer_uid + ) + name_obj.text = StringTemplate( + self.layer_rename_template + ).format(formating_data) + + def _update_formating_data(self, **kwargs): + """ Updating formating data for layer rename + + Attributes: + key=value (optional): will be included to formating data + as {key: value} + Returns: + dict: anatomy context data for formating + """ + self.log.debug(">> self.clip_data: {}".format(self.clip_data)) + clip_name_obj = self.clip_data.find("name") + data = { + "originalBasename": clip_name_obj.text + } + # include version context data + data.update(self.context_data) + # include input kwargs data + data.update(kwargs) + return data + def _update_open_clip(self): self.log.info("Updating openClip ..") @@ -857,11 +906,12 @@ class OpenClipSolver(flib.MediaInfoFile): out_xml = out_xml.getroot() self.log.debug(">> out_xml: {}".format(out_xml)) - self.log.debug(">> self.clip_data: {}".format(self.clip_data)) - # loop tmp tracks updated_any = False for tmp_xml_track in self.clip_data.iter("track"): + # solve track (layer) name + self._rename_track_name(tmp_xml_track) + # get tmp track uid tmp_track_uid = tmp_xml_track.get("uid") self.log.debug(">> tmp_track_uid: {}".format(tmp_track_uid)) diff --git a/openpype/hosts/flame/plugins/load/load_clip.py b/openpype/hosts/flame/plugins/load/load_clip.py index f8cb7b3e11..6f47c23d57 100644 --- a/openpype/hosts/flame/plugins/load/load_clip.py +++ b/openpype/hosts/flame/plugins/load/load_clip.py @@ -1,3 +1,4 @@ +from copy import deepcopy import os import flame from pprint import pformat @@ -25,6 +26,14 @@ class LoadClip(opfapi.ClipLoader): reel_name = "Loaded" clip_name_template = "{asset}_{subset}<_{output}>" + """ Anatomy keys from version context data and dynamically added: + - {layerName} - original layer name token + - {layerUID} - original layer UID token + - {originalBasename} - original clip name taken from file + """ + layer_rename_template = "{asset}_{subset}<_{output}>" + layer_rename_patterns = [] + def load(self, context, name, namespace, options): # get flame objects @@ -38,8 +47,16 @@ class LoadClip(opfapi.ClipLoader): version_name = version.get("name", None) colorspace = self.get_colorspace(context) + # in case output is not in context replace key to representation + if not context["representation"]["context"].get("output"): + self.clip_name_template = self.clip_name_template.replace( + "output", "representation") + self.layer_rename_template = self.layer_rename_template.replace( + "output", "representation") + + formating_data = deepcopy(context["representation"]["context"]) clip_name = StringTemplate(self.clip_name_template).format( - context["representation"]["context"]) + formating_data) # convert colorspace with ocio to flame mapping # in imageio flame section @@ -62,6 +79,9 @@ class LoadClip(opfapi.ClipLoader): "path": self.fname.replace("\\", "/"), "colorspace": colorspace, "version": "v{:0>3}".format(version_name), + "layer_rename_template": self.layer_rename_template, + "layer_rename_patterns": self.layer_rename_patterns, + "context_data": formating_data } self.log.debug(pformat( loading_context diff --git a/openpype/hosts/flame/plugins/load/load_clip_batch.py b/openpype/hosts/flame/plugins/load/load_clip_batch.py index 048ac19431..5975c6e42f 100644 --- a/openpype/hosts/flame/plugins/load/load_clip_batch.py +++ b/openpype/hosts/flame/plugins/load/load_clip_batch.py @@ -25,6 +25,14 @@ class LoadClipBatch(opfapi.ClipLoader): reel_name = "OP_LoadedReel" clip_name_template = "{batch}_{asset}_{subset}<_{output}>" + """ Anatomy keys from version context data and dynamically added: + - {layerName} - original layer name token + - {layerUID} - original layer UID token + - {originalBasename} - original clip name taken from file + """ + layer_rename_template = "{asset}_{subset}<_{output}>" + layer_rename_patterns = [] + def load(self, context, name, namespace, options): # get flame objects @@ -39,7 +47,10 @@ class LoadClipBatch(opfapi.ClipLoader): # in case output is not in context replace key to representation if not context["representation"]["context"].get("output"): - self.clip_name_template.replace("output", "representation") + self.clip_name_template = self.clip_name_template.replace( + "output", "representation") + self.layer_rename_template = self.layer_rename_template.replace( + "output", "representation") formating_data = deepcopy(context["representation"]["context"]) formating_data["batch"] = self.batch.name.get_value() @@ -69,6 +80,9 @@ class LoadClipBatch(opfapi.ClipLoader): "path": self.fname.replace("\\", "/"), "colorspace": colorspace, "version": "v{:0>3}".format(version_name), + "layer_rename_template": self.layer_rename_template, + "layer_rename_patterns": self.layer_rename_patterns, + "context_data": formating_data } self.log.debug(pformat( loading_context diff --git a/openpype/hosts/flame/startup/openpype_babypublisher/modules/panel_app.py b/openpype/hosts/flame/startup/openpype_babypublisher/modules/panel_app.py index 1e8011efaa..5c5bb0b4a1 100644 --- a/openpype/hosts/flame/startup/openpype_babypublisher/modules/panel_app.py +++ b/openpype/hosts/flame/startup/openpype_babypublisher/modules/panel_app.py @@ -1,4 +1,4 @@ -from Qt import QtWidgets, QtCore +from qtpy import QtWidgets, QtCore import uiwidgets import app_utils diff --git a/openpype/hosts/flame/startup/openpype_babypublisher/modules/uiwidgets.py b/openpype/hosts/flame/startup/openpype_babypublisher/modules/uiwidgets.py index c6db875df0..5498a49197 100644 --- a/openpype/hosts/flame/startup/openpype_babypublisher/modules/uiwidgets.py +++ b/openpype/hosts/flame/startup/openpype_babypublisher/modules/uiwidgets.py @@ -1,4 +1,4 @@ -from Qt import QtWidgets, QtCore +from qtpy import QtWidgets, QtCore class FlameLabel(QtWidgets.QLabel): diff --git a/openpype/hosts/flame/startup/openpype_in_flame.py b/openpype/hosts/flame/startup/openpype_in_flame.py index d07aaa6b7d..39869333aa 100644 --- a/openpype/hosts/flame/startup/openpype_in_flame.py +++ b/openpype/hosts/flame/startup/openpype_in_flame.py @@ -1,6 +1,6 @@ from __future__ import print_function import sys -from Qt import QtWidgets +from qtpy import QtWidgets from pprint import pformat import atexit diff --git a/openpype/hosts/hiero/api/launchforhiero.py b/openpype/hosts/hiero/api/launchforhiero.py index 5f7dbe23c9..c2186e1d2a 100644 --- a/openpype/hosts/hiero/api/launchforhiero.py +++ b/openpype/hosts/hiero/api/launchforhiero.py @@ -1,7 +1,7 @@ import logging from scriptsmenu import scriptsmenu -from Qt import QtWidgets +from qtpy import QtWidgets log = logging.getLogger(__name__) diff --git a/openpype/hosts/hiero/api/lib.py b/openpype/hosts/hiero/api/lib.py index 7f0cf8149a..bbd1edc14a 100644 --- a/openpype/hosts/hiero/api/lib.py +++ b/openpype/hosts/hiero/api/lib.py @@ -15,7 +15,11 @@ import secrets import shutil import hiero -from Qt import QtWidgets, QtCore, QtXml +from qtpy import QtWidgets, QtCore +try: + from PySide import QtXml +except ImportError: + from PySide2 import QtXml from openpype.client import get_project from openpype.settings import get_project_settings diff --git a/openpype/hosts/hiero/api/menu.py b/openpype/hosts/hiero/api/menu.py index 2a7560c6ba..6baeb38cc0 100644 --- a/openpype/hosts/hiero/api/menu.py +++ b/openpype/hosts/hiero/api/menu.py @@ -43,7 +43,7 @@ def menu_install(): """ - from Qt import QtGui + from qtpy import QtGui from . import ( publish, launch_workfiles_app, reload_config, apply_colorspace_project, apply_colorspace_clips diff --git a/openpype/hosts/hiero/api/plugin.py b/openpype/hosts/hiero/api/plugin.py index 5ec1c78aaa..38933a1e30 100644 --- a/openpype/hosts/hiero/api/plugin.py +++ b/openpype/hosts/hiero/api/plugin.py @@ -5,7 +5,7 @@ from copy import deepcopy import hiero -from Qt import QtWidgets, QtCore +from qtpy import QtWidgets, QtCore import qargparse from openpype.settings import get_current_project_settings diff --git a/openpype/hosts/hiero/plugins/publish/precollect_workfile.py b/openpype/hosts/hiero/plugins/publish/precollect_workfile.py index c9bfb86810..08963f98fd 100644 --- a/openpype/hosts/hiero/plugins/publish/precollect_workfile.py +++ b/openpype/hosts/hiero/plugins/publish/precollect_workfile.py @@ -3,7 +3,7 @@ import tempfile from pprint import pformat import pyblish.api -from Qt.QtGui import QPixmap +from qtpy.QtGui import QPixmap import hiero.ui diff --git a/openpype/hosts/nuke/api/lib.py b/openpype/hosts/nuke/api/lib.py index bde06e4fd7..a066bbcdcf 100644 --- a/openpype/hosts/nuke/api/lib.py +++ b/openpype/hosts/nuke/api/lib.py @@ -10,7 +10,7 @@ from collections import OrderedDict import clique import nuke -from Qt import QtCore, QtWidgets +from qtpy import QtCore, QtWidgets from openpype.client import ( get_project, @@ -81,7 +81,6 @@ class Context: def get_main_window(): """Acquire Nuke's main window""" if Context.main_window is None: - from Qt import QtWidgets top_widgets = QtWidgets.QApplication.topLevelWidgets() name = "Foundry::UI::DockMainWindow" @@ -611,7 +610,10 @@ def get_created_node_imageio_setting_legacy(nodeclass, creator, subset): if ( onode["subsets"] - and not any(re.search(s, subset) for s in onode["subsets"]) + and not any( + re.search(s.lower(), subset.lower()) + for s in onode["subsets"] + ) ): continue @@ -694,7 +696,8 @@ def get_imageio_node_override_setting( # find matching override node override_imageio_node = None for onode in override_nodes: - log.info(onode) + log.debug("__ onode: {}".format(onode)) + log.debug("__ subset: {}".format(subset)) if node_class not in onode["nukeNodeClass"]: continue @@ -703,7 +706,10 @@ def get_imageio_node_override_setting( if ( onode["subsets"] - and not any(re.search(s, subset) for s in onode["subsets"]) + and not any( + re.search(s.lower(), subset.lower()) + for s in onode["subsets"] + ) ): continue diff --git a/openpype/modules/base.py b/openpype/modules/base.py index 4761462df0..0fd21492e8 100644 --- a/openpype/modules/base.py +++ b/openpype/modules/base.py @@ -786,23 +786,15 @@ class ModulesManager: ).format(expected_keys, " | ".join(msg_items))) return output - def collect_creator_plugin_paths(self, host_name): - """Helper to collect creator plugin paths from modules. - - Args: - host_name (str): For which host are creators meants. - - Returns: - list: List of creator plugin paths. - """ - # Output structure + def _collect_plugin_paths(self, method_name, *args, **kwargs): output = [] for module in self.get_enabled_modules(): # Skip module that do not inherit from `IPluginPaths` if not isinstance(module, IPluginPaths): continue - paths = module.get_creator_plugin_paths(host_name) + method = getattr(module, method_name) + paths = method(*args, **kwargs) if paths: # Convert to list if value is not list if not isinstance(paths, (list, tuple, set)): @@ -810,6 +802,53 @@ class ModulesManager: output.extend(paths) return output + def collect_create_plugin_paths(self, host_name): + """Helper to collect creator plugin paths from modules. + + Args: + host_name (str): For which host are creators meant. + + Returns: + list: List of creator plugin paths. + """ + + return self._collect_plugin_paths( + "get_create_plugin_paths", + host_name + ) + + collect_creator_plugin_paths = collect_create_plugin_paths + + def collect_load_plugin_paths(self, host_name): + """Helper to collect load plugin paths from modules. + + Args: + host_name (str): For which host are load plugins meant. + + Returns: + list: List of load plugin paths. + """ + + return self._collect_plugin_paths( + "get_load_plugin_paths", + host_name + ) + + def collect_publish_plugin_paths(self, host_name): + """Helper to collect load plugin paths from modules. + + Args: + host_name (str): For which host are load plugins meant. + + Returns: + list: List of pyblish plugin paths. + """ + + return self._collect_plugin_paths( + "get_publish_plugin_paths", + host_name + ) + def get_host_module(self, host_name): """Find host module by host name. diff --git a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py index 3dd324f474..070d4eab18 100644 --- a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py @@ -188,7 +188,6 @@ class MayaSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline): # Adding file dependencies. if self.asset_dependencies: dependencies = instance.context.data["fileDependencies"] - dependencies.append(context.data["currentFile"]) for dependency in dependencies: job_info.AssetDependency += dependency @@ -299,7 +298,7 @@ class MayaSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline): # Add export job as dependency -------------------------------------- if export_job: job_info, _ = payload - job_info.JobDependency = export_job + job_info.JobDependencies = export_job if instance.data.get("tileRendering"): # Prepare tiles data @@ -436,7 +435,7 @@ class MayaSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline): frame_assembly_job_info.ExtraInfo[0] = file_hash frame_assembly_job_info.ExtraInfo[1] = file - frame_assembly_job_info.JobDependency = tile_job_id + frame_assembly_job_info.JobDependencies = tile_job_id # write assembly job config files now = datetime.now() diff --git a/openpype/modules/interfaces.py b/openpype/modules/interfaces.py index 7cd299df67..58b8b59931 100644 --- a/openpype/modules/interfaces.py +++ b/openpype/modules/interfaces.py @@ -24,7 +24,7 @@ class OpenPypeInterface: Child classes of OpenPypeInterface may be used as mixin in different OpenPype modules which means they have to have implemented methods defined - in the interface. By default interface does not have any abstract parts. + in the interface. By default, interface does not have any abstract parts. """ pass @@ -44,40 +44,78 @@ class IPluginPaths(OpenPypeInterface): def get_plugin_paths(self): pass - def get_creator_plugin_paths(self, host_name): - """Retreive creator plugin paths. + def _get_plugin_paths_by_type(self, plugin_type): + paths = self.get_plugin_paths() + if not paths or plugin_type not in paths: + return [] - Give addons ability to add creator plugin paths based on host name. + paths = paths[plugin_type] + if not paths: + return [] - NOTES: - - Default implementation uses 'get_plugin_paths' and always return - all creator plugins. - - Host name may help to organize plugins by host, but each creator - alsomay have host filtering. + if not isinstance(paths, (list, tuple, set)): + paths = [paths] + return paths + + def get_create_plugin_paths(self, host_name): + """Receive create plugin paths. + + Give addons ability to add create plugin paths based on host name. + + Notes: + Default implementation uses 'get_plugin_paths' and always return + all create plugin paths. Args: host_name (str): For which host are the plugins meant. """ - paths = self.get_plugin_paths() - if not paths or "create" not in paths: - return [] + if hasattr(self, "get_creator_plugin_paths"): + # TODO remove in 3.16 + self.log.warning(( + "DEPRECATION WARNING: Using method 'get_creator_plugin_paths'" + " which was renamed to 'get_create_plugin_paths'." + )) + return self.get_creator_plugin_paths(host_name) + return self._get_plugin_paths_by_type("create") - create_paths = paths["create"] - if not create_paths: - return [] + def get_load_plugin_paths(self, host_name): + """Receive load plugin paths. - if not isinstance(create_paths, (list, tuple, set)): - create_paths = [create_paths] - return create_paths + Give addons ability to add load plugin paths based on host name. + + Notes: + Default implementation uses 'get_plugin_paths' and always return + all load plugin paths. + + Args: + host_name (str): For which host are the plugins meant. + """ + + return self._get_plugin_paths_by_type("load") + + def get_publish_plugin_paths(self, host_name): + """Receive publish plugin paths. + + Give addons ability to add publish plugin paths based on host name. + + Notes: + Default implementation uses 'get_plugin_paths' and always return + all publish plugin paths. + + Args: + host_name (str): For which host are the plugins meant. + """ + + return self._get_plugin_paths_by_type("publish") class ILaunchHookPaths(OpenPypeInterface): """Module has launch hook paths to return. - Modules does not have to inherit from this interface (changed 8.11.2022). - Module just have to have implemented 'get_launch_hook_paths' to be able use - the advantage. + Modules don't have to inherit from this interface (changed 8.11.2022). + Module just have to have implemented 'get_launch_hook_paths' to be able to + use the advantage. Expected result is list of paths. ["path/to/launch_hooks_dir"] diff --git a/openpype/pipeline/context_tools.py b/openpype/pipeline/context_tools.py index 0ec19d50fe..da0ce8ecf4 100644 --- a/openpype/pipeline/context_tools.py +++ b/openpype/pipeline/context_tools.py @@ -158,17 +158,24 @@ def install_openpype_plugins(project_name=None, host_name=None): pyblish.api.register_discovery_filter(filter_pyblish_plugins) register_loader_plugin_path(LOAD_PATH) - modules_manager = _get_modules_manager() - publish_plugin_dirs = modules_manager.collect_plugin_paths()["publish"] - for path in publish_plugin_dirs: - pyblish.api.register_plugin_path(path) - if host_name is None: host_name = os.environ.get("AVALON_APP") - creator_paths = modules_manager.collect_creator_plugin_paths(host_name) - for creator_path in creator_paths: - register_creator_plugin_path(creator_path) + modules_manager = _get_modules_manager() + publish_plugin_dirs = modules_manager.collect_publish_plugin_paths( + host_name) + for path in publish_plugin_dirs: + pyblish.api.register_plugin_path(path) + + create_plugin_paths = modules_manager.collect_create_plugin_paths( + host_name) + for path in create_plugin_paths: + register_creator_plugin_path(path) + + load_plugin_paths = modules_manager.collect_load_plugin_paths( + host_name) + for path in load_plugin_paths: + register_loader_plugin_path(path) if project_name is None: project_name = os.environ.get("AVALON_PROJECT") diff --git a/openpype/settings/defaults/project_settings/flame.json b/openpype/settings/defaults/project_settings/flame.json index 34baf9ba06..1422a76af3 100644 --- a/openpype/settings/defaults/project_settings/flame.json +++ b/openpype/settings/defaults/project_settings/flame.json @@ -119,7 +119,12 @@ ], "reel_group_name": "OpenPype_Reels", "reel_name": "Loaded", - "clip_name_template": "{asset}_{subset}<_{output}>" + "clip_name_template": "{asset}_{subset}<_{output}>", + "layer_rename_template": "{asset}_{subset}<_{output}>", + "layer_rename_patterns": [ + "rgb", + "rgba" + ] }, "LoadClipBatch": { "enabled": true, @@ -142,7 +147,12 @@ "exr16fpdwaa" ], "reel_name": "OP_LoadedReel", - "clip_name_template": "{batch}_{asset}_{subset}<_{output}>" + "clip_name_template": "{batch}_{asset}_{subset}<_{output}>", + "layer_rename_template": "{asset}_{subset}<_{output}>", + "layer_rename_patterns": [ + "rgb", + "rgba" + ] } } } \ No newline at end of file diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json b/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json index 73664300aa..24726f2d07 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json @@ -512,6 +512,17 @@ "type": "text", "key": "clip_name_template", "label": "Clip name template" + }, + { + "type": "text", + "key": "layer_rename_template", + "label": "Layer name template" + }, + { + "type": "list", + "key": "layer_rename_patterns", + "label": "Layer rename patters", + "object_type": "text" } ] }, @@ -554,6 +565,17 @@ "type": "text", "key": "clip_name_template", "label": "Clip name template" + }, + { + "type": "text", + "key": "layer_rename_template", + "label": "Layer name template" + }, + { + "type": "list", + "key": "layer_rename_patterns", + "label": "Layer rename patters", + "object_type": "text" } ] } diff --git a/openpype/version.py b/openpype/version.py index 8d82df563d..454d56a5b7 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.14.9-nightly.4" +__version__ = "3.14.10-nightly.1"