From 22e4b9862b7aaf1e30b1d1d86fcccff2d2ec6799 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Feb 2023 19:05:27 +0100 Subject: [PATCH 01/12] added 'order' attribute to creators which is used to process them in specific order --- openpype/pipeline/create/creator_plugins.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/openpype/pipeline/create/creator_plugins.py b/openpype/pipeline/create/creator_plugins.py index 1f92056b23..1aba3f8770 100644 --- a/openpype/pipeline/create/creator_plugins.py +++ b/openpype/pipeline/create/creator_plugins.py @@ -107,7 +107,11 @@ class SubsetConvertorPlugin(object): @property def create_context(self): - """Quick access to create context.""" + """Quick access to create context. + + Returns: + CreateContext: Context which initialized the plugin. + """ return self._create_context @@ -157,6 +161,10 @@ class BaseCreator: # Cached group label after first call 'get_group_label' _cached_group_label = None + # Order in which will be plugin executed (collect & update instances) + # less == earlier -> Order '90' will be processed before '100' + order = 100 + # Variable to store logger _log = None From fd31d7815a69b51cfe7bc3ecc0d6a48f13c6e817 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Feb 2023 19:05:46 +0100 Subject: [PATCH 02/12] added helper methods to get sorted creators --- openpype/pipeline/create/context.py | 31 +++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index f260e483d9..04db1df790 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1237,6 +1237,37 @@ class CreateContext: """Access to global publish attributes.""" return self._publish_attributes + def get_sorted_creators(self, identifiers=None): + """Sorted creators by 'order' attribute. + + Returns: + List[BaseCreator]: Sorted creator plugins by 'order' value. + """ + + if identifiers is not None: + identifiers = set(identifiers) + creators = [ + creator + for identifier, creator in self.creators.items() + if identifier in identifiers + ] + else: + creators = self.creators.values() + + return sorted( + creators, key=lambda creator: creator.order + ) + + @property + def sorted_creators(self): + return self.get_sorted_creators() + + @property + def sorted_autocreators(self): + return sorted( + self.autocreators.values(), key=lambda creator: creator.order + ) + @classmethod def get_host_misssing_methods(cls, host): """Collect missing methods from host. From 101bbef42d0532dcca235e145cfdae7dc7d22436 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Feb 2023 19:07:16 +0100 Subject: [PATCH 03/12] added helper method to remove instance --- openpype/pipeline/create/context.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 04db1df790..9d8bb63804 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1630,6 +1630,9 @@ class CreateContext: ) ]) + def _remove_instance(self, instance): + self._instances_by_id.pop(instance.id, None) + def creator_removed_instance(self, instance): """When creator removes instance context should be acknowledged. @@ -1641,7 +1644,7 @@ class CreateContext: from scene metadata. """ - self._instances_by_id.pop(instance.id, None) + self._remove_instance(instance) def add_convertor_item(self, convertor_identifier, label): self.convertor_items_by_id[convertor_identifier] = ConvertorItem( From 3316417173b1f0bdd9a49f91cd210ca4f85236ef Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Feb 2023 19:07:50 +0100 Subject: [PATCH 04/12] process creators in their order --- openpype/pipeline/create/context.py | 56 ++++++++++++++++++++--------- 1 file changed, 40 insertions(+), 16 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 9d8bb63804..5682fb3115 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1688,7 +1688,7 @@ class CreateContext: # Collect instances error_message = "Collection of instances for creator {} failed. {}" failed_info = [] - for creator in self.creators.values(): + for creator in self.sorted_creators: label = creator.label identifier = creator.identifier failed = False @@ -1760,7 +1760,8 @@ class CreateContext: error_message = "Failed to run AutoCreator with identifier \"{}\". {}" failed_info = [] - for identifier, creator in self.autocreators.items(): + for creator in self.sorted_autocreators: + identifier = creator.identifier label = creator.label failed = False add_traceback = False @@ -1865,19 +1866,26 @@ class CreateContext: """Save instance specific values.""" instances_by_identifier = collections.defaultdict(list) for instance in self._instances_by_id.values(): + instance_changes = instance.changes() + if not instance_changes: + continue + identifier = instance.creator_identifier - instances_by_identifier[identifier].append(instance) + instances_by_identifier[identifier].append( + UpdateData(instance, instance_changes) + ) + + if not instances_by_identifier: + return error_message = "Instances update of creator \"{}\" failed. {}" failed_info = [] - for identifier, creator_instances in instances_by_identifier.items(): - update_list = [] - for instance in creator_instances: - instance_changes = instance.changes() - if instance_changes: - update_list.append(UpdateData(instance, instance_changes)) - creator = self.creators[identifier] + for creator in self.get_sorted_creators( + instances_by_identifier.keys() + ): + identifier = creator.identifier + update_list = instances_by_identifier[identifier] if not update_list: continue @@ -1913,9 +1921,13 @@ class CreateContext: def remove_instances(self, instances): """Remove instances from context. + All instances that don't have creator identifier leading to existing + creator are just removed from context. + Args: - instances(list): Instances that should be removed - from context. + instances(List[CreatedInstance]): Instances that should be removed. + Remove logic is done using creator, which may require to + do other cleanup than just remove instance from context. """ instances_by_identifier = collections.defaultdict(list) @@ -1925,8 +1937,13 @@ class CreateContext: error_message = "Instances removement of creator \"{}\" failed. {}" failed_info = [] - for identifier, creator_instances in instances_by_identifier.items(): - creator = self.creators.get(identifier) + # Remove instances by creator plugin order + for creator in self.get_sorted_creators( + instances_by_identifier.keys() + ): + identifier = creator.identifier + creator_instances = instances_by_identifier[identifier] + label = creator.label failed = False add_traceback = False @@ -1969,6 +1986,7 @@ class CreateContext: family(str): Instance family for which should be attribute definitions returned. """ + if family not in self._attr_plugins_by_family: import pyblish.logic @@ -1984,7 +2002,13 @@ class CreateContext: return self._attr_plugins_by_family[family] def _get_publish_plugins_with_attr_for_context(self): - """Publish plugins attributes for Context plugins.""" + """Publish plugins attributes for Context plugins. + + Returns: + List[pyblish.api.Plugin]: Publish plugins that have attribute + definitions for context. + """ + plugins = [] for plugin in self.plugins_with_defs: if not plugin.__instanceEnabled__: @@ -2009,7 +2033,7 @@ class CreateContext: return self._collection_shared_data def run_convertor(self, convertor_identifier): - """Run convertor plugin by it's idenfitifier. + """Run convertor plugin by identifier. Conversion is skipped if convertor is not available. From c5be74156652323a43f05e1d486ed77dfd0f0987 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Feb 2023 19:08:07 +0100 Subject: [PATCH 05/12] handle instances without available creator --- openpype/pipeline/create/context.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 5682fb3115..c978fcc2e1 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1935,6 +1935,12 @@ class CreateContext: identifier = instance.creator_identifier instances_by_identifier[identifier].append(instance) + # Just remove instances from context if creator is not available + missing_creators = set(instances_by_identifier) - set(self.creators) + for identifier in missing_creators: + for instance in instances_by_identifier[identifier]: + self._remove_instance(instance) + error_message = "Instances removement of creator \"{}\" failed. {}" failed_info = [] # Remove instances by creator plugin order @@ -2046,7 +2052,7 @@ class CreateContext: convertor.convert() def run_convertors(self, convertor_identifiers): - """Run convertor plugins by idenfitifiers. + """Run convertor plugins by identifiers. Conversion is skipped if convertor is not available. It is recommended to trigger reset after conversion to reload instances. From c7f051db20c45ab9ba8b835fea3f858051a93df3 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Feb 2023 19:08:42 +0100 Subject: [PATCH 06/12] added 'show_order' attribute to 'Creator' so show order can be different than processing --- openpype/pipeline/create/creator_plugins.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/openpype/pipeline/create/creator_plugins.py b/openpype/pipeline/create/creator_plugins.py index 1aba3f8770..53acb618ed 100644 --- a/openpype/pipeline/create/creator_plugins.py +++ b/openpype/pipeline/create/creator_plugins.py @@ -497,6 +497,17 @@ class Creator(BaseCreator): # - similar to instance attribute definitions pre_create_attr_defs = [] + @property + def show_order(self): + """Order in which is creator shown in UI. + + Returns: + int: Order in which is creator shown (less == earlier). By default + is using Creator's 'order' or processing. + """ + + return self.order + @abstractmethod def create(self, subset_name, instance_data, pre_create_data): """Create new instance and store it. From b67181c4e012ac370a1ca284e2112e92589772d9 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Feb 2023 19:09:32 +0100 Subject: [PATCH 07/12] added show order to publisher UI --- openpype/tools/publisher/constants.py | 2 ++ openpype/tools/publisher/control.py | 10 ++++++++-- openpype/tools/publisher/widgets/create_widget.py | 10 +++++++--- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/openpype/tools/publisher/constants.py b/openpype/tools/publisher/constants.py index e9fdd4774a..b2bfd7dd5c 100644 --- a/openpype/tools/publisher/constants.py +++ b/openpype/tools/publisher/constants.py @@ -24,6 +24,7 @@ CREATOR_THUMBNAIL_ENABLED_ROLE = QtCore.Qt.UserRole + 5 FAMILY_ROLE = QtCore.Qt.UserRole + 6 GROUP_ROLE = QtCore.Qt.UserRole + 7 CONVERTER_IDENTIFIER_ROLE = QtCore.Qt.UserRole + 8 +CREATOR_SORT_ROLE = QtCore.Qt.UserRole + 9 __all__ = ( @@ -36,6 +37,7 @@ __all__ = ( "IS_GROUP_ROLE", "CREATOR_IDENTIFIER_ROLE", "CREATOR_THUMBNAIL_ENABLED_ROLE", + "CREATOR_SORT_ROLE", "FAMILY_ROLE", "GROUP_ROLE", "CONVERTER_IDENTIFIER_ROLE", diff --git a/openpype/tools/publisher/control.py b/openpype/tools/publisher/control.py index 7c8da66744..d1ee3ea8aa 100644 --- a/openpype/tools/publisher/control.py +++ b/openpype/tools/publisher/control.py @@ -832,7 +832,8 @@ class CreatorItem: default_variants, create_allow_context_change, create_allow_thumbnail, - pre_create_attributes_defs + show_order, + pre_create_attributes_defs, ): self.identifier = identifier self.creator_type = creator_type @@ -846,6 +847,7 @@ class CreatorItem: self.default_variants = default_variants self.create_allow_context_change = create_allow_context_change self.create_allow_thumbnail = create_allow_thumbnail + self.show_order = show_order self.pre_create_attributes_defs = pre_create_attributes_defs def get_group_label(self): @@ -869,6 +871,7 @@ class CreatorItem: pre_create_attr_defs = None create_allow_context_change = None create_allow_thumbnail = None + show_order = creator.order if creator_type is CreatorTypes.artist: description = creator.get_description() detail_description = creator.get_detail_description() @@ -877,6 +880,7 @@ class CreatorItem: pre_create_attr_defs = creator.get_pre_create_attr_defs() create_allow_context_change = creator.create_allow_context_change create_allow_thumbnail = creator.create_allow_thumbnail + show_order = creator.show_order identifier = creator.identifier return cls( @@ -892,7 +896,8 @@ class CreatorItem: default_variants, create_allow_context_change, create_allow_thumbnail, - pre_create_attr_defs + show_order, + pre_create_attr_defs, ) def to_data(self): @@ -915,6 +920,7 @@ class CreatorItem: "default_variants": self.default_variants, "create_allow_context_change": self.create_allow_context_change, "create_allow_thumbnail": self.create_allow_thumbnail, + "show_order": self.show_order, "pre_create_attributes_defs": pre_create_attributes_defs, } diff --git a/openpype/tools/publisher/widgets/create_widget.py b/openpype/tools/publisher/widgets/create_widget.py index 07b124f616..994b9ac912 100644 --- a/openpype/tools/publisher/widgets/create_widget.py +++ b/openpype/tools/publisher/widgets/create_widget.py @@ -18,9 +18,10 @@ from .tasks_widget import CreateWidgetTasksWidget from .precreate_widget import PreCreateWidget from ..constants import ( VARIANT_TOOLTIP, - CREATOR_IDENTIFIER_ROLE, FAMILY_ROLE, + CREATOR_IDENTIFIER_ROLE, CREATOR_THUMBNAIL_ENABLED_ROLE, + CREATOR_SORT_ROLE, ) SEPARATORS = ("---separator---", "---") @@ -441,7 +442,8 @@ class CreateWidget(QtWidgets.QWidget): # Add new families new_creators = set() - for identifier, creator_item in self._controller.creator_items.items(): + creator_items_by_identifier = self._controller.creator_items + for identifier, creator_item in creator_items_by_identifier.items(): if creator_item.creator_type != "artist": continue @@ -457,6 +459,7 @@ class CreateWidget(QtWidgets.QWidget): self._creators_model.appendRow(item) item.setData(creator_item.label, QtCore.Qt.DisplayRole) + item.setData(creator_item.show_order, CREATOR_SORT_ROLE) item.setData(identifier, CREATOR_IDENTIFIER_ROLE) item.setData( creator_item.create_allow_thumbnail, @@ -482,8 +485,9 @@ class CreateWidget(QtWidgets.QWidget): index = indexes[0] identifier = index.data(CREATOR_IDENTIFIER_ROLE) + create_item = creator_items_by_identifier.get(identifier) - self._set_creator_by_identifier(identifier) + self._set_creator(create_item) def _on_plugins_refresh(self): # Trigger refresh only if is visible From 7e1450e95d1cbc2d3df206235a13f280c3c642cb Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Feb 2023 19:09:53 +0100 Subject: [PATCH 08/12] modified proxy filter for sorting of creators --- openpype/tools/publisher/widgets/create_widget.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/openpype/tools/publisher/widgets/create_widget.py b/openpype/tools/publisher/widgets/create_widget.py index 994b9ac912..bc1f1ec4f7 100644 --- a/openpype/tools/publisher/widgets/create_widget.py +++ b/openpype/tools/publisher/widgets/create_widget.py @@ -91,6 +91,15 @@ class CreatorShortDescWidget(QtWidgets.QWidget): self._description_label.setText(description) +class CreatorsProxyModel(QtCore.QSortFilterProxyModel): + def lessThan(self, left, right): + l_show_order = left.data(CREATOR_SORT_ROLE) + r_show_order = right.data(CREATOR_SORT_ROLE) + if l_show_order == r_show_order: + return super(CreatorsProxyModel, self).lessThan(left, right) + return l_show_order < r_show_order + + class CreateWidget(QtWidgets.QWidget): def __init__(self, controller, parent=None): super(CreateWidget, self).__init__(parent) @@ -142,7 +151,7 @@ class CreateWidget(QtWidgets.QWidget): creators_view = QtWidgets.QListView(creators_view_widget) creators_model = QtGui.QStandardItemModel() - creators_sort_model = QtCore.QSortFilterProxyModel() + creators_sort_model = CreatorsProxyModel() creators_sort_model.setSourceModel(creators_model) creators_view.setModel(creators_sort_model) From dfb718741355d5b0c4cad8a9a4947d2dce2eb606 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Feb 2023 19:10:07 +0100 Subject: [PATCH 09/12] removed window title from creator widget --- openpype/tools/publisher/widgets/create_widget.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/openpype/tools/publisher/widgets/create_widget.py b/openpype/tools/publisher/widgets/create_widget.py index bc1f1ec4f7..dbf075c216 100644 --- a/openpype/tools/publisher/widgets/create_widget.py +++ b/openpype/tools/publisher/widgets/create_widget.py @@ -104,8 +104,6 @@ class CreateWidget(QtWidgets.QWidget): def __init__(self, controller, parent=None): super(CreateWidget, self).__init__(parent) - self.setWindowTitle("Create new instance") - self._controller = controller self._asset_name = None From 310456ec137c621554b91d2a831f875489059106 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Feb 2023 19:10:24 +0100 Subject: [PATCH 10/12] fix creator_items cache cleanup --- openpype/tools/publisher/control.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/openpype/tools/publisher/control.py b/openpype/tools/publisher/control.py index d1ee3ea8aa..435db5fcb3 100644 --- a/openpype/tools/publisher/control.py +++ b/openpype/tools/publisher/control.py @@ -1508,9 +1508,6 @@ class BasePublisherController(AbstractPublisherController): def _reset_attributes(self): """Reset most of attributes that can be reset.""" - # Reset creator items - self._creator_items = None - self.publish_is_running = False self.publish_has_validated = False self.publish_has_crashed = False @@ -1766,6 +1763,8 @@ class PublisherController(BasePublisherController): self._resetting_plugins = True self._create_context.reset_plugins() + # Reset creator items + self._creator_items = None self._resetting_plugins = False From cd1b02c59504dab76294663b326851cf70920d7e Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Feb 2023 19:14:53 +0100 Subject: [PATCH 11/12] move batch creator after simple creators --- .../hosts/traypublisher/plugins/create/create_movie_batch.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/hosts/traypublisher/plugins/create/create_movie_batch.py b/openpype/hosts/traypublisher/plugins/create/create_movie_batch.py index 1dc4bad9b3..d077131e4c 100644 --- a/openpype/hosts/traypublisher/plugins/create/create_movie_batch.py +++ b/openpype/hosts/traypublisher/plugins/create/create_movie_batch.py @@ -33,6 +33,8 @@ class BatchMovieCreator(TrayPublishCreator): create_allow_context_change = False version_regex = re.compile(r"^(.+)_v([0-9]+)$") + # Position batch creator after simple creators + order = 110 def __init__(self, project_settings, *args, **kwargs): super(BatchMovieCreator, self).__init__(project_settings, From b51a2a72579966e721e268de68ad1d01e62c93a6 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Feb 2023 19:24:30 +0100 Subject: [PATCH 12/12] added more docstrings --- openpype/pipeline/create/context.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index c978fcc2e1..0ee70f39cb 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1240,6 +1240,10 @@ class CreateContext: def get_sorted_creators(self, identifiers=None): """Sorted creators by 'order' attribute. + Args: + identifiers (Iterable[str]): Filter creators by identifiers. All + creators are returned if 'None' is passed. + Returns: List[BaseCreator]: Sorted creator plugins by 'order' value. """ @@ -1260,10 +1264,22 @@ class CreateContext: @property def sorted_creators(self): + """Sorted creators by 'order' attribute. + + Returns: + List[BaseCreator]: Sorted creator plugins by 'order' value. + """ + return self.get_sorted_creators() @property def sorted_autocreators(self): + """Sorted auto-creators by 'order' attribute. + + Returns: + List[AutoCreator]: Sorted plugins by 'order' value. + """ + return sorted( self.autocreators.values(), key=lambda creator: creator.order )