diff --git a/client/ayon_core/tools/common_models/hierarchy.py b/client/ayon_core/tools/common_models/hierarchy.py index a90e06a63b..f92563db20 100644 --- a/client/ayon_core/tools/common_models/hierarchy.py +++ b/client/ayon_core/tools/common_models/hierarchy.py @@ -1,3 +1,4 @@ +import time import collections import contextlib from abc import ABCMeta, abstractmethod @@ -535,13 +536,15 @@ class HierarchyModel(object): def _refresh_tasks_cache(self, project_name, folder_id, sender=None): if folder_id in self._tasks_refreshing: + while folder_id in self._tasks_refreshing: + time.sleep(0.01) return + cache = self._task_items[project_name][folder_id] with self._task_refresh_event_manager( project_name, folder_id, sender ): - task_items = self._query_tasks(project_name, folder_id) - self._task_items[project_name][folder_id] = task_items + cache.update_data(self._query_tasks(project_name, folder_id)) def _query_tasks(self, project_name, folder_id): tasks = list(ayon_api.get_tasks( diff --git a/client/ayon_core/tools/common_models/projects.py b/client/ayon_core/tools/common_models/projects.py index 90b75e15c1..4e8925388d 100644 --- a/client/ayon_core/tools/common_models/projects.py +++ b/client/ayon_core/tools/common_models/projects.py @@ -89,7 +89,7 @@ class FolderTypeItem: def from_project_item(cls, folder_type_data): return cls( name=folder_type_data["name"], - short=folder_type_data["shortName"], + short=folder_type_data.get("shortName", ""), icon=folder_type_data["icon"], ) diff --git a/client/ayon_core/tools/utils/folders_widget.py b/client/ayon_core/tools/utils/folders_widget.py index 3b53266c9f..bb75f3b6e5 100644 --- a/client/ayon_core/tools/utils/folders_widget.py +++ b/client/ayon_core/tools/utils/folders_widget.py @@ -192,7 +192,11 @@ class FoldersQtModel(QtGui.QStandardItemModel): or thread_id != self._current_refresh_thread.id ): return - folder_items, folder_type_items = thread.get_result() + if thread.failed: + # TODO visualize that refresh failed + folder_items, folder_type_items = {}, {} + else: + folder_items, folder_type_items = thread.get_result() self._fill_items(folder_items, folder_type_items) self._current_refresh_thread = None diff --git a/client/ayon_core/tools/utils/lib.py b/client/ayon_core/tools/utils/lib.py index e0626ec1db..f31bb82e59 100644 --- a/client/ayon_core/tools/utils/lib.py +++ b/client/ayon_core/tools/utils/lib.py @@ -2,7 +2,9 @@ import os import sys import contextlib import collections +import traceback from functools import partial +from typing import Union, Any from qtpy import QtWidgets, QtCore, QtGui import qtawesome @@ -425,26 +427,39 @@ class RefreshThread(QtCore.QThread): self._id = thread_id self._callback = partial(func, *args, **kwargs) self._exception = None + self._traceback = None self._result = None self.finished.connect(self._on_finish_callback) @property - def id(self): + def id(self) -> str: return self._id @property - def failed(self): + def failed(self) -> bool: return self._exception is not None def run(self): try: self._result = self._callback() except Exception as exc: + exc_type, exc_value, exc_traceback = sys.exc_info() + err_traceback = "".join(traceback.format_exception( + exc_type, exc_value, exc_traceback + )) + print(err_traceback) + self._traceback = err_traceback self._exception = exc - def get_result(self): + def get_result(self) -> Any: return self._result + def get_exception(self) -> Union[BaseException, None]: + return self._exception + + def get_traceback(self) -> Union[str, None]: + return self._traceback + def _on_finish_callback(self): """Trigger custom signal with thread id. diff --git a/client/ayon_core/tools/utils/tasks_widget.py b/client/ayon_core/tools/utils/tasks_widget.py index 314521b6b0..bba7b93925 100644 --- a/client/ayon_core/tools/utils/tasks_widget.py +++ b/client/ayon_core/tools/utils/tasks_widget.py @@ -191,12 +191,12 @@ class TasksQtModel(QtGui.QStandardItemModel): def _thread_getter(self, project_name, folder_id): task_items = self._controller.get_task_items( - project_name, folder_id + project_name, folder_id, sender=TASKS_MODEL_SENDER_NAME ) task_type_items = {} if hasattr(self._controller, "get_task_type_items"): task_type_items = self._controller.get_task_type_items( - project_name + project_name, sender=TASKS_MODEL_SENDER_NAME ) return task_items, task_type_items @@ -580,6 +580,7 @@ class TasksWidget(QtWidgets.QWidget): return if expected_data is None: expected_data = self._controller.get_expected_selection_data() + folder_data = expected_data.get("folder") task_data = expected_data.get("task") if ( diff --git a/server_addon/aftereffects/client/ayon_aftereffects/plugins/create/create_render.py b/server_addon/aftereffects/client/ayon_aftereffects/plugins/create/create_render.py index eafe845247..8e44b51a1c 100644 --- a/server_addon/aftereffects/client/ayon_aftereffects/plugins/create/create_render.py +++ b/server_addon/aftereffects/client/ayon_aftereffects/plugins/create/create_render.py @@ -94,7 +94,7 @@ class RenderCreator(Creator): new_instance.creator_attributes["farm"] = use_farm review = pre_create_data["mark_for_review"] - new_instance. creator_attributes["mark_for_review"] = review + new_instance.creator_attributes["mark_for_review"] = review api.get_stub().imprint(new_instance.id, new_instance.data_to_store()) diff --git a/server_addon/aftereffects/client/ayon_aftereffects/plugins/publish/collect_render.py b/server_addon/aftereffects/client/ayon_aftereffects/plugins/publish/collect_render.py index 3508512cba..e550e73709 100644 --- a/server_addon/aftereffects/client/ayon_aftereffects/plugins/publish/collect_render.py +++ b/server_addon/aftereffects/client/ayon_aftereffects/plugins/publish/collect_render.py @@ -23,8 +23,16 @@ class AERenderInstance(RenderInstance): class CollectAERender(publish.AbstractCollectRender): + """Prepares RenderInstance. - order = pyblish.api.CollectorOrder + 0.100 + RenderInstance is meant to replace simple dictionaries to provide code + assist and typing. (Currently used only in AE, Harmony though.) + + This must run after `collect_review`, but before Deadline plugins (which + should be run only on renderable instances.) + """ + + order = pyblish.api.CollectorOrder + 0.125 label = "Collect After Effects Render Layers" hosts = ["aftereffects"] @@ -173,24 +181,25 @@ class CollectAERender(publish.AbstractCollectRender): _, ext = os.path.splitext(os.path.basename(file_name)) ext = ext.replace('.', '') version_str = "v{:03d}".format(render_instance.version) - if "#" not in file_name: # single frame (mov)W - path = os.path.join(base_dir, "{}_{}_{}.{}".format( - render_instance.folderPath, + if "#" not in file_name: # single frame (mov) + file_name = "{}_{}.{}".format( render_instance.productName, version_str, ext - )) - expected_files.append(path) + ) + file_path = os.path.join(base_dir, file_name) + expected_files.append(file_path) else: for frame in range(start, end + 1): - path = os.path.join(base_dir, "{}_{}_{}.{}.{}".format( - render_instance.folderPath, + file_name = "{}_{}.{}.{}".format( render_instance.productName, version_str, str(frame).zfill(self.padding_width), ext - )) - expected_files.append(path) + ) + + file_path = os.path.join(base_dir, file_name) + expected_files.append(file_path) return expected_files def _get_output_dir(self, render_instance): diff --git a/server_addon/aftereffects/client/ayon_aftereffects/version.py b/server_addon/aftereffects/client/ayon_aftereffects/version.py index 2faa06ba3a..4e371a2467 100644 --- a/server_addon/aftereffects/client/ayon_aftereffects/version.py +++ b/server_addon/aftereffects/client/ayon_aftereffects/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'aftereffects' version.""" -__version__ = "0.2.1" +__version__ = "0.2.2" diff --git a/server_addon/aftereffects/package.py b/server_addon/aftereffects/package.py index 37a54b4212..eabba27df1 100644 --- a/server_addon/aftereffects/package.py +++ b/server_addon/aftereffects/package.py @@ -1,6 +1,6 @@ name = "aftereffects" title = "AfterEffects" -version = "0.2.1" +version = "0.2.2" client_dir = "ayon_aftereffects" diff --git a/server_addon/deadline/client/ayon_deadline/plugins/publish/submit_aftereffects_deadline.py b/server_addon/deadline/client/ayon_deadline/plugins/publish/submit_aftereffects_deadline.py index 9ca077d162..a7be38bf3b 100644 --- a/server_addon/deadline/client/ayon_deadline/plugins/publish/submit_aftereffects_deadline.py +++ b/server_addon/deadline/client/ayon_deadline/plugins/publish/submit_aftereffects_deadline.py @@ -57,12 +57,11 @@ class AfterEffectsSubmitDeadline( dln_job_info.Plugin = "AfterEffects" dln_job_info.UserName = context.data.get( "deadlineUser", getpass.getuser()) - if self._instance.data["frameEnd"] > self._instance.data["frameStart"]: - # Deadline requires integers in frame range - frame_range = "{}-{}".format( - int(round(self._instance.data["frameStart"])), - int(round(self._instance.data["frameEnd"]))) - dln_job_info.Frames = frame_range + # Deadline requires integers in frame range + frame_range = "{}-{}".format( + int(round(self._instance.data["frameStart"])), + int(round(self._instance.data["frameEnd"]))) + dln_job_info.Frames = frame_range dln_job_info.Priority = self.priority dln_job_info.Pool = self._instance.data.get("primaryPool")