From 449549b72bb76142fd669f3da9dd197e87d4bad8 Mon Sep 17 00:00:00 2001 From: "Sveinbjorn J. Tryggvason" Date: Tue, 1 Jul 2025 11:11:18 +0000 Subject: [PATCH 01/20] allow merging of file sequence entries --- .../tools/attribute_defs/files_widget.py | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/client/ayon_core/tools/attribute_defs/files_widget.py b/client/ayon_core/tools/attribute_defs/files_widget.py index 8a40b3ff38..edac8059f0 100644 --- a/client/ayon_core/tools/attribute_defs/files_widget.py +++ b/client/ayon_core/tools/attribute_defs/files_widget.py @@ -892,6 +892,28 @@ class FilesWidget(QtWidgets.QFrame): self._add_filepaths(new_items) self._remove_item_by_ids(item_ids) + def _on_merge_request(self): + if self._multivalue: + return + + item_ids = self._files_view.get_selected_item_ids() + if not item_ids: + return + + all_paths = [] + for item_id in item_ids: + file_item = self._files_model.get_file_item_by_id(item_id) + if not file_item: + return + + new_items = file_item.split_sequence() + for nitem in new_items: + all_paths.append(os.path.join(nitem.directory, nitem.filenames[0])) + unique_all_pahts = list(set(all_paths)) + self._remove_item_by_ids(item_ids) + new_items = FileDefItem.from_value(unique_all_pahts, True) + self._add_filepaths(new_items) + def _on_remove_requested(self): if self._multivalue: return @@ -911,6 +933,9 @@ class FilesWidget(QtWidgets.QFrame): split_action.triggered.connect(self._on_split_request) menu.addAction(split_action) + merge_action = QtWidgets.QAction("Merge sequence", menu) + merge_action.triggered.connect(self._on_merge_request) + menu.addAction(merge_action) remove_action = QtWidgets.QAction("Remove", menu) remove_action.triggered.connect(self._on_remove_requested) menu.addAction(remove_action) From 1b2d3606191fc7f294c113c35b90c3ebdf852f3f Mon Sep 17 00:00:00 2001 From: sjt-rvx <72554834+sjt-rvx@users.noreply.github.com> Date: Wed, 2 Jul 2025 10:22:35 +0000 Subject: [PATCH 02/20] Update client/ayon_core/tools/attribute_defs/files_widget.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- .../tools/attribute_defs/files_widget.py | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/client/ayon_core/tools/attribute_defs/files_widget.py b/client/ayon_core/tools/attribute_defs/files_widget.py index edac8059f0..f1b0f06dbc 100644 --- a/client/ayon_core/tools/attribute_defs/files_widget.py +++ b/client/ayon_core/tools/attribute_defs/files_widget.py @@ -900,18 +900,20 @@ class FilesWidget(QtWidgets.QFrame): if not item_ids: return - all_paths = [] + all_paths = set() + merged_item_ids = set() for item_id in item_ids: file_item = self._files_model.get_file_item_by_id(item_id) - if not file_item: - return - - new_items = file_item.split_sequence() - for nitem in new_items: - all_paths.append(os.path.join(nitem.directory, nitem.filenames[0])) - unique_all_pahts = list(set(all_paths)) - self._remove_item_by_ids(item_ids) - new_items = FileDefItem.from_value(unique_all_pahts, True) + if file_item is None: + continue + merged_item_ids.add(item_id) + all_paths |= { + os.path.join(file_item.directory, filename) + for filename in file_item.filenames + } + + self._remove_item_by_ids(merged_item_ids) + new_items = FileDefItem.from_value(list(all_paths), True) self._add_filepaths(new_items) def _on_remove_requested(self): From b3d87eac82a84305ee788f357fd73cac20abcc53 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 2 Jul 2025 16:59:13 +0200 Subject: [PATCH 03/20] added 'is_mandatory' to instance --- .../ayon_core/pipeline/create/structures.py | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/client/ayon_core/pipeline/create/structures.py b/client/ayon_core/pipeline/create/structures.py index d7ba6b9c24..1bc20501cc 100644 --- a/client/ayon_core/pipeline/create/structures.py +++ b/client/ayon_core/pipeline/create/structures.py @@ -507,6 +507,7 @@ class CreatedInstance: if transient_data is None: transient_data = {} self._transient_data = transient_data + self._is_mandatory = False # Create a copy of passed data to avoid changing them on the fly data = copy.deepcopy(data or {}) @@ -605,6 +606,12 @@ class CreatedInstance: if key in self._data and self._data[key] == value: return + if self.is_mandatory and key == "active" and value is not True: + raise ImmutableKeyError( + key, + "Instance is mandatory and can't be disabled." + ) + self._data[key] = value self._create_context.instance_values_changed( self.id, {key: value} @@ -718,6 +725,33 @@ class CreatedInstance: return self._transient_data + @property + def is_mandatory(self) -> bool: + """Check if instance is mandatory. + + Returns: + bool: True if instance is mandatory, False otherwise. + + """ + return self._is_mandatory + + def set_mandatory(self, value: bool) -> None: + """Set instance as mandatory or not. + + Mandatory instance can't be disabled in UI. + + Args: + value (bool): True if instance should be mandatory, False + otherwise. + + """ + if value is self._is_mandatory: + return + self._is_mandatory = value + if value is True: + self["active"] = True + self._create_context.instance_state_changed(self.id) + def changes(self): """Calculate and return changes.""" From 541c7c328a5fa2bfee59bcc7c97205925b3c9e35 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 2 Jul 2025 16:59:24 +0200 Subject: [PATCH 04/20] capture state changes of instance --- client/ayon_core/pipeline/create/context.py | 75 +++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/client/ayon_core/pipeline/create/context.py b/client/ayon_core/pipeline/create/context.py index 05531afd05..14b13613b6 100644 --- a/client/ayon_core/pipeline/create/context.py +++ b/client/ayon_core/pipeline/create/context.py @@ -79,6 +79,7 @@ _NOT_SET = object() INSTANCE_ADDED_TOPIC = "instances.added" INSTANCE_REMOVED_TOPIC = "instances.removed" VALUE_CHANGED_TOPIC = "values.changed" +INSTANCE_STATE_CHANGED_TOPIC = "instance.stated.changed" PRE_CREATE_ATTR_DEFS_CHANGED_TOPIC = "pre.create.attr.defs.changed" CREATE_ATTR_DEFS_CHANGED_TOPIC = "create.attr.defs.changed" PUBLISH_ATTR_DEFS_CHANGED_TOPIC = "publish.attr.defs.changed" @@ -257,6 +258,10 @@ class CreateContext: "create_attrs_change": BulkInfo(), # Publish attribute definitions changed "publish_attrs_change": BulkInfo(), + # Instance state changed + # - right now used only for 'mandatory' state but can be extended + # in future + "state_change": BulkInfo(), } self._bulk_order = [] @@ -1049,6 +1054,35 @@ class CreateContext: PUBLISH_ATTR_DEFS_CHANGED_TOPIC, callback ) + def add_instance_state_change_callback( + self, callback: Callable + ) -> "EventCallback": + """Register callback to listen instance state changes. + + Create plugin changed attribute definitions of instance. + + Data structure of event:: + + ```python + { + "instances": [CreatedInstance, ...], + "create_context": CreateContext + } + ``` + + Args: + callback (Callable): Callback function that will be called when + create attributes changed. + + Returns: + EventCallback: Created callback object which can be used to + stop listening. + + """ + return self._event_hub.add_callback( + INSTANCE_STATE_CHANGED_TOPIC, callback + ) + def context_data_to_store(self) -> dict[str, Any]: """Data that should be stored by host function. @@ -1323,6 +1357,13 @@ class CreateContext: ) as bulk_info: yield bulk_info + @contextmanager + def bulk_instance_state_change(self, sender: Optional[str] = None): + with self._bulk_context( + "state_change", sender + ) as bulk_info: + yield bulk_info + @contextmanager def bulk_publish_attr_defs_change(self, sender: Optional[str] = None): with self._bulk_context("publish_attrs_change", sender) as bulk_info: @@ -1390,6 +1431,19 @@ class CreateContext: with self.bulk_value_changes() as bulk_item: bulk_item.append((instance_id, new_values)) + def instance_state_changed(self, instance_id: str) -> None: + """Instance state changed. + + Triggered by `CreatedInstance`. + + Args: + instance_id (Optional[str]): Instance id. + + """ + if self._is_instance_events_ready(instance_id): + with self.bulk_instance_state_change() as bulk_item: + bulk_item.append(instance_id) + # --- context change callbacks --- def publish_attribute_value_changed( self, plugin_name: str, value: dict[str, Any] @@ -2249,6 +2303,8 @@ class CreateContext: self._bulk_create_attrs_change_finished(data, sender) elif key == "publish_attrs_change": self._bulk_publish_attrs_change_finished(data, sender) + elif key == "state_change": + self._bulk_instance_state_change_finished(data, sender) def _bulk_add_instances_finished( self, @@ -2443,3 +2499,22 @@ class CreateContext: {"instance_changes": instance_changes}, sender, ) + + def _bulk_instance_state_change_finished( + self, + instance_ids: list[str], + sender: Optional[str], + ) -> None: + if not instance_ids: + return + + instances = [ + self.get_instance_by_id(instance_id) + for instance_id in set(instance_ids) + ] + + self._emit_event( + INSTANCE_STATE_CHANGED_TOPIC, + {"instances": instances}, + sender, + ) From 9f69202538071dfe2770e9d07c142deb66986f7e Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 2 Jul 2025 16:59:40 +0200 Subject: [PATCH 05/20] handle mandatory property in publisher --- client/ayon_core/tools/publisher/control.py | 1 + .../tools/publisher/models/create.py | 20 +++++++++++++++++++ .../publisher/widgets/card_view_widgets.py | 4 ++++ .../publisher/widgets/list_view_widgets.py | 5 +++++ .../publisher/widgets/overview_widget.py | 12 ++++++++++- 5 files changed, 41 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/tools/publisher/control.py b/client/ayon_core/tools/publisher/control.py index ef2e122692..b551b21bd4 100644 --- a/client/ayon_core/tools/publisher/control.py +++ b/client/ayon_core/tools/publisher/control.py @@ -53,6 +53,7 @@ class PublisherController( changed. "create.context.create.attrs.changed" - Create attributes changed. "create.context.publish.attrs.changed" - Publish attributes changed. + "create.context.instance.state.changed" - Instance state changed. "create.context.removed.instance" - Instance removed from context. "create.model.instances.context.changed" - Instances changed context. like folder, task or variant. diff --git a/client/ayon_core/tools/publisher/models/create.py b/client/ayon_core/tools/publisher/models/create.py index 900168eaef..7f44b374e6 100644 --- a/client/ayon_core/tools/publisher/models/create.py +++ b/client/ayon_core/tools/publisher/models/create.py @@ -217,6 +217,7 @@ class InstanceItem: folder_path: Optional[str], task_name: Optional[str], is_active: bool, + is_mandatory: bool, has_promised_context: bool, ): self._instance_id: str = instance_id @@ -229,6 +230,7 @@ class InstanceItem: self._folder_path: Optional[str] = folder_path self._task_name: Optional[str] = task_name self._is_active: bool = is_active + self._is_mandatory: bool = is_mandatory self._has_promised_context: bool = has_promised_context @property @@ -251,6 +253,10 @@ class InstanceItem: def product_type(self): return self._product_type + @property + def is_mandatory(self): + return self._is_mandatory + @property def has_promised_context(self): return self._has_promised_context @@ -304,6 +310,7 @@ class InstanceItem: instance["folderPath"], instance["task"], instance["active"], + instance.is_mandatory, instance.has_promised_context, ) @@ -476,6 +483,9 @@ class CreateModel: self._create_context.add_publish_attr_defs_change_callback( self._cc_publish_attr_changed ) + self._create_context.add_instance_state_change_callback( + self._cc_instance_state_changed + ) self._create_context.reset_finalization() @@ -1171,6 +1181,16 @@ class CreateModel: event_data, ) + def _cc_instance_state_changed(self, event): + instance_ids = { + instance.id + for instance in event.data["instances"] + } + self._emit_event( + "create.context.instance.state.changed", + {"instance_ids": instance_ids}, + ) + def _get_allowed_creators_pattern(self) -> Union[Pattern, None]: """Provide regex pattern for configured creator labels in this context diff --git a/client/ayon_core/tools/publisher/widgets/card_view_widgets.py b/client/ayon_core/tools/publisher/widgets/card_view_widgets.py index 2f633b3149..8a4eddf058 100644 --- a/client/ayon_core/tools/publisher/widgets/card_view_widgets.py +++ b/client/ayon_core/tools/publisher/widgets/card_view_widgets.py @@ -482,6 +482,9 @@ class InstanceCardWidget(CardWidget): if checkbox_value != new_value: self._active_checkbox.setChecked(new_value) + def _set_is_mandatory(self, is_mandatory: bool) -> None: + self._active_checkbox.setVisible(not is_mandatory) + def update_instance(self, instance, context_info): """Update instance object and update UI.""" self.instance = instance @@ -525,6 +528,7 @@ class InstanceCardWidget(CardWidget): """Update instance data""" self._update_product_name() self._set_active(self.instance.is_active) + self._set_is_mandatory(self.instance.is_mandatory) self._validate_context(context_info) def _set_expanded(self, expanded=None): diff --git a/client/ayon_core/tools/publisher/widgets/list_view_widgets.py b/client/ayon_core/tools/publisher/widgets/list_view_widgets.py index bc3353ba5e..8d9f509b0e 100644 --- a/client/ayon_core/tools/publisher/widgets/list_view_widgets.py +++ b/client/ayon_core/tools/publisher/widgets/list_view_widgets.py @@ -132,6 +132,7 @@ class InstanceListItemWidget(QtWidgets.QWidget): active_checkbox = NiceCheckbox(parent=self) active_checkbox.setChecked(instance.is_active) + active_checkbox.setVisible(not instance.is_mandatory) layout = QtWidgets.QHBoxLayout(self) content_margins = layout.contentsMargins() @@ -192,6 +193,7 @@ class InstanceListItemWidget(QtWidgets.QWidget): self._instance_label_widget.setText(html_escape(label)) # Check active state self.set_active(instance.is_active) + self._set_is_mandatory(instance.is_mandatory) # Check valid states self._set_valid_property(context_info.is_valid) @@ -203,6 +205,9 @@ class InstanceListItemWidget(QtWidgets.QWidget): def set_active_toggle_enabled(self, enabled): self._active_checkbox.setEnabled(enabled) + def _set_is_mandatory(self, is_mandatory: bool) -> None: + self._active_checkbox.setVisible(not is_mandatory) + class ListContextWidget(QtWidgets.QFrame): """Context (or global attributes) widget.""" diff --git a/client/ayon_core/tools/publisher/widgets/overview_widget.py b/client/ayon_core/tools/publisher/widgets/overview_widget.py index c6c3b774f0..44bb09d4fc 100644 --- a/client/ayon_core/tools/publisher/widgets/overview_widget.py +++ b/client/ayon_core/tools/publisher/widgets/overview_widget.py @@ -155,6 +155,10 @@ class OverviewWidget(QtWidgets.QFrame): "create.model.instances.context.changed", self._on_instance_context_change ) + controller.register_event_callback( + "create.model.instance.state.changed", + self._on_instance_state_changed + ) self._product_content_widget = product_content_widget self._product_content_layout = product_content_layout @@ -352,6 +356,12 @@ class OverviewWidget(QtWidgets.QFrame): ) def _on_instance_context_change(self, event): + self._refresh_instance_states(event["instance_ids"]) + + def _on_instance_state_changed(self, event): + self._refresh_instance_states(event["instance_ids"]) + + def _refresh_instance_states(self, instance_ids): current_idx = self._product_views_layout.currentIndex() for idx in range(self._product_views_layout.count()): if idx == current_idx: @@ -361,7 +371,7 @@ class OverviewWidget(QtWidgets.QFrame): widget.set_refreshed(False) current_widget = self._product_views_layout.widget(current_idx) - current_widget.refresh_instance_states(event["instance_ids"]) + current_widget.refresh_instance_states(instance_ids) def _on_convert_requested(self): self.convert_requested.emit() From 39d011b89f63f0268367ab86fbbf19d92ab6f17b Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 3 Jul 2025 10:20:03 +0200 Subject: [PATCH 06/20] don't change active state of mandatory instances --- .../publisher/widgets/list_view_widgets.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/tools/publisher/widgets/list_view_widgets.py b/client/ayon_core/tools/publisher/widgets/list_view_widgets.py index 8d9f509b0e..969bec11e5 100644 --- a/client/ayon_core/tools/publisher/widgets/list_view_widgets.py +++ b/client/ayon_core/tools/publisher/widgets/list_view_widgets.py @@ -152,6 +152,8 @@ class InstanceListItemWidget(QtWidgets.QWidget): self._has_valid_context = None + self._checkbox_enabled = not instance.is_mandatory + self._set_valid_property(context_info.is_valid) def mouseDoubleClickEvent(self, event): @@ -185,6 +187,10 @@ class InstanceListItemWidget(QtWidgets.QWidget): self._active_checkbox.setChecked(new_value) self._active_checkbox.blockSignals(False) + def is_checkbox_enabled(self) -> bool: + """Checkbox can be changed by user.""" + return self._checkbox_enabled + def update_instance(self, instance, context_info): """Update instance object.""" # Check product name @@ -206,6 +212,7 @@ class InstanceListItemWidget(QtWidgets.QWidget): self._active_checkbox.setEnabled(enabled) def _set_is_mandatory(self, is_mandatory: bool) -> None: + self._checkbox_enabled = not is_mandatory self._active_checkbox.setVisible(not is_mandatory) @@ -954,11 +961,17 @@ class InstanceListView(AbstractInstanceView): return active_by_id = {} + all_changed = True for row in range(group_item.rowCount()): item = group_item.child(row) instance_id = item.data(INSTANCE_ID_ROLE) - if instance_id is not None: + widget = self._widgets_by_id.get(instance_id) + if widget is None: + continue + if widget.is_checkbox_enabled(): active_by_id[instance_id] = active + else: + all_changed = False self._controller.set_instances_active_state(active_by_id) @@ -968,6 +981,10 @@ class InstanceListView(AbstractInstanceView): if not self._instance_view.isExpanded(proxy_index): self._instance_view.expand(proxy_index) + if not all_changed: + # If not all instances were changed, update group checkstate + self._update_group_checkstate(group_name) + def has_items(self): if self._convertor_group_widget is not None: return True From b1e0a925059eb5c077a35c60e663bb61bd58a27d Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 3 Jul 2025 13:05:31 +0200 Subject: [PATCH 07/20] define logging config for AYON processes --- client/ayon_core/cli.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/client/ayon_core/cli.py b/client/ayon_core/cli.py index 2340696ad9..5978e8ba19 100644 --- a/client/ayon_core/cli.py +++ b/client/ayon_core/cli.py @@ -2,6 +2,7 @@ """Package for handling AYON command line arguments.""" import os import sys +import logging import code import traceback from pathlib import Path @@ -23,6 +24,9 @@ from ayon_core.lib.env_tools import ( merge_env_variables, ) +logging.basicConfig() +log = logging.getLogger() + @click.group(invoke_without_command=True) @click.pass_context From f2b67e3d79497b6f2c9b26fc33edd353a784235d Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 4 Jul 2025 23:28:41 +0200 Subject: [PATCH 08/20] Add "blender" to hosts list for Validate File Saved --- client/ayon_core/plugins/publish/validate_file_saved.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/validate_file_saved.py b/client/ayon_core/plugins/publish/validate_file_saved.py index 7656687b4f..f8fdd27342 100644 --- a/client/ayon_core/plugins/publish/validate_file_saved.py +++ b/client/ayon_core/plugins/publish/validate_file_saved.py @@ -37,7 +37,7 @@ class ValidateCurrentSaveFile(pyblish.api.ContextPlugin): label = "Validate File Saved" order = pyblish.api.ValidatorOrder - 0.1 hosts = ["fusion", "houdini", "max", "maya", "nuke", "substancepainter", - "cinema4d", "silhouette", "gaffer"] + "cinema4d", "silhouette", "gaffer", "blender"] actions = [SaveByVersionUpAction, ShowWorkfilesAction] def process(self, context): From 54ecd2b834ec773b75e600beb13445df5fa7799e Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 7 Jul 2025 12:40:34 +0200 Subject: [PATCH 09/20] move basicconfig to main --- client/ayon_core/cli.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/cli.py b/client/ayon_core/cli.py index 5978e8ba19..ca3dcc86ee 100644 --- a/client/ayon_core/cli.py +++ b/client/ayon_core/cli.py @@ -24,9 +24,6 @@ from ayon_core.lib.env_tools import ( merge_env_variables, ) -logging.basicConfig() -log = logging.getLogger() - @click.group(invoke_without_command=True) @click.pass_context @@ -310,6 +307,8 @@ def _add_addons(addons_manager): def main(*args, **kwargs): + logging.basicConfig() + initialize_ayon_connection() python_path = os.getenv("PYTHONPATH", "") split_paths = python_path.split(os.pathsep) From c54b7c25178e0c8910f16d7a94a7f78d17c33059 Mon Sep 17 00:00:00 2001 From: sjt-rvx <72554834+sjt-rvx@users.noreply.github.com> Date: Mon, 7 Jul 2025 10:58:18 +0000 Subject: [PATCH 10/20] Update client/ayon_core/tools/attribute_defs/files_widget.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/tools/attribute_defs/files_widget.py | 1 - 1 file changed, 1 deletion(-) diff --git a/client/ayon_core/tools/attribute_defs/files_widget.py b/client/ayon_core/tools/attribute_defs/files_widget.py index f1b0f06dbc..4c55ae5620 100644 --- a/client/ayon_core/tools/attribute_defs/files_widget.py +++ b/client/ayon_core/tools/attribute_defs/files_widget.py @@ -911,7 +911,6 @@ class FilesWidget(QtWidgets.QFrame): os.path.join(file_item.directory, filename) for filename in file_item.filenames } - self._remove_item_by_ids(merged_item_ids) new_items = FileDefItem.from_value(list(all_paths), True) self._add_filepaths(new_items) From df9d58c384058241d54adde81780d7358907d1fa Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 8 Jul 2025 15:23:56 +0200 Subject: [PATCH 11/20] fix typo --- client/ayon_core/pipeline/create/context.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/create/context.py b/client/ayon_core/pipeline/create/context.py index 14b13613b6..e9f259e53b 100644 --- a/client/ayon_core/pipeline/create/context.py +++ b/client/ayon_core/pipeline/create/context.py @@ -79,7 +79,7 @@ _NOT_SET = object() INSTANCE_ADDED_TOPIC = "instances.added" INSTANCE_REMOVED_TOPIC = "instances.removed" VALUE_CHANGED_TOPIC = "values.changed" -INSTANCE_STATE_CHANGED_TOPIC = "instance.stated.changed" +INSTANCE_STATE_CHANGED_TOPIC = "instance.state.changed" PRE_CREATE_ATTR_DEFS_CHANGED_TOPIC = "pre.create.attr.defs.changed" CREATE_ATTR_DEFS_CHANGED_TOPIC = "create.attr.defs.changed" PUBLISH_ATTR_DEFS_CHANGED_TOPIC = "publish.attr.defs.changed" From 031ccebb45a548d0e54a936d9ab8f3770c5dd226 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 8 Jul 2025 17:39:47 +0200 Subject: [PATCH 12/20] use requirement instead of state --- client/ayon_core/pipeline/create/context.py | 34 +++++++++---------- .../ayon_core/pipeline/create/structures.py | 2 +- client/ayon_core/tools/publisher/control.py | 3 +- .../tools/publisher/models/create.py | 8 ++--- .../publisher/widgets/overview_widget.py | 2 +- 5 files changed, 25 insertions(+), 24 deletions(-) diff --git a/client/ayon_core/pipeline/create/context.py b/client/ayon_core/pipeline/create/context.py index e9f259e53b..fd66881344 100644 --- a/client/ayon_core/pipeline/create/context.py +++ b/client/ayon_core/pipeline/create/context.py @@ -79,7 +79,7 @@ _NOT_SET = object() INSTANCE_ADDED_TOPIC = "instances.added" INSTANCE_REMOVED_TOPIC = "instances.removed" VALUE_CHANGED_TOPIC = "values.changed" -INSTANCE_STATE_CHANGED_TOPIC = "instance.state.changed" +INSTANCE_REQUIREMENT_CHANGED_TOPIC = "instance.requirement.changed" PRE_CREATE_ATTR_DEFS_CHANGED_TOPIC = "pre.create.attr.defs.changed" CREATE_ATTR_DEFS_CHANGED_TOPIC = "create.attr.defs.changed" PUBLISH_ATTR_DEFS_CHANGED_TOPIC = "publish.attr.defs.changed" @@ -258,10 +258,10 @@ class CreateContext: "create_attrs_change": BulkInfo(), # Publish attribute definitions changed "publish_attrs_change": BulkInfo(), - # Instance state changed - # - right now used only for 'mandatory' state but can be extended + # Instance requirement changed + # - right now used only for 'mandatory' but can be extended # in future - "state_change": BulkInfo(), + "requirement_change": BulkInfo(), } self._bulk_order = [] @@ -1054,10 +1054,10 @@ class CreateContext: PUBLISH_ATTR_DEFS_CHANGED_TOPIC, callback ) - def add_instance_state_change_callback( + def add_instance_requirement_change_callback( self, callback: Callable ) -> "EventCallback": - """Register callback to listen instance state changes. + """Register callback to listen instance requirement changes. Create plugin changed attribute definitions of instance. @@ -1072,7 +1072,7 @@ class CreateContext: Args: callback (Callable): Callback function that will be called when - create attributes changed. + instance requirement changed. Returns: EventCallback: Created callback object which can be used to @@ -1080,7 +1080,7 @@ class CreateContext: """ return self._event_hub.add_callback( - INSTANCE_STATE_CHANGED_TOPIC, callback + INSTANCE_REQUIREMENT_CHANGED_TOPIC, callback ) def context_data_to_store(self) -> dict[str, Any]: @@ -1358,9 +1358,9 @@ class CreateContext: yield bulk_info @contextmanager - def bulk_instance_state_change(self, sender: Optional[str] = None): + def bulk_instance_requirement_change(self, sender: Optional[str] = None): with self._bulk_context( - "state_change", sender + "requirement_change", sender ) as bulk_info: yield bulk_info @@ -1431,8 +1431,8 @@ class CreateContext: with self.bulk_value_changes() as bulk_item: bulk_item.append((instance_id, new_values)) - def instance_state_changed(self, instance_id: str) -> None: - """Instance state changed. + def instance_requirement_changed(self, instance_id: str) -> None: + """Instance requirement changed. Triggered by `CreatedInstance`. @@ -1441,7 +1441,7 @@ class CreateContext: """ if self._is_instance_events_ready(instance_id): - with self.bulk_instance_state_change() as bulk_item: + with self.bulk_instance_requirement_change() as bulk_item: bulk_item.append(instance_id) # --- context change callbacks --- @@ -2303,8 +2303,8 @@ class CreateContext: self._bulk_create_attrs_change_finished(data, sender) elif key == "publish_attrs_change": self._bulk_publish_attrs_change_finished(data, sender) - elif key == "state_change": - self._bulk_instance_state_change_finished(data, sender) + elif key == "requirement_change": + self._bulk_instance_requirement_change_finished(data, sender) def _bulk_add_instances_finished( self, @@ -2500,7 +2500,7 @@ class CreateContext: sender, ) - def _bulk_instance_state_change_finished( + def _bulk_instance_requirement_change_finished( self, instance_ids: list[str], sender: Optional[str], @@ -2514,7 +2514,7 @@ class CreateContext: ] self._emit_event( - INSTANCE_STATE_CHANGED_TOPIC, + INSTANCE_REQUIREMENT_CHANGED_TOPIC, {"instances": instances}, sender, ) diff --git a/client/ayon_core/pipeline/create/structures.py b/client/ayon_core/pipeline/create/structures.py index 1bc20501cc..a4c68d2502 100644 --- a/client/ayon_core/pipeline/create/structures.py +++ b/client/ayon_core/pipeline/create/structures.py @@ -750,7 +750,7 @@ class CreatedInstance: self._is_mandatory = value if value is True: self["active"] = True - self._create_context.instance_state_changed(self.id) + self._create_context.instance_requirement_changed(self.id) def changes(self): """Calculate and return changes.""" diff --git a/client/ayon_core/tools/publisher/control.py b/client/ayon_core/tools/publisher/control.py index b551b21bd4..038816c6fc 100644 --- a/client/ayon_core/tools/publisher/control.py +++ b/client/ayon_core/tools/publisher/control.py @@ -53,7 +53,8 @@ class PublisherController( changed. "create.context.create.attrs.changed" - Create attributes changed. "create.context.publish.attrs.changed" - Publish attributes changed. - "create.context.instance.state.changed" - Instance state changed. + "create.context.instance.requirement.changed" - Instance requirement + changed. "create.context.removed.instance" - Instance removed from context. "create.model.instances.context.changed" - Instances changed context. like folder, task or variant. diff --git a/client/ayon_core/tools/publisher/models/create.py b/client/ayon_core/tools/publisher/models/create.py index 7f44b374e6..75ed2c73fe 100644 --- a/client/ayon_core/tools/publisher/models/create.py +++ b/client/ayon_core/tools/publisher/models/create.py @@ -483,8 +483,8 @@ class CreateModel: self._create_context.add_publish_attr_defs_change_callback( self._cc_publish_attr_changed ) - self._create_context.add_instance_state_change_callback( - self._cc_instance_state_changed + self._create_context.add_instance_requirement_change_callback( + self._cc_instance_requirement_changed ) self._create_context.reset_finalization() @@ -1181,13 +1181,13 @@ class CreateModel: event_data, ) - def _cc_instance_state_changed(self, event): + def _cc_instance_requirement_changed(self, event): instance_ids = { instance.id for instance in event.data["instances"] } self._emit_event( - "create.context.instance.state.changed", + "create.model.instance.requirement.changed", {"instance_ids": instance_ids}, ) diff --git a/client/ayon_core/tools/publisher/widgets/overview_widget.py b/client/ayon_core/tools/publisher/widgets/overview_widget.py index 44bb09d4fc..a7e099c0d0 100644 --- a/client/ayon_core/tools/publisher/widgets/overview_widget.py +++ b/client/ayon_core/tools/publisher/widgets/overview_widget.py @@ -156,7 +156,7 @@ class OverviewWidget(QtWidgets.QFrame): self._on_instance_context_change ) controller.register_event_callback( - "create.model.instance.state.changed", + "create.model.instance.requirement.changed", self._on_instance_state_changed ) From 4a9132132b7b06202b552b06aaab4653c32633a7 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 8 Jul 2025 17:42:01 +0200 Subject: [PATCH 13/20] one more method name change --- client/ayon_core/tools/publisher/widgets/overview_widget.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/tools/publisher/widgets/overview_widget.py b/client/ayon_core/tools/publisher/widgets/overview_widget.py index a7e099c0d0..46395328e0 100644 --- a/client/ayon_core/tools/publisher/widgets/overview_widget.py +++ b/client/ayon_core/tools/publisher/widgets/overview_widget.py @@ -157,7 +157,7 @@ class OverviewWidget(QtWidgets.QFrame): ) controller.register_event_callback( "create.model.instance.requirement.changed", - self._on_instance_state_changed + self._on_instance_requirement_changed ) self._product_content_widget = product_content_widget @@ -358,7 +358,7 @@ class OverviewWidget(QtWidgets.QFrame): def _on_instance_context_change(self, event): self._refresh_instance_states(event["instance_ids"]) - def _on_instance_state_changed(self, event): + def _on_instance_requirement_changed(self, event): self._refresh_instance_states(event["instance_ids"]) def _refresh_instance_states(self, instance_ids): From ef4c5d676187fa6bb31a0e0857be2359b73d1723 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 9 Jul 2025 15:31:29 +0200 Subject: [PATCH 14/20] added anotations import --- client/ayon_core/tools/loader/control.py | 2 ++ client/ayon_core/tools/loader/ui/_multicombobox.py | 1 + client/ayon_core/tools/loader/ui/products_delegates.py | 3 +-- client/ayon_core/tools/loader/ui/products_widget.py | 1 + client/ayon_core/tools/loader/ui/search_bar.py | 2 ++ client/ayon_core/tools/loader/ui/window.py | 2 ++ 6 files changed, 9 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/tools/loader/control.py b/client/ayon_core/tools/loader/control.py index 95f48b3519..7ba42a0981 100644 --- a/client/ayon_core/tools/loader/control.py +++ b/client/ayon_core/tools/loader/control.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import logging import uuid diff --git a/client/ayon_core/tools/loader/ui/_multicombobox.py b/client/ayon_core/tools/loader/ui/_multicombobox.py index 393272fdf9..b4e1707242 100644 --- a/client/ayon_core/tools/loader/ui/_multicombobox.py +++ b/client/ayon_core/tools/loader/ui/_multicombobox.py @@ -1,4 +1,5 @@ from __future__ import annotations + import typing from typing import List, Tuple, Optional, Iterable, Any diff --git a/client/ayon_core/tools/loader/ui/products_delegates.py b/client/ayon_core/tools/loader/ui/products_delegates.py index e78b32ceb1..b500b86b97 100644 --- a/client/ayon_core/tools/loader/ui/products_delegates.py +++ b/client/ayon_core/tools/loader/ui/products_delegates.py @@ -2,7 +2,6 @@ from __future__ import annotations import numbers import uuid -from typing import Dict from qtpy import QtWidgets, QtCore, QtGui @@ -249,7 +248,7 @@ class VersionDelegate(QtWidgets.QStyledItemDelegate): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self._editor_by_id: Dict[str, VersionComboBox] = {} + self._editor_by_id: dict[str, VersionComboBox] = {} self._task_ids_filter = None self._statuses_filter = None self._version_tags_filter = None diff --git a/client/ayon_core/tools/loader/ui/products_widget.py b/client/ayon_core/tools/loader/ui/products_widget.py index f1c82ee83d..e5bb75a208 100644 --- a/client/ayon_core/tools/loader/ui/products_widget.py +++ b/client/ayon_core/tools/loader/ui/products_widget.py @@ -1,4 +1,5 @@ from __future__ import annotations + import collections from typing import Optional diff --git a/client/ayon_core/tools/loader/ui/search_bar.py b/client/ayon_core/tools/loader/ui/search_bar.py index ab673df1ac..a855a3c452 100644 --- a/client/ayon_core/tools/loader/ui/search_bar.py +++ b/client/ayon_core/tools/loader/ui/search_bar.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import copy import uuid from dataclasses import dataclass diff --git a/client/ayon_core/tools/loader/ui/window.py b/client/ayon_core/tools/loader/ui/window.py index d056b62b13..df5beb708f 100644 --- a/client/ayon_core/tools/loader/ui/window.py +++ b/client/ayon_core/tools/loader/ui/window.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from qtpy import QtWidgets, QtCore, QtGui from ayon_core.resources import get_ayon_icon_filepath From 0927358cd862bec7c3ae253c5af6db01bc59250c Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 9 Jul 2025 15:51:36 +0200 Subject: [PATCH 15/20] add more annotations imports --- client/ayon_core/tools/loader/models/actions.py | 2 ++ client/ayon_core/tools/loader/models/products.py | 1 + client/ayon_core/tools/loader/models/selection.py | 3 +++ client/ayon_core/tools/loader/models/sitesync.py | 2 ++ 4 files changed, 8 insertions(+) diff --git a/client/ayon_core/tools/loader/models/actions.py b/client/ayon_core/tools/loader/models/actions.py index 40331d73a4..b792f92dfd 100644 --- a/client/ayon_core/tools/loader/models/actions.py +++ b/client/ayon_core/tools/loader/models/actions.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import sys import traceback import inspect diff --git a/client/ayon_core/tools/loader/models/products.py b/client/ayon_core/tools/loader/models/products.py index edf8efc3b3..87e2406c81 100644 --- a/client/ayon_core/tools/loader/models/products.py +++ b/client/ayon_core/tools/loader/models/products.py @@ -1,5 +1,6 @@ """Products model for loader tools.""" from __future__ import annotations + import collections import contextlib from typing import TYPE_CHECKING, Iterable, Optional diff --git a/client/ayon_core/tools/loader/models/selection.py b/client/ayon_core/tools/loader/models/selection.py index 04add26f86..f2148352cd 100644 --- a/client/ayon_core/tools/loader/models/selection.py +++ b/client/ayon_core/tools/loader/models/selection.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + class SelectionModel(object): """Model handling selection changes. diff --git a/client/ayon_core/tools/loader/models/sitesync.py b/client/ayon_core/tools/loader/models/sitesync.py index c7f0038df4..3a54a1b5f8 100644 --- a/client/ayon_core/tools/loader/models/sitesync.py +++ b/client/ayon_core/tools/loader/models/sitesync.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import collections from ayon_api import ( From 8f995df928f5550eeed0b023c73c5ba89da733e3 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 10 Jul 2025 08:29:44 +0200 Subject: [PATCH 16/20] fix docstring --- client/ayon_core/pipeline/create/context.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/pipeline/create/context.py b/client/ayon_core/pipeline/create/context.py index fd66881344..cd06b06ea3 100644 --- a/client/ayon_core/pipeline/create/context.py +++ b/client/ayon_core/pipeline/create/context.py @@ -1057,9 +1057,9 @@ class CreateContext: def add_instance_requirement_change_callback( self, callback: Callable ) -> "EventCallback": - """Register callback to listen instance requirement changes. + """Register callback to listen to instance requirement changes. - Create plugin changed attribute definitions of instance. + Instance changed requirement of active state. Data structure of event:: From d1fe9d4af6928c5ae259d843142e04842c0633d0 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 10 Jul 2025 08:29:55 +0200 Subject: [PATCH 17/20] removed doubled doble colon --- client/ayon_core/pipeline/create/context.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/client/ayon_core/pipeline/create/context.py b/client/ayon_core/pipeline/create/context.py index cd06b06ea3..929cc59d2a 100644 --- a/client/ayon_core/pipeline/create/context.py +++ b/client/ayon_core/pipeline/create/context.py @@ -872,7 +872,7 @@ class CreateContext: Event is triggered when instances are already available in context and have set create/publish attribute definitions. - Data structure of event:: + Data structure of event: ```python { @@ -899,7 +899,7 @@ class CreateContext: Event is triggered when instances are already removed from context. - Data structure of event:: + Data structure of event: ```python { @@ -927,7 +927,7 @@ class CreateContext: Event is triggered when any value changes on any instance or context data. - Data structure of event:: + Data structure of event: ```python { @@ -965,7 +965,7 @@ class CreateContext: Create plugin can trigger refresh of pre-create attributes. Usage of this event is mainly for publisher UI. - Data structure of event:: + Data structure of event: ```python { @@ -994,7 +994,7 @@ class CreateContext: Create plugin changed attribute definitions of instance. - Data structure of event:: + Data structure of event: ```python { @@ -1023,7 +1023,7 @@ class CreateContext: Publish plugin changed attribute definitions of instance of context. - Data structure of event:: + Data structure of event: ```python { @@ -1061,7 +1061,7 @@ class CreateContext: Instance changed requirement of active state. - Data structure of event:: + Data structure of event: ```python { From bb4131e0e5148d509b59d097f6b3d579dce0e582 Mon Sep 17 00:00:00 2001 From: Ynbot Date: Thu, 10 Jul 2025 06:41:18 +0000 Subject: [PATCH 18/20] [Automated] Add generated package files from main --- client/ayon_core/version.py | 2 +- package.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index df92396802..963fc9ecdc 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'core' version.""" -__version__ = "1.4.0+dev" +__version__ = "1.4.1" diff --git a/package.py b/package.py index efed91b6cf..acc27aa7b8 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.4.0+dev" +version = "1.4.1" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index 91579f04fb..9b89ab23ac 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.4.0+dev" +version = "1.4.1" description = "" authors = ["Ynput Team "] readme = "README.md" From f6d39301ecab441c75457c8935283dd455c1c5ae Mon Sep 17 00:00:00 2001 From: Ynbot Date: Thu, 10 Jul 2025 06:41:50 +0000 Subject: [PATCH 19/20] [Automated] Update version in package.py for develop --- client/ayon_core/version.py | 2 +- package.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index 963fc9ecdc..509c4a8d14 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'core' version.""" -__version__ = "1.4.1" +__version__ = "1.4.1+dev" diff --git a/package.py b/package.py index acc27aa7b8..039bf0379c 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.4.1" +version = "1.4.1+dev" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index 9b89ab23ac..9609729420 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.4.1" +version = "1.4.1+dev" description = "" authors = ["Ynput Team "] readme = "README.md" From f6077eea2d089e7aafa696acfccd4958263ab23d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 10 Jul 2025 06:42:43 +0000 Subject: [PATCH 20/20] chore(): update bug report / version --- .github/ISSUE_TEMPLATE/bug_report.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index eff53116a2..9fb6ee645d 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -35,6 +35,7 @@ body: label: Version description: What version are you running? Look to AYON Tray options: + - 1.4.1 - 1.4.0 - 1.3.2 - 1.3.1