diff --git a/client/ayon_core/cli.py b/client/ayon_core/cli.py index d7cd3ba7f5..6f89a6d17d 100644 --- a/client/ayon_core/cli.py +++ b/client/ayon_core/cli.py @@ -252,7 +252,6 @@ def _set_global_environments() -> None: os.environ.update(env) # Hardcoded default values - os.environ["PYBLISH_GUI"] = "pyblish_pype" # Change scale factor only if is not set if "QT_AUTO_SCREEN_SCALE_FACTOR" not in os.environ: os.environ["QT_AUTO_SCREEN_SCALE_FACTOR"] = "1" @@ -290,8 +289,6 @@ def main(*args, **kwargs): split_paths = python_path.split(os.pathsep) additional_paths = [ - # add AYON tools for 'pyblish_pype' - os.path.join(AYON_CORE_ROOT, "tools"), # add common AYON vendor # (common for multiple Python interpreter versions) os.path.join(AYON_CORE_ROOT, "vendor", "python") diff --git a/client/ayon_core/lib/python_2_comp.py b/client/ayon_core/lib/python_2_comp.py deleted file mode 100644 index 900db59062..0000000000 --- a/client/ayon_core/lib/python_2_comp.py +++ /dev/null @@ -1,17 +0,0 @@ -# Deprecated file -# - the file container 'WeakMethod' implementation for Python 2 which is not -# needed anymore. -import warnings -import weakref - - -WeakMethod = weakref.WeakMethod - -warnings.warn( - ( - "'ayon_core.lib.python_2_comp' is deprecated." - "Please use 'weakref.WeakMethod'." - ), - DeprecationWarning, - stacklevel=2 -) diff --git a/client/ayon_core/lib/python_module_tools.py b/client/ayon_core/lib/python_module_tools.py index d146e069a9..a6dc8031c7 100644 --- a/client/ayon_core/lib/python_module_tools.py +++ b/client/ayon_core/lib/python_module_tools.py @@ -1,6 +1,8 @@ +"""Tools for working with python modules and classes.""" import os import sys import types +from typing import Optional import importlib import inspect import logging @@ -8,13 +10,22 @@ import logging log = logging.getLogger(__name__) -def import_filepath(filepath, module_name=None): +def import_filepath( + filepath: str, + module_name: Optional[str] = None, + sys_module_name: Optional[str] = None) -> types.ModuleType: """Import python file as python module. Args: filepath (str): Path to python file. module_name (str): Name of loaded module. Only for Python 3. By default is filled with filename of filepath. + sys_module_name (str): Name of module in `sys.modules` where to store + loaded module. By default is None so module is not added to + `sys.modules`. + + Todo (antirotor): We should add the module to the sys.modules always but + we need to be careful about it and test it properly. """ if module_name is None: @@ -28,6 +39,9 @@ def import_filepath(filepath, module_name=None): module_loader = importlib.machinery.SourceFileLoader( module_name, filepath ) + # only add to sys.modules if requested + if sys_module_name: + sys.modules[sys_module_name] = module module_loader.exec_module(module) return module @@ -126,7 +140,8 @@ def classes_from_module(superclass, module): return classes -def import_module_from_dirpath(dirpath, folder_name, dst_module_name=None): +def import_module_from_dirpath( + dirpath, folder_name, dst_module_name=None): """Import passed directory as a python module. Imported module can be assigned as a child attribute of already loaded @@ -193,7 +208,7 @@ def is_func_signature_supported(func, *args, **kwargs): Notes: This does NOT check if the function would work with passed arguments only if they can be passed in. If function have *args, **kwargs - in paramaters, this will always return 'True'. + in parameters, this will always return 'True'. Example: >>> def my_function(my_number): diff --git a/client/ayon_core/pipeline/create/context.py b/client/ayon_core/pipeline/create/context.py index 24557234f4..26b04ed3ed 100644 --- a/client/ayon_core/pipeline/create/context.py +++ b/client/ayon_core/pipeline/create/context.py @@ -839,7 +839,7 @@ class CreateContext: publish_attributes.update(output) for plugin in self.plugins_with_defs: - attr_defs = plugin.get_attr_defs_for_context (self) + attr_defs = plugin.get_attr_defs_for_context(self) if not attr_defs: continue self._publish_attributes.set_publish_plugin_attr_defs( @@ -1259,50 +1259,6 @@ class CreateContext: with self._bulk_context("add", sender) as bulk_info: yield bulk_info - # Set publish attributes before bulk context is exited - for instance in bulk_info.get_data(): - publish_attributes = instance.publish_attributes - # Prepare publish plugin attributes and set it on instance - for plugin in self.plugins_with_defs: - try: - if is_func_signature_supported( - plugin.convert_attribute_values, self, instance - ): - plugin.convert_attribute_values(self, instance) - - elif plugin.__instanceEnabled__: - output = plugin.convert_attribute_values( - publish_attributes - ) - if output: - publish_attributes.update(output) - - except Exception: - self.log.error( - "Failed to convert attribute values of" - f" plugin '{plugin.__name__}'", - exc_info=True - ) - - for plugin in self.plugins_with_defs: - attr_defs = None - try: - attr_defs = plugin.get_attr_defs_for_instance( - self, instance - ) - except Exception: - self.log.error( - "Failed to get attribute definitions" - f" from plugin '{plugin.__name__}'.", - exc_info=True - ) - - if not attr_defs: - continue - instance.set_publish_plugin_attr_defs( - plugin.__name__, attr_defs - ) - @contextmanager def bulk_instances_collection(self, sender=None): """DEPRECATED use 'bulk_add_instances' instead.""" @@ -2251,6 +2207,50 @@ class CreateContext: if not instances_to_validate: return + # Set publish attributes before bulk callbacks are triggered + for instance in instances_to_validate: + publish_attributes = instance.publish_attributes + # Prepare publish plugin attributes and set it on instance + for plugin in self.plugins_with_defs: + try: + if is_func_signature_supported( + plugin.convert_attribute_values, self, instance + ): + plugin.convert_attribute_values(self, instance) + + elif plugin.__instanceEnabled__: + output = plugin.convert_attribute_values( + publish_attributes + ) + if output: + publish_attributes.update(output) + + except Exception: + self.log.error( + "Failed to convert attribute values of" + f" plugin '{plugin.__name__}'", + exc_info=True + ) + + for plugin in self.plugins_with_defs: + attr_defs = None + try: + attr_defs = plugin.get_attr_defs_for_instance( + self, instance + ) + except Exception: + self.log.error( + "Failed to get attribute definitions" + f" from plugin '{plugin.__name__}'.", + exc_info=True + ) + + if not attr_defs: + continue + instance.set_publish_plugin_attr_defs( + plugin.__name__, attr_defs + ) + # Cache folder and task entities for all instances at once self.get_instances_context_info(instances_to_validate) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index e48d99602e..c6f3ae7115 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -1,3 +1,4 @@ +from __future__ import annotations import copy import os import re @@ -247,7 +248,8 @@ def create_skeleton_instance( "useSequenceForReview": data.get("useSequenceForReview", True), # map inputVersions `ObjectId` -> `str` so json supports it "inputVersions": list(map(str, data.get("inputVersions", []))), - "colorspace": data.get("colorspace") + "colorspace": data.get("colorspace"), + "hasExplicitFrames": data.get("hasExplicitFrames") } if data.get("renderlayer"): @@ -324,8 +326,8 @@ def prepare_representations( skip_integration_repre_list (list): exclude specific extensions, do_not_add_review (bool): explicitly skip review color_managed_plugin (publish.ColormanagedPyblishPluginMixin) - frames_to_render (str): implicit or explicit range of frames to render - this value is sent to Deadline in JobInfo.Frames + frames_to_render (str | None): implicit or explicit range of frames + to render this value is sent to Deadline in JobInfo.Frames Returns: list of representations @@ -337,7 +339,7 @@ def prepare_representations( log = Logger.get_logger("farm_publishing") if frames_to_render is not None: - frames_to_render = _get_real_frames_to_render(frames_to_render) + frames_to_render = convert_frames_str_to_list(frames_to_render) else: # Backwards compatibility for older logic frame_start = int(skeleton_data.get("frameStartHandle")) @@ -386,17 +388,21 @@ def prepare_representations( frame_start -= 1 frames_to_render.insert(0, frame_start) - files = _get_real_files_to_render(collection, frames_to_render) + filenames = [ + os.path.basename(filepath) + for filepath in _get_real_files_to_render( + collection, frames_to_render + ) + ] # explicitly disable review by user preview = preview and not do_not_add_review rep = { "name": ext, "ext": ext, - "files": files, + "files": filenames, + "stagingDir": staging, "frameStart": frame_start, "frameEnd": frame_end, - # If expectedFile are absolute, we need only filenames - "stagingDir": staging, "fps": skeleton_data.get("fps"), "tags": ["review"] if preview else [], } @@ -475,21 +481,45 @@ def prepare_representations( return representations -def _get_real_frames_to_render(frames): - """Returns list of frames that should be rendered. +def convert_frames_str_to_list(frames: str) -> list[int]: + """Convert frames definition string to frames. + + Handles formats as: + >>> convert_frames_str_to_list('1001') + [1001] + >>> convert_frames_str_to_list('1002,1004') + [1002, 1004] + >>> convert_frames_str_to_list('1003-1005') + [1003, 1004, 1005] + >>> convert_frames_str_to_list('1001-1021x5') + [1001, 1006, 1011, 1016, 1021] + + Args: + frames (str): String with frames definition. + + Returns: + list[int]: List of frames. - Artists could want to selectively render only particular frames """ - frames_to_render = [] + step_pattern = re.compile(r"(?:step|by|every|x|:)(\d+)$") + + output = [] + step = 1 for frame in frames.split(","): if "-" in frame: - splitted = frame.split("-") - frames_to_render.extend( - range(int(splitted[0]), int(splitted[1])+1)) + frame_start, frame_end = frame.split("-") + match = step_pattern.findall(frame_end) + if match: + step = int(match[0]) + frame_end = re.sub(step_pattern, "", frame_end) + + output.extend( + range(int(frame_start), int(frame_end) + 1, step) + ) else: - frames_to_render.append(int(frame)) - frames_to_render.sort() - return frames_to_render + output.append(int(frame)) + output.sort() + return output def _get_real_files_to_render(collection, frames_to_render): @@ -502,22 +532,23 @@ def _get_real_files_to_render(collection, frames_to_render): This range would override and filter previously prepared expected files from DCC. + Example: + >>> expected_files = clique.parse([ + >>> "foo_v01.0001.exr", + >>> "foo_v01.0002.exr", + >>> ]) + >>> frames_to_render = [1] + >>> _get_real_files_to_render(expected_files, frames_to_render) + ["foo_v01.0001.exr"] + Args: collection (clique.Collection): absolute paths frames_to_render (list[int]): of int 1001 + Returns: - (list[str]) + list[str]: absolute paths of files to be rendered - Example: - -------- - expectedFiles = [ - "foo_v01.0001.exr", - "foo_v01.0002.exr", - ] - frames_to_render = 1 - >> - ["foo_v01.0001.exr"] - only explicitly requested frame returned """ included_frames = set(collection.indexes).intersection(frames_to_render) real_collection = clique.Collection( @@ -526,13 +557,17 @@ def _get_real_files_to_render(collection, frames_to_render): collection.padding, indexes=included_frames ) - real_full_paths = list(real_collection) - return [os.path.basename(file_url) for file_url in real_full_paths] + return list(real_collection) -def create_instances_for_aov(instance, skeleton, aov_filter, - skip_integration_repre_list, - do_not_add_review): +def create_instances_for_aov( + instance, + skeleton, + aov_filter, + skip_integration_repre_list, + do_not_add_review, + frames_to_render=None +): """Create instances from AOVs. This will create new pyblish.api.Instances by going over expected @@ -544,6 +579,7 @@ def create_instances_for_aov(instance, skeleton, aov_filter, aov_filter (dict): AOV filter. skip_integration_repre_list (list): skip do_not_add_review (bool): Explicitly disable reviews + frames_to_render (str | None): Frames to render. Returns: list of pyblish.api.Instance: Instances created from @@ -590,7 +626,8 @@ def create_instances_for_aov(instance, skeleton, aov_filter, aov_filter, additional_color_data, skip_integration_repre_list, - do_not_add_review + do_not_add_review, + frames_to_render ) @@ -719,8 +756,15 @@ def get_product_name_and_group_from_template( return resulting_product_name, resulting_group_name -def _create_instances_for_aov(instance, skeleton, aov_filter, additional_data, - skip_integration_repre_list, do_not_add_review): +def _create_instances_for_aov( + instance, + skeleton, + aov_filter, + additional_data, + skip_integration_repre_list, + do_not_add_review, + frames_to_render +): """Create instance for each AOV found. This will create new instance for every AOV it can detect in expected @@ -734,7 +778,8 @@ def _create_instances_for_aov(instance, skeleton, aov_filter, additional_data, skip_integration_repre_list (list): list of extensions that shouldn't be published do_not_add_review (bool): explicitly disable review - + frames_to_render (str | None): implicit or explicit range of + frames to render this value is sent to Deadline in JobInfo.Frames Returns: list of instances @@ -754,10 +799,23 @@ def _create_instances_for_aov(instance, skeleton, aov_filter, additional_data, # go through AOVs in expected files for aov, files in expected_files[0].items(): collected_files = _collect_expected_files_for_aov(files) + first_filepath = collected_files + if isinstance(first_filepath, (list, tuple)): + first_filepath = first_filepath[0] + staging_dir = os.path.dirname(first_filepath) - expected_filepath = collected_files - if isinstance(collected_files, (list, tuple)): - expected_filepath = collected_files[0] + if ( + frames_to_render is not None + and isinstance(collected_files, (list, tuple)) # not single file + ): + aov_frames_to_render = convert_frames_str_to_list(frames_to_render) + collections, _ = clique.assemble(collected_files) + collected_files = _get_real_files_to_render( + collections[0], aov_frames_to_render) + else: + frame_start = int(skeleton.get("frameStartHandle")) + frame_end = int(skeleton.get("frameEndHandle")) + aov_frames_to_render = list(range(frame_start, frame_end + 1)) dynamic_data = { "aov": aov, @@ -768,7 +826,7 @@ def _create_instances_for_aov(instance, skeleton, aov_filter, additional_data, # TODO: this must be changed to be more robust. Any coincidence # of camera name in the file path will be considered as # camera name. This is not correct. - camera = [cam for cam in cameras if cam in expected_filepath] + camera = [cam for cam in cameras if cam in first_filepath] # Is there just one camera matching? # TODO: this is not true, we can have multiple cameras in the scene @@ -813,10 +871,8 @@ def _create_instances_for_aov(instance, skeleton, aov_filter, additional_data, dynamic_data=dynamic_data ) - staging = os.path.dirname(expected_filepath) - try: - staging = remap_source(staging, anatomy) + staging_dir = remap_source(staging_dir, anatomy) except ValueError as e: log.warning(e) @@ -824,7 +880,7 @@ def _create_instances_for_aov(instance, skeleton, aov_filter, additional_data, app = os.environ.get("AYON_HOST_NAME", "") - render_file_name = os.path.basename(expected_filepath) + render_file_name = os.path.basename(first_filepath) aov_patterns = aov_filter @@ -881,10 +937,10 @@ def _create_instances_for_aov(instance, skeleton, aov_filter, additional_data, "name": ext, "ext": ext, "files": collected_files, - "frameStart": int(skeleton["frameStartHandle"]), - "frameEnd": int(skeleton["frameEndHandle"]), + "frameStart": aov_frames_to_render[0], + "frameEnd": aov_frames_to_render[-1], # If expectedFile are absolute, we need only filenames - "stagingDir": staging, + "stagingDir": staging_dir, "fps": new_instance.get("fps"), "tags": ["review"] if preview else [], "colorspaceData": { diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index 1fb906fd65..b601914acd 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -13,15 +13,7 @@ from .utils import get_representation_path_from_context class LoaderPlugin(list): - """Load representation into host application - - Arguments: - context (dict): avalon-core:context-1.0 - - .. versionadded:: 4.0 - This class was introduced - - """ + """Load representation into host application""" product_types = set() representations = set() diff --git a/client/ayon_core/pipeline/project_folders.py b/client/ayon_core/pipeline/project_folders.py index 902b969457..def2af9ba1 100644 --- a/client/ayon_core/pipeline/project_folders.py +++ b/client/ayon_core/pipeline/project_folders.py @@ -1,6 +1,8 @@ +from __future__ import annotations import os import re import json +from typing import Any, Union from ayon_core.settings import get_project_settings from ayon_core.lib import Logger @@ -9,7 +11,7 @@ from .anatomy import Anatomy from .template_data import get_project_template_data -def concatenate_splitted_paths(split_paths, anatomy): +def concatenate_splitted_paths(split_paths, anatomy: Anatomy): log = Logger.get_logger("concatenate_splitted_paths") pattern_array = re.compile(r"\[.*\]") output = [] @@ -47,7 +49,7 @@ def concatenate_splitted_paths(split_paths, anatomy): return output -def fill_paths(path_list, anatomy): +def fill_paths(path_list: list[str], anatomy: Anatomy): format_data = get_project_template_data(project_name=anatomy.project_name) format_data["root"] = anatomy.roots filled_paths = [] @@ -59,7 +61,7 @@ def fill_paths(path_list, anatomy): return filled_paths -def create_project_folders(project_name, basic_paths=None): +def create_project_folders(project_name: str, basic_paths=None): log = Logger.get_logger("create_project_folders") anatomy = Anatomy(project_name) if basic_paths is None: @@ -80,8 +82,19 @@ def create_project_folders(project_name, basic_paths=None): os.makedirs(path) -def _list_path_items(folder_structure): +def _list_path_items( + folder_structure: Union[dict[str, Any], list[str]]): output = [] + + # Allow leaf folders of the `project_folder_structure` to use a list of + # strings instead of a dictionary of keys with empty values. + if isinstance(folder_structure, list): + if not all(isinstance(item, str) for item in folder_structure): + raise ValueError( + f"List items must all be strings. Got: {folder_structure}") + return [[path] for path in folder_structure] + + # Process key, value as key for folder names and value its subfolders for key, value in folder_structure.items(): if not value: output.append(key) @@ -99,7 +112,7 @@ def _list_path_items(folder_structure): return output -def get_project_basic_paths(project_name): +def get_project_basic_paths(project_name: str): project_settings = get_project_settings(project_name) folder_structure = ( project_settings["core"]["project_folder_structure"] diff --git a/client/ayon_core/pipeline/publish/lib.py b/client/ayon_core/pipeline/publish/lib.py index cc5f67c74b..49ecab2221 100644 --- a/client/ayon_core/pipeline/publish/lib.py +++ b/client/ayon_core/pipeline/publish/lib.py @@ -1,3 +1,5 @@ +"""Library functions for publishing.""" +from __future__ import annotations import os import sys import inspect @@ -12,8 +14,8 @@ import pyblish.plugin import pyblish.api from ayon_core.lib import ( - Logger, import_filepath, + Logger, filter_profiles, ) from ayon_core.settings import get_project_settings @@ -163,7 +165,7 @@ class HelpContent: def load_help_content_from_filepath(filepath): """Load help content from xml file. - Xml file may containt errors and warnings. + Xml file may contain errors and warnings. """ errors = {} warnings = {} @@ -208,8 +210,9 @@ def load_help_content_from_plugin(plugin): return load_help_content_from_filepath(filepath) -def publish_plugins_discover(paths=None): - """Find and return available pyblish plug-ins +def publish_plugins_discover( + paths: Optional[list[str]] = None) -> DiscoverResult: + """Find and return available pyblish plug-ins. Overridden function from `pyblish` module to be able to collect crashed files and reason of their crash. @@ -252,17 +255,14 @@ def publish_plugins_discover(paths=None): continue try: - module = import_filepath(abspath, mod_name) + module = import_filepath( + abspath, mod_name, sys_module_name=mod_name) - # Store reference to original module, to avoid - # garbage collection from collecting it's global - # imports, such as `import os`. - sys.modules[abspath] = module - - except Exception as err: + except Exception as err: # noqa: BLE001 + # we need broad exception to catch all possible errors. result.crashed_file_paths[abspath] = sys.exc_info() - log.debug("Skipped: \"%s\" (%s)", mod_name, err) + log.debug('Skipped: "%s" (%s)', mod_name, err) continue for plugin in pyblish.plugin.plugins_from_module(module): @@ -280,9 +280,8 @@ def publish_plugins_discover(paths=None): continue plugin_names.append(plugin.__name__) - - plugin.__module__ = module.__file__ - key = "{0}.{1}".format(plugin.__module__, plugin.__name__) + plugin.__file__ = module.__file__ + key = f"{module.__file__}.{plugin.__name__}" plugins[key] = plugin # Include plug-ins from registration. @@ -361,7 +360,7 @@ def get_plugin_settings(plugin, project_settings, log, category=None): # Settings category determined from path # - usually path is './/plugins/publish/' # - category can be host name of addon name ('maya', 'deadline', ...) - filepath = os.path.normpath(inspect.getsourcefile(plugin)) + filepath = os.path.normpath(inspect.getfile(plugin)) split_path = filepath.rsplit(os.path.sep, 5) if len(split_path) < 4: @@ -427,7 +426,7 @@ def filter_pyblish_plugins(plugins): log = Logger.get_logger("filter_pyblish_plugins") # TODO: Don't use host from 'pyblish.api' but from defined host by us. - # - kept becau on farm is probably used host 'shell' which propably + # - kept because on farm is probably used host 'shell' which probably # affect how settings are applied there host_name = pyblish.api.current_host() project_name = os.environ.get("AYON_PROJECT_NAME") @@ -529,7 +528,7 @@ def filter_instances_for_context_plugin(plugin, context): Args: plugin (pyblish.api.Plugin): Plugin with filters. - context (pyblish.api.Context): Pyblish context with insances. + context (pyblish.api.Context): Pyblish context with instances. Returns: Iterator[pyblish.lib.Instance]: Iteration of valid instances. diff --git a/client/ayon_core/plugins/publish/extract_color_transcode.py b/client/ayon_core/plugins/publish/extract_color_transcode.py index 3c11a016ec..1f2c2a89af 100644 --- a/client/ayon_core/plugins/publish/extract_color_transcode.py +++ b/client/ayon_core/plugins/publish/extract_color_transcode.py @@ -280,6 +280,9 @@ class ExtractOIIOTranscode(publish.Extractor): collection = collections[0] frames = list(collection.indexes) + if collection.holes(): + return files_to_convert + frame_str = "{}-{}#".format(frames[0], frames[-1]) file_name = "{}{}{}".format(collection.head, frame_str, collection.tail) diff --git a/client/ayon_core/plugins/publish/integrate.py b/client/ayon_core/plugins/publish/integrate.py index e8fe09bab7..ae043a10a9 100644 --- a/client/ayon_core/plugins/publish/integrate.py +++ b/client/ayon_core/plugins/publish/integrate.py @@ -706,7 +706,8 @@ class IntegrateAsset(pyblish.api.InstancePlugin): # In case source are published in place we need to # skip renumbering repre_frame_start = repre.get("frameStart") - if repre_frame_start is not None: + explicit_frames = instance.data.get("hasExplicitFrames", False) + if not explicit_frames and repre_frame_start is not None: index_frame_start = int(repre_frame_start) # Shift destination sequence to the start frame destination_indexes = [ diff --git a/client/ayon_core/tools/pyblish_pype/__init__.py b/client/ayon_core/tools/pyblish_pype/__init__.py deleted file mode 100644 index ef507005a5..0000000000 --- a/client/ayon_core/tools/pyblish_pype/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -from .version import version, version_info, __version__ - -# This must be run prior to importing the application, due to the -# application requiring a discovered copy of Qt bindings. - -from .app import show - -__all__ = [ - 'show', - 'version', - 'version_info', - '__version__' -] diff --git a/client/ayon_core/tools/pyblish_pype/__main__.py b/client/ayon_core/tools/pyblish_pype/__main__.py deleted file mode 100644 index 5fc1b44a35..0000000000 --- a/client/ayon_core/tools/pyblish_pype/__main__.py +++ /dev/null @@ -1,19 +0,0 @@ -from .app import show - - -if __name__ == '__main__': - import argparse - - parser = argparse.ArgumentParser() - parser.add_argument("--debug", action="store_true") - - args = parser.parse_args() - - if args.debug: - from . import mock - import pyblish.api - - for Plugin in mock.plugins: - pyblish.api.register_plugin(Plugin) - - show() diff --git a/client/ayon_core/tools/pyblish_pype/app.css b/client/ayon_core/tools/pyblish_pype/app.css deleted file mode 100644 index 33b6acbddb..0000000000 --- a/client/ayon_core/tools/pyblish_pype/app.css +++ /dev/null @@ -1,539 +0,0 @@ -/* Global CSS */ - -* { - outline: none; - color: #ddd; - font-family: "Open Sans"; - font-style: normal; -} - -/* General CSS */ - -QWidget { - background: #555; - background-position: center center; - background-repeat: no-repeat; - font-size: 12px; -} - -QMenu { - background-color: #555; /* sets background of the menu */ - border: 1px solid #222; -} - -QMenu::item { - /* sets background of menu item. set this to something non-transparent - if you want menu color and menu item color to be different */ - background-color: transparent; - padding: 5px; - padding-left: 30px; -} - -QMenu::item:selected { /* when user selects item using mouse or keyboard */ - background-color: #666; -} - -QDialog { - min-width: 300; - background: "#555"; -} - -QListView { - border: 0px; - background: "transparent" -} - -QTreeView { - border: 0px; - background: "transparent" -} - -QPushButton { - width: 27px; - height: 27px; - background: #555; - border: 1px solid #aaa; - border-radius: 4px; - font-family: "FontAwesome"; - font-size: 11pt; - color: white; - padding: 0px; -} - -QPushButton:pressed { - background: "#777"; -} - -QPushButton:hover { - color: white; - background: "#666"; -} - -QPushButton:disabled { - color: rgba(255, 255, 255, 50); -} - -QTextEdit, QLineEdit { - background: #555; - border: 1px solid #333; - font-size: 9pt; - color: #fff; -} - -QCheckBox { - min-width: 17px; - max-width: 17px; - border: 1px solid #222; - background: transparent; -} - -QCheckBox::indicator { - width: 15px; - height: 15px; - /*background: #444;*/ - background: transparent; - border: 1px solid #555; -} - -QCheckBox::indicator:checked { - background: #222; -} - -QComboBox { - background: #444; - color: #EEE; - font-size: 8pt; - border: 1px solid #333; - padding: 0px; -} - -QComboBox[combolist="true"]::drop-down { - background: transparent; -} - -QComboBox[combolist="true"]::down-arrow { - max-width: 0px; - width: 1px; -} - -QComboBox[combolist="true"] QAbstractItemView { - background: #555; -} - -QScrollBar:vertical { - border: none; - background: transparent; - width: 6px; - margin: 0; -} - -QScrollBar::handle:vertical { - background: #333; - border-radius: 3px; - min-height: 20px; -} - -QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical { - height: 0px; -} - -QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical { - border: 1px solid #444; - width: 3px; - height: 3px; - background: white; -} - -QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { - background: none; -} - -QToolTip { - color: #eee; - background-color: #555; - border: none; - padding: 5px; -} - -QLabel { - border-radius: 0px; -} - -QToolButton { - background-color: transparent; - margin: 0px; - padding: 0px; - border-radius: 0px; - border: none; -} - -/* Specific CSS */ -#PerspectiveToggleBtn { - border-bottom: 3px solid lightblue; - border-top: 0px; - border-radius: 0px; - border-right: 1px solid #232323; - border-left: 0px; - font-size: 26pt; - font-family: "FontAwesome"; -} - -#Terminal QComboBox::drop-down { - width: 60px; -} - -#Header { - background: #555; - border: 1px solid #444; - padding: 0px; - margin: 0px; -} - -#Header QRadioButton { - border: 3px solid "transparent"; - border-right: 1px solid #333; - left: 2px; -} - -#Header QRadioButton::indicator { - width: 65px; - height: 40px; - background-repeat: no-repeat; - background-position: center center; - image: none; -} - -#Header QRadioButton:hover { - background-color: rgba(255, 255, 255, 10); -} - -#Header QRadioButton:checked { - background-color: rgba(255, 255, 255, 20); - border-bottom: 3px solid "lightblue"; -} - -#Body { - padding: 0px; - border: 1px solid #333; - background: #444; -} - -#Body QWidget { - background: #444; -} - -#Header #TerminalTab { - background-image: url("img/tab-terminal.png"); -} - -#Header #OverviewTab { - background-image: url("img/tab-overview.png"); -} - -#ButtonWithMenu { - background: #555; - border: 1px solid #fff; - border-radius: 4px; - font-family: "FontAwesome"; - font-size: 11pt; - color: white; -} - -#ButtonWithMenu:pressed { - background: #777; -} - -#ButtonWithMenu:hover { - color: white; - background: #666; -} -#ButtonWithMenu:disabled { - background: #666; - color: #999; - border: 1px solid #999; -} - -#FooterSpacer, #FooterInfo, #HeaderSpacer { - background: transparent; -} - -#Footer { - background: #555; - min-height: 43px; -} - -#Footer[success="1"] { - background: #458056 -} - -#Footer[success="0"] { - background-color: #AA5050 -} - -#Footer QPushButton { - background: #555; - border: 1px solid #aaa; - border-radius: 4px; - font-family: "FontAwesome"; - font-size: 11pt; - color: white; - padding: 0px; -} - -#Footer QPushButton:pressed:hover { - color: #3784c5; - background: #444; -} - -#Footer QPushButton:hover { - background: #505050; - border: 2px solid #3784c5; -} - -#Footer QPushButton:disabled { - border: 1px solid #888; - background: #666; - color: #999; -} - -#ClosingPlaceholder { - background: rgba(0, 0, 0, 50); -} - -#CommentIntentWidget { - background: transparent; -} - -#CommentBox, #CommentPlaceholder { - font-family: "Open Sans"; - font-size: 8pt; - padding: 5px; - background: #444; -} - -#CommentBox { - selection-background-color: #222; -} - -#CommentBox:disabled, #CommentPlaceholder:disabled, #IntentBox:disabled { - background: #555; -} - -#CommentPlaceholder { - color: #888 -} - -#IntentBox { - background: #444; - font-size: 8pt; - padding: 5px; - min-width: 75px; - color: #EEE; -} - -#IntentBox::drop-down:button { - border: 0px; - background: transparent; -} - -#IntentBox::down-arrow { - image: url("/img/down_arrow.png"); -} - -#IntentBox::down-arrow:disabled { - image: url(); -} - -#TerminalView { - background-color: transparent; -} - -#TerminalView:item { - background-color: transparent; -} - -#TerminalView:hover { - background-color: transparent; -} - -#TerminalView:selected { - background-color: transparent; -} - -#TerminalView:item:hover { - color: #ffffff; -} - -#TerminalView:item:selected { - color: #eeeeee; -} - -#TerminalView QTextEdit { - padding:3px; - color: #aaa; - border-radius: 7px; - border-color: #222; - border-style: solid; - border-width: 2px; - background-color: #333; -} - -#TerminalView QTextEdit:hover { - background-color: #353535; -} - -#TerminalView QTextEdit:selected { - background-color: #303030; -} - -#ExpandableWidgetContent { - border: none; - background-color: #232323; - color:#eeeeee; -} - -#EllidableLabel { - font-size: 16pt; - font-weight: normal; -} - -#PerspectiveScrollContent { - border: 1px solid #333; - border-radius: 0px; -} - -#PerspectiveWidgetContent{ - padding: 0px; -} - -#PerspectiveLabel { - background-color: transparent; - border: none; -} - -#PerspectiveIndicator { - font-size: 16pt; - font-weight: normal; - padding: 5px; - background-color: #ffffff; - color: #333333; -} - -#PerspectiveIndicator[state="warning"] { - background-color: #ff9900; - color: #ffffff; -} - -#PerspectiveIndicator[state="active"] { - background-color: #99CEEE; - color: #ffffff; -} - -#PerspectiveIndicator[state="error"] { - background-color: #cc4a4a; - color: #ffffff; -} - -#PerspectiveIndicator[state="ok"] { - background-color: #69a567; - color: #ffffff; -} - -#ExpandableHeader { - background-color: transparent; - margin: 0px; - padding: 0px; - border-radius: 0px; - border: none; -} - -#ExpandableHeader QWidget { - color: #ddd; -} - -#ExpandableHeader QWidget:hover { - color: #fff; -} - -#TerminalFilterWidget QPushButton { - /* font: %(font_size_pt)spt; */ - font-family: "FontAwesome"; - text-align: center; - background-color: transparent; - border-width: 1px; - border-color: #777777; - border-style: none; - padding: 0px; - border-radius: 8px; -} -#TerminalFilterWidget QPushButton:hover { - background: #5f5f5f; - border-style: none; -} -#TerminalFilterWidget QPushButton:pressed { - background: #606060; - border-style: none; -} -#TerminalFilterWidget QPushButton:pressed:hover { - background: #626262; - border-style: none; -} - -#TerminalFilerBtn[type="info"]:checked {color: rgb(255, 255, 255);} -#TerminalFilerBtn[type="info"]:hover:pressed {color: rgba(255, 255, 255, 163);} -#TerminalFilerBtn[type="info"] {color: rgba(255, 255, 255, 63);} - -#TerminalFilerBtn[type="error"]:checked {color: rgb(255, 74, 74);} -#TerminalFilerBtn[type="error"]:hover:pressed {color: rgba(255, 74, 74, 163);} -#TerminalFilerBtn[type="error"] {color: rgba(255, 74, 74, 63);} - -#TerminalFilerBtn[type="log_debug"]:checked {color: rgb(255, 102, 232);} -#TerminalFilerBtn[type="log_debug"] {color: rgba(255, 102, 232, 63);} -#TerminalFilerBtn[type="log_debug"]:hover:pressed { - color: rgba(255, 102, 232, 163); -} - -#TerminalFilerBtn[type="log_info"]:checked {color: rgb(102, 171, 255);} -#TerminalFilerBtn[type="log_info"] {color: rgba(102, 171, 255, 63);} -#TerminalFilerBtn[type="log_info"]:hover:pressed { - color: rgba(102, 171, 255, 163); -} - -#TerminalFilerBtn[type="log_warning"]:checked {color: rgb(255, 186, 102);} -#TerminalFilerBtn[type="log_warning"] {color: rgba(255, 186, 102, 63);} -#TerminalFilerBtn[type="log_warning"]:hover:pressed { - color: rgba(255, 186, 102, 163); -} - -#TerminalFilerBtn[type="log_error"]:checked {color: rgb(255, 77, 88);} -#TerminalFilerBtn[type="log_error"] {color: rgba(255, 77, 88, 63);} -#TerminalFilerBtn[type="log_error"]:hover:pressed { - color: rgba(255, 77, 88, 163); -} - -#TerminalFilerBtn[type="log_critical"]:checked {color: rgb(255, 79, 117);} -#TerminalFilerBtn[type="log_critical"] {color: rgba(255, 79, 117, 63);} -#TerminalFilerBtn[type="log_critical"]:hover:pressed { - color: rgba(255, 79, 117, 163); -} - -#SuspendLogsBtn { - background: #444; - border: none; - border-top-right-radius: 7px; - border-bottom-right-radius: 7px; - border-top-left-radius: 0px; - border-bottom-left-radius: 0px; - font-family: "FontAwesome"; - font-size: 11pt; - color: white; - padding: 0px; -} - -#SuspendLogsBtn:hover { - background: #333; -} - -#SuspendLogsBtn:disabled { - background: #4c4c4c; -} diff --git a/client/ayon_core/tools/pyblish_pype/app.py b/client/ayon_core/tools/pyblish_pype/app.py deleted file mode 100644 index bdc204bfbd..0000000000 --- a/client/ayon_core/tools/pyblish_pype/app.py +++ /dev/null @@ -1,110 +0,0 @@ -from __future__ import print_function - -import os -import sys -import ctypes -import platform -import contextlib - -from qtpy import QtCore, QtGui, QtWidgets - -from . import control, settings, util, window - -self = sys.modules[__name__] - -# Maintain reference to currently opened window -self._window = None - - -@contextlib.contextmanager -def application(): - app = QtWidgets.QApplication.instance() - - if not app: - print("Starting new QApplication..") - app = QtWidgets.QApplication(sys.argv) - yield app - app.exec_() - else: - print("Using existing QApplication..") - yield app - if os.environ.get("PYBLISH_GUI_ALWAYS_EXEC"): - app.exec_() - - -def install_translator(app): - translator = QtCore.QTranslator(app) - translator.load(QtCore.QLocale.system(), "i18n/", - directory=util.root) - app.installTranslator(translator) - print("Installed translator") - - -def install_fonts(): - database = QtGui.QFontDatabase() - fonts = [ - "opensans/OpenSans-Bold.ttf", - "opensans/OpenSans-BoldItalic.ttf", - "opensans/OpenSans-ExtraBold.ttf", - "opensans/OpenSans-ExtraBoldItalic.ttf", - "opensans/OpenSans-Italic.ttf", - "opensans/OpenSans-Light.ttf", - "opensans/OpenSans-LightItalic.ttf", - "opensans/OpenSans-Regular.ttf", - "opensans/OpenSans-Semibold.ttf", - "opensans/OpenSans-SemiboldItalic.ttf", - "fontawesome/fontawesome-webfont.ttf" - ] - - for font in fonts: - path = util.get_asset("font", font) - - # TODO(marcus): Check if they are already installed first. - # In hosts, this will be called each time the GUI is shown, - # potentially installing a font each time. - if database.addApplicationFont(path) < 0: - print("Could not install %s" % path) - else: - print("Installed %s" % font) - - -def on_destroyed(): - """Remove internal reference to window on window destroyed""" - self._window = None - - -def show(parent=None): - with open(util.get_asset("app.css")) as f: - css = f.read() - - # Make relative paths absolute - root = util.get_asset("").replace("\\", "/") - css = css.replace("url(\"", "url(\"%s" % root) - - with application() as app: - - if platform.system().lower() == "windows": - ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID( - u"pyblish_pype" - ) - - install_fonts() - install_translator(app) - - if self._window is None: - ctrl = control.Controller() - self._window = window.Window(ctrl, parent) - self._window.destroyed.connect(on_destroyed) - - self._window.show() - self._window.activateWindow() - self._window.setWindowTitle(settings.WindowTitle) - - font = QtGui.QFont("Open Sans", 8, QtGui.QFont.Normal) - self._window.setFont(font) - self._window.setStyleSheet(css) - - self._window.reset() - self._window.resize(*settings.WindowSize) - - return self._window diff --git a/client/ayon_core/tools/pyblish_pype/awesome.py b/client/ayon_core/tools/pyblish_pype/awesome.py deleted file mode 100644 index c70f5b1064..0000000000 --- a/client/ayon_core/tools/pyblish_pype/awesome.py +++ /dev/null @@ -1,733 +0,0 @@ - -tags = { - "500px": u"\uf26e", - "adjust": u"\uf042", - "adn": u"\uf170", - "align-center": u"\uf037", - "align-justify": u"\uf039", - "align-left": u"\uf036", - "align-right": u"\uf038", - "amazon": u"\uf270", - "ambulance": u"\uf0f9", - "american-sign-language-interpreting": u"\uf2a3", - "anchor": u"\uf13d", - "android": u"\uf17b", - "angellist": u"\uf209", - "angle-double-down": u"\uf103", - "angle-double-left": u"\uf100", - "angle-double-right": u"\uf101", - "angle-double-up": u"\uf102", - "angle-down": u"\uf107", - "angle-left": u"\uf104", - "angle-right": u"\uf105", - "angle-up": u"\uf106", - "apple": u"\uf179", - "archive": u"\uf187", - "area-chart": u"\uf1fe", - "arrow-circle-down": u"\uf0ab", - "arrow-circle-left": u"\uf0a8", - "arrow-circle-o-down": u"\uf01a", - "arrow-circle-o-left": u"\uf190", - "arrow-circle-o-right": u"\uf18e", - "arrow-circle-o-up": u"\uf01b", - "arrow-circle-right": u"\uf0a9", - "arrow-circle-up": u"\uf0aa", - "arrow-down": u"\uf063", - "arrow-left": u"\uf060", - "arrow-right": u"\uf061", - "arrow-up": u"\uf062", - "arrows": u"\uf047", - "arrows-alt": u"\uf0b2", - "arrows-h": u"\uf07e", - "arrows-v": u"\uf07d", - "asl-interpreting (alias)": u"\uf2a3", - "assistive-listening-systems": u"\uf2a2", - "asterisk": u"\uf069", - "at": u"\uf1fa", - "audio-description": u"\uf29e", - "automobile (alias)": u"\uf1b9", - "backward": u"\uf04a", - "balance-scale": u"\uf24e", - "ban": u"\uf05e", - "bank (alias)": u"\uf19c", - "bar-chart": u"\uf080", - "bar-chart-o (alias)": u"\uf080", - "barcode": u"\uf02a", - "bars": u"\uf0c9", - "battery-0 (alias)": u"\uf244", - "battery-1 (alias)": u"\uf243", - "battery-2 (alias)": u"\uf242", - "battery-3 (alias)": u"\uf241", - "battery-4 (alias)": u"\uf240", - "battery-empty": u"\uf244", - "battery-full": u"\uf240", - "battery-half": u"\uf242", - "battery-quarter": u"\uf243", - "battery-three-quarters": u"\uf241", - "bed": u"\uf236", - "beer": u"\uf0fc", - "behance": u"\uf1b4", - "behance-square": u"\uf1b5", - "bell": u"\uf0f3", - "bell-o": u"\uf0a2", - "bell-slash": u"\uf1f6", - "bell-slash-o": u"\uf1f7", - "bicycle": u"\uf206", - "binoculars": u"\uf1e5", - "birthday-cake": u"\uf1fd", - "bitbucket": u"\uf171", - "bitbucket-square": u"\uf172", - "bitcoin (alias)": u"\uf15a", - "black-tie": u"\uf27e", - "blind": u"\uf29d", - "bluetooth": u"\uf293", - "bluetooth-b": u"\uf294", - "bold": u"\uf032", - "bolt": u"\uf0e7", - "bomb": u"\uf1e2", - "book": u"\uf02d", - "bookmark": u"\uf02e", - "bookmark-o": u"\uf097", - "braille": u"\uf2a1", - "briefcase": u"\uf0b1", - "btc": u"\uf15a", - "bug": u"\uf188", - "building": u"\uf1ad", - "building-o": u"\uf0f7", - "bullhorn": u"\uf0a1", - "bullseye": u"\uf140", - "bus": u"\uf207", - "buysellads": u"\uf20d", - "cab (alias)": u"\uf1ba", - "calculator": u"\uf1ec", - "calendar": u"\uf073", - "calendar-check-o": u"\uf274", - "calendar-minus-o": u"\uf272", - "calendar-o": u"\uf133", - "calendar-plus-o": u"\uf271", - "calendar-times-o": u"\uf273", - "camera": u"\uf030", - "camera-retro": u"\uf083", - "car": u"\uf1b9", - "caret-down": u"\uf0d7", - "caret-left": u"\uf0d9", - "caret-right": u"\uf0da", - "caret-square-o-down": u"\uf150", - "caret-square-o-left": u"\uf191", - "caret-square-o-right": u"\uf152", - "caret-square-o-up": u"\uf151", - "caret-up": u"\uf0d8", - "cart-arrow-down": u"\uf218", - "cart-plus": u"\uf217", - "cc": u"\uf20a", - "cc-amex": u"\uf1f3", - "cc-diners-club": u"\uf24c", - "cc-discover": u"\uf1f2", - "cc-jcb": u"\uf24b", - "cc-mastercard": u"\uf1f1", - "cc-paypal": u"\uf1f4", - "cc-stripe": u"\uf1f5", - "cc-visa": u"\uf1f0", - "certificate": u"\uf0a3", - "chain (alias)": u"\uf0c1", - "chain-broken": u"\uf127", - "check": u"\uf00c", - "check-circle": u"\uf058", - "check-circle-o": u"\uf05d", - "check-square": u"\uf14a", - "check-square-o": u"\uf046", - "chevron-circle-down": u"\uf13a", - "chevron-circle-left": u"\uf137", - "chevron-circle-right": u"\uf138", - "chevron-circle-up": u"\uf139", - "chevron-down": u"\uf078", - "chevron-left": u"\uf053", - "chevron-right": u"\uf054", - "chevron-up": u"\uf077", - "child": u"\uf1ae", - "chrome": u"\uf268", - "circle": u"\uf111", - "circle-o": u"\uf10c", - "circle-o-notch": u"\uf1ce", - "circle-thin": u"\uf1db", - "clipboard": u"\uf0ea", - "clock-o": u"\uf017", - "clone": u"\uf24d", - "close (alias)": u"\uf00d", - "cloud": u"\uf0c2", - "cloud-download": u"\uf0ed", - "cloud-upload": u"\uf0ee", - "cny (alias)": u"\uf157", - "code": u"\uf121", - "code-fork": u"\uf126", - "codepen": u"\uf1cb", - "codiepie": u"\uf284", - "coffee": u"\uf0f4", - "cog": u"\uf013", - "cogs": u"\uf085", - "columns": u"\uf0db", - "comment": u"\uf075", - "comment-o": u"\uf0e5", - "commenting": u"\uf27a", - "commenting-o": u"\uf27b", - "comments": u"\uf086", - "comments-o": u"\uf0e6", - "compass": u"\uf14e", - "compress": u"\uf066", - "connectdevelop": u"\uf20e", - "contao": u"\uf26d", - "copy (alias)": u"\uf0c5", - "copyright": u"\uf1f9", - "creative-commons": u"\uf25e", - "credit-card": u"\uf09d", - "credit-card-alt": u"\uf283", - "crop": u"\uf125", - "crosshairs": u"\uf05b", - "css3": u"\uf13c", - "cube": u"\uf1b2", - "cubes": u"\uf1b3", - "cut (alias)": u"\uf0c4", - "cutlery": u"\uf0f5", - "dashboard (alias)": u"\uf0e4", - "dashcube": u"\uf210", - "database": u"\uf1c0", - "deaf": u"\uf2a4", - "deafness (alias)": u"\uf2a4", - "dedent (alias)": u"\uf03b", - "delicious": u"\uf1a5", - "desktop": u"\uf108", - "deviantart": u"\uf1bd", - "diamond": u"\uf219", - "digg": u"\uf1a6", - "dollar (alias)": u"\uf155", - "dot-circle-o": u"\uf192", - "download": u"\uf019", - "dribbble": u"\uf17d", - "dropbox": u"\uf16b", - "drupal": u"\uf1a9", - "edge": u"\uf282", - "edit (alias)": u"\uf044", - "eject": u"\uf052", - "ellipsis-h": u"\uf141", - "ellipsis-v": u"\uf142", - "empire": u"\uf1d1", - "envelope": u"\uf0e0", - "envelope-o": u"\uf003", - "envelope-square": u"\uf199", - "envira": u"\uf299", - "eraser": u"\uf12d", - "eur": u"\uf153", - "euro (alias)": u"\uf153", - "exchange": u"\uf0ec", - "exclamation": u"\uf12a", - "exclamation-circle": u"\uf06a", - "exclamation-triangle": u"\uf071", - "expand": u"\uf065", - "expeditedssl": u"\uf23e", - "external-link": u"\uf08e", - "external-link-square": u"\uf14c", - "eye": u"\uf06e", - "eye-slash": u"\uf070", - "eyedropper": u"\uf1fb", - "fa (alias)": u"\uf2b4", - "facebook": u"\uf09a", - "facebook-f (alias)": u"\uf09a", - "facebook-official": u"\uf230", - "facebook-square": u"\uf082", - "fast-backward": u"\uf049", - "fast-forward": u"\uf050", - "fax": u"\uf1ac", - "feed (alias)": u"\uf09e", - "female": u"\uf182", - "fighter-jet": u"\uf0fb", - "file": u"\uf15b", - "file-archive-o": u"\uf1c6", - "file-audio-o": u"\uf1c7", - "file-code-o": u"\uf1c9", - "file-excel-o": u"\uf1c3", - "file-image-o": u"\uf1c5", - "file-movie-o (alias)": u"\uf1c8", - "file-o": u"\uf016", - "file-pdf-o": u"\uf1c1", - "file-photo-o (alias)": u"\uf1c5", - "file-picture-o (alias)": u"\uf1c5", - "file-powerpoint-o": u"\uf1c4", - "file-sound-o (alias)": u"\uf1c7", - "file-text": u"\uf15c", - "file-text-o": u"\uf0f6", - "file-video-o": u"\uf1c8", - "file-word-o": u"\uf1c2", - "file-zip-o (alias)": u"\uf1c6", - "files-o": u"\uf0c5", - "film": u"\uf008", - "filter": u"\uf0b0", - "fire": u"\uf06d", - "fire-extinguisher": u"\uf134", - "firefox": u"\uf269", - "first-order": u"\uf2b0", - "flag": u"\uf024", - "flag-checkered": u"\uf11e", - "flag-o": u"\uf11d", - "flash (alias)": u"\uf0e7", - "flask": u"\uf0c3", - "flickr": u"\uf16e", - "floppy-o": u"\uf0c7", - "folder": u"\uf07b", - "folder-o": u"\uf114", - "folder-open": u"\uf07c", - "folder-open-o": u"\uf115", - "font": u"\uf031", - "font-awesome": u"\uf2b4", - "fonticons": u"\uf280", - "fort-awesome": u"\uf286", - "forumbee": u"\uf211", - "forward": u"\uf04e", - "foursquare": u"\uf180", - "frown-o": u"\uf119", - "futbol-o": u"\uf1e3", - "gamepad": u"\uf11b", - "gavel": u"\uf0e3", - "gbp": u"\uf154", - "ge (alias)": u"\uf1d1", - "gear (alias)": u"\uf013", - "gears (alias)": u"\uf085", - "genderless": u"\uf22d", - "get-pocket": u"\uf265", - "gg": u"\uf260", - "gg-circle": u"\uf261", - "gift": u"\uf06b", - "git": u"\uf1d3", - "git-square": u"\uf1d2", - "github": u"\uf09b", - "github-alt": u"\uf113", - "github-square": u"\uf092", - "gitlab": u"\uf296", - "gittip (alias)": u"\uf184", - "glass": u"\uf000", - "glide": u"\uf2a5", - "glide-g": u"\uf2a6", - "globe": u"\uf0ac", - "google": u"\uf1a0", - "google-plus": u"\uf0d5", - "google-plus-circle (alias)": u"\uf2b3", - "google-plus-official": u"\uf2b3", - "google-plus-square": u"\uf0d4", - "google-wallet": u"\uf1ee", - "graduation-cap": u"\uf19d", - "gratipay": u"\uf184", - "group (alias)": u"\uf0c0", - "h-square": u"\uf0fd", - "hacker-news": u"\uf1d4", - "hand-grab-o (alias)": u"\uf255", - "hand-lizard-o": u"\uf258", - "hand-o-down": u"\uf0a7", - "hand-o-left": u"\uf0a5", - "hand-o-right": u"\uf0a4", - "hand-o-up": u"\uf0a6", - "hand-paper-o": u"\uf256", - "hand-peace-o": u"\uf25b", - "hand-pointer-o": u"\uf25a", - "hand-rock-o": u"\uf255", - "hand-scissors-o": u"\uf257", - "hand-spock-o": u"\uf259", - "hand-stop-o (alias)": u"\uf256", - "hard-of-hearing (alias)": u"\uf2a4", - "hashtag": u"\uf292", - "hdd-o": u"\uf0a0", - "header": u"\uf1dc", - "headphones": u"\uf025", - "heart": u"\uf004", - "heart-o": u"\uf08a", - "heartbeat": u"\uf21e", - "history": u"\uf1da", - "home": u"\uf015", - "hospital-o": u"\uf0f8", - "hotel (alias)": u"\uf236", - "hourglass": u"\uf254", - "hourglass-1 (alias)": u"\uf251", - "hourglass-2 (alias)": u"\uf252", - "hourglass-3 (alias)": u"\uf253", - "hourglass-end": u"\uf253", - "hourglass-half": u"\uf252", - "hourglass-o": u"\uf250", - "hourglass-start": u"\uf251", - "houzz": u"\uf27c", - "html5": u"\uf13b", - "i-cursor": u"\uf246", - "ils": u"\uf20b", - "image (alias)": u"\uf03e", - "inbox": u"\uf01c", - "indent": u"\uf03c", - "industry": u"\uf275", - "info": u"\uf129", - "info-circle": u"\uf05a", - "inr": u"\uf156", - "instagram": u"\uf16d", - "institution (alias)": u"\uf19c", - "internet-explorer": u"\uf26b", - "intersex (alias)": u"\uf224", - "ioxhost": u"\uf208", - "italic": u"\uf033", - "joomla": u"\uf1aa", - "jpy": u"\uf157", - "jsfiddle": u"\uf1cc", - "key": u"\uf084", - "keyboard-o": u"\uf11c", - "krw": u"\uf159", - "language": u"\uf1ab", - "laptop": u"\uf109", - "lastfm": u"\uf202", - "lastfm-square": u"\uf203", - "leaf": u"\uf06c", - "leanpub": u"\uf212", - "legal (alias)": u"\uf0e3", - "lemon-o": u"\uf094", - "level-down": u"\uf149", - "level-up": u"\uf148", - "life-bouy (alias)": u"\uf1cd", - "life-buoy (alias)": u"\uf1cd", - "life-ring": u"\uf1cd", - "life-saver (alias)": u"\uf1cd", - "lightbulb-o": u"\uf0eb", - "line-chart": u"\uf201", - "link": u"\uf0c1", - "linkedin": u"\uf0e1", - "linkedin-square": u"\uf08c", - "linux": u"\uf17c", - "list": u"\uf03a", - "list-alt": u"\uf022", - "list-ol": u"\uf0cb", - "list-ul": u"\uf0ca", - "location-arrow": u"\uf124", - "lock": u"\uf023", - "long-arrow-down": u"\uf175", - "long-arrow-left": u"\uf177", - "long-arrow-right": u"\uf178", - "long-arrow-up": u"\uf176", - "low-vision": u"\uf2a8", - "magic": u"\uf0d0", - "magnet": u"\uf076", - "mail-forward (alias)": u"\uf064", - "mail-reply (alias)": u"\uf112", - "mail-reply-all (alias)": u"\uf122", - "male": u"\uf183", - "map": u"\uf279", - "map-marker": u"\uf041", - "map-o": u"\uf278", - "map-pin": u"\uf276", - "map-signs": u"\uf277", - "mars": u"\uf222", - "mars-double": u"\uf227", - "mars-stroke": u"\uf229", - "mars-stroke-h": u"\uf22b", - "mars-stroke-v": u"\uf22a", - "maxcdn": u"\uf136", - "meanpath": u"\uf20c", - "medium": u"\uf23a", - "medkit": u"\uf0fa", - "meh-o": u"\uf11a", - "mercury": u"\uf223", - "microphone": u"\uf130", - "microphone-slash": u"\uf131", - "minus": u"\uf068", - "minus-circle": u"\uf056", - "minus-square": u"\uf146", - "minus-square-o": u"\uf147", - "mixcloud": u"\uf289", - "mobile": u"\uf10b", - "mobile-phone (alias)": u"\uf10b", - "modx": u"\uf285", - "money": u"\uf0d6", - "moon-o": u"\uf186", - "mortar-board (alias)": u"\uf19d", - "motorcycle": u"\uf21c", - "mouse-pointer": u"\uf245", - "music": u"\uf001", - "navicon (alias)": u"\uf0c9", - "neuter": u"\uf22c", - "newspaper-o": u"\uf1ea", - "object-group": u"\uf247", - "object-ungroup": u"\uf248", - "odnoklassniki": u"\uf263", - "odnoklassniki-square": u"\uf264", - "opencart": u"\uf23d", - "openid": u"\uf19b", - "opera": u"\uf26a", - "optin-monster": u"\uf23c", - "outdent": u"\uf03b", - "pagelines": u"\uf18c", - "paint-brush": u"\uf1fc", - "paper-plane": u"\uf1d8", - "paper-plane-o": u"\uf1d9", - "paperclip": u"\uf0c6", - "paragraph": u"\uf1dd", - "paste (alias)": u"\uf0ea", - "pause": u"\uf04c", - "pause-circle": u"\uf28b", - "pause-circle-o": u"\uf28c", - "paw": u"\uf1b0", - "paypal": u"\uf1ed", - "pencil": u"\uf040", - "pencil-square": u"\uf14b", - "pencil-square-o": u"\uf044", - "percent": u"\uf295", - "phone": u"\uf095", - "phone-square": u"\uf098", - "photo (alias)": u"\uf03e", - "picture-o": u"\uf03e", - "pie-chart": u"\uf200", - "pied-piper": u"\uf2ae", - "pied-piper-alt": u"\uf1a8", - "pied-piper-pp": u"\uf1a7", - "pinterest": u"\uf0d2", - "pinterest-p": u"\uf231", - "pinterest-square": u"\uf0d3", - "plane": u"\uf072", - "play": u"\uf04b", - "play-circle": u"\uf144", - "play-circle-o": u"\uf01d", - "plug": u"\uf1e6", - "plus": u"\uf067", - "plus-circle": u"\uf055", - "plus-square": u"\uf0fe", - "plus-square-o": u"\uf196", - "power-off": u"\uf011", - "print": u"\uf02f", - "product-hunt": u"\uf288", - "puzzle-piece": u"\uf12e", - "qq": u"\uf1d6", - "qrcode": u"\uf029", - "question": u"\uf128", - "question-circle": u"\uf059", - "question-circle-o": u"\uf29c", - "quote-left": u"\uf10d", - "quote-right": u"\uf10e", - "ra (alias)": u"\uf1d0", - "random": u"\uf074", - "rebel": u"\uf1d0", - "recycle": u"\uf1b8", - "reddit": u"\uf1a1", - "reddit-alien": u"\uf281", - "reddit-square": u"\uf1a2", - "refresh": u"\uf021", - "registered": u"\uf25d", - "remove (alias)": u"\uf00d", - "renren": u"\uf18b", - "reorder (alias)": u"\uf0c9", - "repeat": u"\uf01e", - "reply": u"\uf112", - "reply-all": u"\uf122", - "resistance (alias)": u"\uf1d0", - "retweet": u"\uf079", - "rmb (alias)": u"\uf157", - "road": u"\uf018", - "rocket": u"\uf135", - "rotate-left (alias)": u"\uf0e2", - "rotate-right (alias)": u"\uf01e", - "rouble (alias)": u"\uf158", - "rss": u"\uf09e", - "rss-square": u"\uf143", - "rub": u"\uf158", - "ruble (alias)": u"\uf158", - "rupee (alias)": u"\uf156", - "safari": u"\uf267", - "save (alias)": u"\uf0c7", - "scissors": u"\uf0c4", - "scribd": u"\uf28a", - "search": u"\uf002", - "search-minus": u"\uf010", - "search-plus": u"\uf00e", - "sellsy": u"\uf213", - "send (alias)": u"\uf1d8", - "send-o (alias)": u"\uf1d9", - "server": u"\uf233", - "share": u"\uf064", - "share-alt": u"\uf1e0", - "share-alt-square": u"\uf1e1", - "share-square": u"\uf14d", - "share-square-o": u"\uf045", - "shekel (alias)": u"\uf20b", - "sheqel (alias)": u"\uf20b", - "shield": u"\uf132", - "ship": u"\uf21a", - "shirtsinbulk": u"\uf214", - "shopping-bag": u"\uf290", - "shopping-basket": u"\uf291", - "shopping-cart": u"\uf07a", - "sign-in": u"\uf090", - "sign-language": u"\uf2a7", - "sign-out": u"\uf08b", - "signal": u"\uf012", - "signing (alias)": u"\uf2a7", - "simplybuilt": u"\uf215", - "sitemap": u"\uf0e8", - "skyatlas": u"\uf216", - "skype": u"\uf17e", - "slack": u"\uf198", - "sliders": u"\uf1de", - "slideshare": u"\uf1e7", - "smile-o": u"\uf118", - "snapchat": u"\uf2ab", - "snapchat-ghost": u"\uf2ac", - "snapchat-square": u"\uf2ad", - "soccer-ball-o (alias)": u"\uf1e3", - "sort": u"\uf0dc", - "sort-alpha-asc": u"\uf15d", - "sort-alpha-desc": u"\uf15e", - "sort-amount-asc": u"\uf160", - "sort-amount-desc": u"\uf161", - "sort-asc": u"\uf0de", - "sort-desc": u"\uf0dd", - "sort-down (alias)": u"\uf0dd", - "sort-numeric-asc": u"\uf162", - "sort-numeric-desc": u"\uf163", - "sort-up (alias)": u"\uf0de", - "soundcloud": u"\uf1be", - "space-shuttle": u"\uf197", - "spinner": u"\uf110", - "spoon": u"\uf1b1", - "spotify": u"\uf1bc", - "square": u"\uf0c8", - "square-o": u"\uf096", - "stack-exchange": u"\uf18d", - "stack-overflow": u"\uf16c", - "star": u"\uf005", - "star-half": u"\uf089", - "star-half-empty (alias)": u"\uf123", - "star-half-full (alias)": u"\uf123", - "star-half-o": u"\uf123", - "star-o": u"\uf006", - "steam": u"\uf1b6", - "steam-square": u"\uf1b7", - "step-backward": u"\uf048", - "step-forward": u"\uf051", - "stethoscope": u"\uf0f1", - "sticky-note": u"\uf249", - "sticky-note-o": u"\uf24a", - "stop": u"\uf04d", - "stop-circle": u"\uf28d", - "stop-circle-o": u"\uf28e", - "street-view": u"\uf21d", - "strikethrough": u"\uf0cc", - "stumbleupon": u"\uf1a4", - "stumbleupon-circle": u"\uf1a3", - "subscript": u"\uf12c", - "subway": u"\uf239", - "suitcase": u"\uf0f2", - "sun-o": u"\uf185", - "superscript": u"\uf12b", - "support (alias)": u"\uf1cd", - "table": u"\uf0ce", - "tablet": u"\uf10a", - "tachometer": u"\uf0e4", - "tag": u"\uf02b", - "tags": u"\uf02c", - "tasks": u"\uf0ae", - "taxi": u"\uf1ba", - "television": u"\uf26c", - "tencent-weibo": u"\uf1d5", - "terminal": u"\uf120", - "text-height": u"\uf034", - "text-width": u"\uf035", - "th": u"\uf00a", - "th-large": u"\uf009", - "th-list": u"\uf00b", - "themeisle": u"\uf2b2", - "thumb-tack": u"\uf08d", - "thumbs-down": u"\uf165", - "thumbs-o-down": u"\uf088", - "thumbs-o-up": u"\uf087", - "thumbs-up": u"\uf164", - "ticket": u"\uf145", - "times": u"\uf00d", - "times-circle": u"\uf057", - "times-circle-o": u"\uf05c", - "tint": u"\uf043", - "toggle-down (alias)": u"\uf150", - "toggle-left (alias)": u"\uf191", - "toggle-off": u"\uf204", - "toggle-on": u"\uf205", - "toggle-right (alias)": u"\uf152", - "toggle-up (alias)": u"\uf151", - "trademark": u"\uf25c", - "train": u"\uf238", - "transgender": u"\uf224", - "transgender-alt": u"\uf225", - "trash": u"\uf1f8", - "trash-o": u"\uf014", - "tree": u"\uf1bb", - "trello": u"\uf181", - "tripadvisor": u"\uf262", - "trophy": u"\uf091", - "truck": u"\uf0d1", - "try": u"\uf195", - "tty": u"\uf1e4", - "tumblr": u"\uf173", - "tumblr-square": u"\uf174", - "turkish-lira (alias)": u"\uf195", - "tv (alias)": u"\uf26c", - "twitch": u"\uf1e8", - "twitter": u"\uf099", - "twitter-square": u"\uf081", - "umbrella": u"\uf0e9", - "underline": u"\uf0cd", - "undo": u"\uf0e2", - "universal-access": u"\uf29a", - "university": u"\uf19c", - "unlink (alias)": u"\uf127", - "unlock": u"\uf09c", - "unlock-alt": u"\uf13e", - "unsorted (alias)": u"\uf0dc", - "upload": u"\uf093", - "usb": u"\uf287", - "usd": u"\uf155", - "user": u"\uf007", - "user-md": u"\uf0f0", - "user-plus": u"\uf234", - "user-secret": u"\uf21b", - "user-times": u"\uf235", - "users": u"\uf0c0", - "venus": u"\uf221", - "venus-double": u"\uf226", - "venus-mars": u"\uf228", - "viacoin": u"\uf237", - "viadeo": u"\uf2a9", - "viadeo-square": u"\uf2aa", - "video-camera": u"\uf03d", - "vimeo": u"\uf27d", - "vimeo-square": u"\uf194", - "vine": u"\uf1ca", - "vk": u"\uf189", - "volume-control-phone": u"\uf2a0", - "volume-down": u"\uf027", - "volume-off": u"\uf026", - "volume-up": u"\uf028", - "warning (alias)": u"\uf071", - "wechat (alias)": u"\uf1d7", - "weibo": u"\uf18a", - "weixin": u"\uf1d7", - "whatsapp": u"\uf232", - "wheelchair": u"\uf193", - "wheelchair-alt": u"\uf29b", - "wifi": u"\uf1eb", - "wikipedia-w": u"\uf266", - "windows": u"\uf17a", - "won (alias)": u"\uf159", - "wordpress": u"\uf19a", - "wpbeginner": u"\uf297", - "wpforms": u"\uf298", - "wrench": u"\uf0ad", - "xing": u"\uf168", - "xing-square": u"\uf169", - "y-combinator": u"\uf23b", - "y-combinator-square (alias)": u"\uf1d4", - "yahoo": u"\uf19e", - "yc (alias)": u"\uf23b", - "yc-square (alias)": u"\uf1d4", - "yelp": u"\uf1e9", - "yen (alias)": u"\uf157", - "yoast": u"\uf2b1", - "youtube": u"\uf167", - "youtube-play": u"\uf16a", - "youtube-square": u"\uf166" -} diff --git a/client/ayon_core/tools/pyblish_pype/constants.py b/client/ayon_core/tools/pyblish_pype/constants.py deleted file mode 100644 index 10f95ca4af..0000000000 --- a/client/ayon_core/tools/pyblish_pype/constants.py +++ /dev/null @@ -1,97 +0,0 @@ -from qtpy import QtCore - -EXPANDER_WIDTH = 20 - - -def flags(*args, **kwargs): - type_name = kwargs.pop("type_name", "Flags") - with_base = kwargs.pop("with_base", False) - enums = {} - for idx, attr_name in enumerate(args): - if with_base: - if idx == 0: - enums[attr_name] = 0 - continue - idx -= 1 - enums[attr_name] = 2**idx - - for attr_name, value in kwargs.items(): - enums[attr_name] = value - return type(type_name, (), enums) - - -def roles(*args, **kwargs): - type_name = kwargs.pop("type_name", "Roles") - enums = {} - for attr_name, value in kwargs.items(): - enums[attr_name] = value - - offset = 0 - for idx, attr_name in enumerate(args): - _idx = idx + QtCore.Qt.UserRole + offset - while _idx in enums.values(): - offset += 1 - _idx = idx + offset - - enums[attr_name] = _idx - - return type(type_name, (), enums) - - -Roles = roles( - "ObjectIdRole", - "ObjectUIdRole", - "TypeRole", - "PublishFlagsRole", - "LogRecordsRole", - - "IsOptionalRole", - "IsEnabledRole", - - "FamiliesRole", - - "DocstringRole", - "PathModuleRole", - "PluginActionsVisibleRole", - "PluginValidActionsRole", - "PluginActionProgressRole", - - "TerminalItemTypeRole", - - "IntentItemValue", - - type_name="ModelRoles" -) - -InstanceStates = flags( - "ContextType", - "InProgress", - "HasWarning", - "HasError", - "HasFinished", - type_name="InstanceState" -) - -PluginStates = flags( - "IsCompatible", - "InProgress", - "WasProcessed", - "WasSkipped", - "HasWarning", - "HasError", - type_name="PluginState" -) - -GroupStates = flags( - "HasWarning", - "HasError", - "HasFinished", - type_name="GroupStates" -) - -PluginActionStates = flags( - "InProgress", - "HasFailed", - "HasFinished", - type_name="PluginActionStates" -) diff --git a/client/ayon_core/tools/pyblish_pype/control.py b/client/ayon_core/tools/pyblish_pype/control.py deleted file mode 100644 index c5034e2736..0000000000 --- a/client/ayon_core/tools/pyblish_pype/control.py +++ /dev/null @@ -1,666 +0,0 @@ -"""The Controller in a Model/View/Controller-based application -The graphical components of Pyblish Lite use this object to perform -publishing. It communicates via the Qt Signals/Slots mechanism -and has no direct connection to any graphics. This is important, -because this is how unittests are able to run without requiring -an active window manager; such as via Travis-CI. -""" -import os -import sys -import inspect -import logging -import collections - -from qtpy import QtCore - -import pyblish.api -import pyblish.util -import pyblish.logic -import pyblish.lib -import pyblish.version - -from . import util -from .constants import InstanceStates - -from ayon_core.settings import get_current_project_settings - - -class IterationBreak(Exception): - pass - - -class MainThreadItem: - """Callback with args and kwargs.""" - def __init__(self, callback, *args, **kwargs): - self.callback = callback - self.args = args - self.kwargs = kwargs - - def process(self): - self.callback(*self.args, **self.kwargs) - - -class MainThreadProcess(QtCore.QObject): - """Qt based main thread process executor. - - Has timer which controls each 50ms if there is new item to process. - - This approach gives ability to update UI meanwhile plugin is in progress. - """ - # How many times let pass QtApplication to process events - # - use 2 as resize event can trigger repaint event but not process in - # same loop - count_timeout = 2 - - def __init__(self): - super(MainThreadProcess, self).__init__() - self._items_to_process = collections.deque() - - timer = QtCore.QTimer() - timer.setInterval(0) - - timer.timeout.connect(self._execute) - - self._timer = timer - self._switch_counter = self.count_timeout - - def process(self, func, *args, **kwargs): - item = MainThreadItem(func, *args, **kwargs) - self.add_item(item) - - def add_item(self, item): - self._items_to_process.append(item) - - def _execute(self): - if not self._items_to_process: - return - - if self._switch_counter > 0: - self._switch_counter -= 1 - return - - self._switch_counter = self.count_timeout - - item = self._items_to_process.popleft() - item.process() - - def start(self): - if not self._timer.isActive(): - self._timer.start() - - def stop(self): - if self._timer.isActive(): - self._timer.stop() - - def clear(self): - if self._timer.isActive(): - self._timer.stop() - self._items_to_process = collections.deque() - - def stop_if_empty(self): - if self._timer.isActive(): - item = MainThreadItem(self._stop_if_empty) - self.add_item(item) - - def _stop_if_empty(self): - if not self._items_to_process: - self.stop() - - -class Controller(QtCore.QObject): - log = logging.getLogger("PyblishController") - # Emitted when the GUI is about to start processing; - # e.g. resetting, validating or publishing. - about_to_process = QtCore.Signal(object, object) - - # ??? Emitted for each process - was_processed = QtCore.Signal(dict) - - # Emitted when reset - # - all data are reset (plugins, processing, pari yielder, etc.) - was_reset = QtCore.Signal() - - # Emitted when previous group changed - passed_group = QtCore.Signal(object) - - # Emitted when want to change state of instances - switch_toggleability = QtCore.Signal(bool) - - # On action finished - was_acted = QtCore.Signal(dict) - - # Emitted when processing has stopped - was_stopped = QtCore.Signal() - - # Emitted when processing has finished - was_finished = QtCore.Signal() - - # Emitted when plugin was skipped - was_skipped = QtCore.Signal(object) - - # store OrderGroups - now it is a singleton - order_groups = util.OrderGroups - - # When instance is toggled - instance_toggled = QtCore.Signal(object, object, object) - - def __init__(self, parent=None): - super(Controller, self).__init__(parent) - self.context = None - self.plugins = {} - self.optional_default = {} - self.instance_toggled.connect(self._on_instance_toggled) - self._main_thread_processor = MainThreadProcess() - - self._current_state = "" - - def reset_variables(self): - self.log.debug("Resetting pyblish context variables") - - # Data internal to the GUI itself - self.is_running = False - self.stopped = False - self.errored = False - self._current_state = "" - - # Active producer of pairs - self.pair_generator = None - # Active pair - self.current_pair = None - - # Orders which changes GUI - # - passing collectors order disables plugin/instance toggle - self.collect_state = 0 - - # - passing validators order disables validate button and gives ability - # to know when to stop on validate button press - self.validators_order = None - self.validated = False - - # Get collectors and validators order - plugin_groups_keys = list(self.order_groups.groups.keys()) - self.validators_order = self.order_groups.validation_order - next_group_order = None - if len(plugin_groups_keys) > 1: - next_group_order = plugin_groups_keys[1] - - # This is used to track whether or not to continue - # processing when, for example, validation has failed. - self.processing = { - "stop_on_validation": False, - # Used? - "last_plugin_order": None, - "current_group_order": plugin_groups_keys[0], - "next_group_order": next_group_order, - "nextOrder": None, - "ordersWithError": set() - } - self._set_state_by_order() - self.log.debug("Reset of pyblish context variables done") - - @property - def current_state(self): - return self._current_state - - @staticmethod - def _convert_filter_presets(filter_presets): - """Convert AYON settings presets to dictionary. - - Returns: - dict[str, dict[str, Any]]: Filter presets converted to dictionary. - """ - if not isinstance(filter_presets, list): - return filter_presets - - return { - filter_preset["name"]: { - item["name"]: item["value"] - for item in filter_preset["value"] - } - for filter_preset in filter_presets - } - - def presets_by_hosts(self): - # Get global filters as base - presets = get_current_project_settings() - if not presets: - return {} - - result = {} - hosts = pyblish.api.registered_hosts() - for host in hosts: - host_presets = presets.get(host, {}).get("filters") - if not host_presets: - continue - - host_presets = self._convert_filter_presets(host_presets) - - for key, value in host_presets.items(): - if value is None: - if key in result: - result.pop(key) - continue - - result[key] = value - - return result - - def reset_context(self): - self.log.debug("Resetting pyblish context object") - - comment = None - if ( - self.context is not None and - self.context.data.get("comment") and - # We only preserve the user typed comment if we are *not* - # resetting from a successful publish without errors - self._current_state != "Published" - ): - comment = self.context.data["comment"] - - self.context = pyblish.api.Context() - - self.context._publish_states = InstanceStates.ContextType - self.context.optional = False - - self.context.data["publish"] = True - self.context.data["name"] = "context" - - self.context.data["host"] = reversed(pyblish.api.registered_hosts()) - self.context.data["port"] = int( - os.environ.get("PYBLISH_CLIENT_PORT", -1) - ) - self.context.data["connectTime"] = pyblish.lib.time(), - self.context.data["pyblishVersion"] = pyblish.version, - self.context.data["pythonVersion"] = sys.version - - self.context.data["icon"] = "book" - - self.context.families = ("__context__",) - - if comment: - # Preserve comment on reset if user previously had a comment - self.context.data["comment"] = comment - - self.log.debug("Reset of pyblish context object done") - - def reset(self): - """Discover plug-ins and run collection.""" - self._main_thread_processor.clear() - self._main_thread_processor.process(self._reset) - self._main_thread_processor.start() - - def _reset(self): - self.reset_context() - self.reset_variables() - - self.possible_presets = self.presets_by_hosts() - - # Load plugins and set pair generator - self.load_plugins() - self.pair_generator = self._pair_yielder(self.plugins) - - self.was_reset.emit() - - # Process collectors load rest of plugins with collected instances - self.collect() - - def load_plugins(self): - self.test = pyblish.logic.registered_test() - self.optional_default = {} - - plugins = pyblish.api.discover() - - targets = set(pyblish.logic.registered_targets()) - targets.add("default") - targets = list(targets) - plugins_by_targets = pyblish.logic.plugins_by_targets(plugins, targets) - - _plugins = [] - for plugin in plugins_by_targets: - # Skip plugin if is not optional and not active - if ( - not getattr(plugin, "optional", False) - and not getattr(plugin, "active", True) - ): - continue - _plugins.append(plugin) - self.plugins = _plugins - - def on_published(self): - if self.is_running: - self.is_running = False - self._current_state = ( - "Published" if not self.errored else "Published, with errors" - ) - self.was_finished.emit() - self._main_thread_processor.stop() - - def stop(self): - self.log.debug("Stopping") - self.stopped = True - - def act(self, plugin, action): - self.is_running = True - item = MainThreadItem(self._process_action, plugin, action) - self._main_thread_processor.add_item(item) - self._main_thread_processor.start() - self._main_thread_processor.stop_if_empty() - - def _process_action(self, plugin, action): - result = pyblish.plugin.process( - plugin, self.context, None, action.id - ) - self.is_running = False - self.was_acted.emit(result) - - def emit_(self, signal, kwargs): - pyblish.api.emit(signal, **kwargs) - - def _process(self, plugin, instance=None): - """Produce `result` from `plugin` and `instance` - :func:`process` shares state with :func:`_iterator` such that - an instance/plugin pair can be fetched and processed in isolation. - Arguments: - plugin (pyblish.api.Plugin): Produce result using plug-in - instance (optional, pyblish.api.Instance): Process this instance, - if no instance is provided, context is processed. - """ - - self.processing["nextOrder"] = plugin.order - - try: - result = pyblish.plugin.process(plugin, self.context, instance) - # Make note of the order at which the - # potential error error occurred. - if result["error"] is not None: - self.processing["ordersWithError"].add(plugin.order) - - except Exception as exc: - raise Exception("Unknown error({}): {}".format( - plugin.__name__, str(exc) - )) - - return result - - def _pair_yielder(self, plugins): - for plugin in plugins: - if ( - self.processing["current_group_order"] is not None - and plugin.order > self.processing["current_group_order"] - ): - current_group_order = self.processing["current_group_order"] - - new_next_group_order = None - new_current_group_order = self.processing["next_group_order"] - if new_current_group_order is not None: - current_next_order_found = False - for order in self.order_groups.groups.keys(): - if current_next_order_found: - new_next_group_order = order - break - - if order == new_current_group_order: - current_next_order_found = True - - self.processing["next_group_order"] = new_next_group_order - self.processing["current_group_order"] = ( - new_current_group_order - ) - - # Force update to the current state - self._set_state_by_order() - - if self.collect_state == 0: - self.collect_state = 1 - self._current_state = ( - "Ready" if not self.errored else - "Collected, with errors" - ) - self.switch_toggleability.emit(True) - self.passed_group.emit(current_group_order) - yield IterationBreak("Collected") - - else: - self.passed_group.emit(current_group_order) - if self.errored: - self._current_state = ( - "Stopped, due to errors" if not - self.processing["stop_on_validation"] else - "Validated, with errors" - ) - yield IterationBreak("Last group errored") - - if self.collect_state == 1: - self.collect_state = 2 - self.switch_toggleability.emit(False) - - if not self.validated and plugin.order > self.validators_order: - self.validated = True - if self.processing["stop_on_validation"]: - self._current_state = ( - "Validated" if not self.errored else - "Validated, with errors" - ) - yield IterationBreak("Validated") - - # Stop if was stopped - if self.stopped: - self.stopped = False - self._current_state = "Paused" - yield IterationBreak("Stopped") - - # check test if will stop - self.processing["nextOrder"] = plugin.order - message = self.test(**self.processing) - if message: - self._current_state = "Paused" - yield IterationBreak("Stopped due to \"{}\"".format(message)) - - self.processing["last_plugin_order"] = plugin.order - if not plugin.active: - pyblish.logic.log.debug("%s was inactive, skipping.." % plugin) - self.was_skipped.emit(plugin) - continue - - in_collect_stage = self.collect_state == 0 - if plugin.__instanceEnabled__: - instances = pyblish.logic.instances_by_plugin( - self.context, plugin - ) - if not instances: - self.was_skipped.emit(plugin) - continue - - for instance in instances: - if ( - not in_collect_stage - and instance.data.get("publish") is False - ): - pyblish.logic.log.debug( - "%s was inactive, skipping.." % instance - ) - continue - # Stop if was stopped - if self.stopped: - self.stopped = False - self._current_state = "Paused" - yield IterationBreak("Stopped") - - yield (plugin, instance) - else: - families = util.collect_families_from_instances( - self.context, only_active=not in_collect_stage - ) - plugins = pyblish.logic.plugins_by_families( - [plugin], families - ) - if not plugins: - self.was_skipped.emit(plugin) - continue - yield (plugin, None) - - self.passed_group.emit(self.processing["next_group_order"]) - - def iterate_and_process(self, on_finished=None): - """ Iterating inserted plugins with current context. - Collectors do not contain instances, they are None when collecting! - This process don't stop on one - """ - self._main_thread_processor.start() - - def on_next(): - self.log.debug("Looking for next pair to process") - try: - self.current_pair = next(self.pair_generator) - if isinstance(self.current_pair, IterationBreak): - raise self.current_pair - - except IterationBreak: - self.log.debug("Iteration break was raised") - self.is_running = False - self.was_stopped.emit() - self._main_thread_processor.stop() - return - - except StopIteration: - self.log.debug("Iteration stop was raised") - self.is_running = False - # All pairs were processed successfully! - if on_finished is not None: - self._main_thread_processor.add_item( - MainThreadItem(on_finished) - ) - self._main_thread_processor.stop_if_empty() - return - - except Exception as exc: - self.log.warning( - "Unexpected exception during `on_next` happened", - exc_info=True - ) - exc_msg = str(exc) - self._main_thread_processor.add_item( - MainThreadItem(on_unexpected_error, error=exc_msg) - ) - return - - self.about_to_process.emit(*self.current_pair) - self._main_thread_processor.add_item( - MainThreadItem(on_process) - ) - - def on_process(): - try: - self.log.debug( - "Processing pair: {}".format(str(self.current_pair)) - ) - result = self._process(*self.current_pair) - if result["error"] is not None: - self.log.debug("Error happened") - self.errored = True - - self.log.debug("Pair processed") - self.was_processed.emit(result) - - except Exception as exc: - self.log.warning( - "Unexpected exception during `on_process` happened", - exc_info=True - ) - exc_msg = str(exc) - self._main_thread_processor.add_item( - MainThreadItem(on_unexpected_error, error=exc_msg) - ) - return - - self._main_thread_processor.add_item( - MainThreadItem(on_next) - ) - - def on_unexpected_error(error): - # TODO this should be handled much differently - # TODO emit crash signal to show message box with traceback? - self.is_running = False - self.was_stopped.emit() - util.u_print(u"An unexpected error occurred:\n %s" % error) - if on_finished is not None: - self._main_thread_processor.add_item( - MainThreadItem(on_finished) - ) - self._main_thread_processor.stop_if_empty() - - self.is_running = True - self._main_thread_processor.add_item( - MainThreadItem(on_next) - ) - - def _set_state_by_order(self): - order = self.processing["current_group_order"] - self._current_state = self.order_groups.groups[order]["state"] - - def collect(self): - """ Iterate and process Collect plugins - - load_plugins method is launched again when finished - """ - self._set_state_by_order() - self._main_thread_processor.process(self._start_collect) - self._main_thread_processor.start() - - def validate(self): - """ Process plugins to validations_order value.""" - self._set_state_by_order() - self._main_thread_processor.process(self._start_validate) - self._main_thread_processor.start() - - def publish(self): - """ Iterate and process all remaining plugins.""" - self._set_state_by_order() - self._main_thread_processor.process(self._start_publish) - self._main_thread_processor.start() - - def _start_collect(self): - self.iterate_and_process() - - def _start_validate(self): - self.processing["stop_on_validation"] = True - self.iterate_and_process() - - def _start_publish(self): - self.processing["stop_on_validation"] = False - self.iterate_and_process(self.on_published) - - def cleanup(self): - """Forcefully delete objects from memory - In an ideal world, this shouldn't be necessary. Garbage - collection guarantees that anything without reference - is automatically removed. - However, because this application is designed to be run - multiple times from the same interpreter process, extra - case must be taken to ensure there are no memory leaks. - Explicitly deleting objects shines a light on where objects - may still be referenced in the form of an error. No errors - means this was unnecessary, but that's ok. - """ - - for instance in self.context: - del(instance) - - for plugin in self.plugins: - del(plugin) - - def _on_instance_toggled(self, instance, old_value, new_value): - callbacks = pyblish.api.registered_callbacks().get("instanceToggled") - if not callbacks: - return - - for callback in callbacks: - try: - callback(instance, old_value, new_value) - except Exception: - self.log.warning( - "Callback for `instanceToggled` crashed. {}".format( - os.path.abspath(inspect.getfile(callback)) - ), - exc_info=True - ) diff --git a/client/ayon_core/tools/pyblish_pype/delegate.py b/client/ayon_core/tools/pyblish_pype/delegate.py deleted file mode 100644 index bb253dd1a3..0000000000 --- a/client/ayon_core/tools/pyblish_pype/delegate.py +++ /dev/null @@ -1,540 +0,0 @@ -import platform - -from qtpy import QtWidgets, QtGui, QtCore - -from . import model -from .awesome import tags as awesome -from .constants import ( - PluginStates, InstanceStates, PluginActionStates, Roles, EXPANDER_WIDTH -) - -colors = { - "error": QtGui.QColor("#ff4a4a"), - "warning": QtGui.QColor("#ff9900"), - "ok": QtGui.QColor("#77AE24"), - "active": QtGui.QColor("#99CEEE"), - "idle": QtCore.Qt.white, - "inactive": QtGui.QColor("#888"), - "hover": QtGui.QColor(255, 255, 255, 10), - "selected": QtGui.QColor(255, 255, 255, 20), - "outline": QtGui.QColor("#333"), - "group": QtGui.QColor("#333"), - "group-hover": QtGui.QColor("#3c3c3c"), - "group-selected-hover": QtGui.QColor("#555555"), - "expander-bg": QtGui.QColor("#222"), - "expander-hover": QtGui.QColor("#2d6c9f"), - "expander-selected-hover": QtGui.QColor("#3784c5") -} - -scale_factors = {"darwin": 1.5} -scale_factor = scale_factors.get(platform.system().lower(), 1.0) -fonts = { - "h3": QtGui.QFont("Open Sans", 10 * scale_factor, QtGui.QFont.Normal), - "h4": QtGui.QFont("Open Sans", 8 * scale_factor, QtGui.QFont.Normal), - "h5": QtGui.QFont("Open Sans", 8 * scale_factor, QtGui.QFont.DemiBold), - "awesome6": QtGui.QFont("FontAwesome", 6 * scale_factor), - "awesome10": QtGui.QFont("FontAwesome", 10 * scale_factor), - "smallAwesome": QtGui.QFont("FontAwesome", 8 * scale_factor), - "largeAwesome": QtGui.QFont("FontAwesome", 16 * scale_factor), -} -font_metrics = { - "awesome6": QtGui.QFontMetrics(fonts["awesome6"]), - "h4": QtGui.QFontMetrics(fonts["h4"]), - "h5": QtGui.QFontMetrics(fonts["h5"]) -} -icons = { - "action": awesome["adn"], - "angle-right": awesome["angle-right"], - "angle-left": awesome["angle-left"], - "plus-sign": awesome['plus'], - "minus-sign": awesome['minus'] -} - - -class PluginItemDelegate(QtWidgets.QStyledItemDelegate): - """Generic delegate for model items""" - - def paint(self, painter, option, index): - """Paint checkbox and text. - _ - |_| My label > - """ - - body_rect = QtCore.QRectF(option.rect) - - check_rect = QtCore.QRectF(body_rect) - check_rect.setWidth(check_rect.height()) - check_offset = (check_rect.height() / 4) + 1 - check_rect.adjust( - check_offset, check_offset, -check_offset, -check_offset - ) - - check_color = colors["idle"] - - perspective_icon = icons["angle-right"] - perspective_rect = QtCore.QRectF(body_rect) - perspective_rect.setWidth(perspective_rect.height()) - perspective_rect.adjust(0, 3, 0, 0) - perspective_rect.translate( - body_rect.width() - (perspective_rect.width() / 2 + 2), - 0 - ) - - publish_states = index.data(Roles.PublishFlagsRole) - if publish_states & PluginStates.InProgress: - check_color = colors["active"] - - elif publish_states & PluginStates.HasError: - check_color = colors["error"] - - elif publish_states & PluginStates.HasWarning: - check_color = colors["warning"] - - elif publish_states & PluginStates.WasProcessed: - check_color = colors["ok"] - - elif not index.data(Roles.IsEnabledRole): - check_color = colors["inactive"] - - offset = (body_rect.height() - font_metrics["h4"].height()) / 2 - label_rect = QtCore.QRectF(body_rect.adjusted( - check_rect.width() + 12, offset - 1, 0, 0 - )) - - assert label_rect.width() > 0 - - label = index.data(QtCore.Qt.DisplayRole) - label = font_metrics["h4"].elidedText( - label, - QtCore.Qt.ElideRight, - label_rect.width() - 20 - ) - - font_color = colors["idle"] - if not index.data(QtCore.Qt.CheckStateRole): - font_color = colors["inactive"] - - # Maintain reference to state, so we can restore it once we're done - painter.save() - - # Draw perspective icon - painter.setFont(fonts["awesome10"]) - painter.setPen(QtGui.QPen(font_color)) - painter.drawText(perspective_rect, perspective_icon) - - # Draw label - painter.setFont(fonts["h4"]) - painter.setPen(QtGui.QPen(font_color)) - painter.drawText(label_rect, label) - - # Draw action icon - if index.data(Roles.PluginActionsVisibleRole): - painter.save() - action_state = index.data(Roles.PluginActionProgressRole) - if action_state & PluginActionStates.HasFailed: - color = colors["error"] - elif action_state & PluginActionStates.HasFinished: - color = colors["ok"] - elif action_state & PluginActionStates.InProgress: - color = colors["active"] - else: - color = colors["idle"] - - painter.setFont(fonts["smallAwesome"]) - painter.setPen(QtGui.QPen(color)) - - icon_rect = QtCore.QRectF( - option.rect.adjusted( - label_rect.width() - perspective_rect.width() / 2, - label_rect.height() / 3, 0, 0 - ) - ) - painter.drawText(icon_rect, icons["action"]) - - painter.restore() - - # Draw checkbox - pen = QtGui.QPen(check_color, 1) - painter.setPen(pen) - - if index.data(Roles.IsOptionalRole): - painter.drawRect(check_rect) - - if index.data(QtCore.Qt.CheckStateRole): - optional_check_rect = QtCore.QRectF(check_rect) - optional_check_rect.adjust(2, 2, -1, -1) - painter.fillRect(optional_check_rect, check_color) - - else: - painter.fillRect(check_rect, check_color) - - if option.state & QtWidgets.QStyle.State_MouseOver: - painter.fillRect(body_rect, colors["hover"]) - - if option.state & QtWidgets.QStyle.State_Selected: - painter.fillRect(body_rect, colors["selected"]) - - # Ok, we're done, tidy up. - painter.restore() - - def sizeHint(self, option, index): - return QtCore.QSize(option.rect.width(), 20) - - -class InstanceItemDelegate(QtWidgets.QStyledItemDelegate): - """Generic delegate for model items""" - - def paint(self, painter, option, index): - """Paint checkbox and text. - _ - |_| My label > - """ - - body_rect = QtCore.QRectF(option.rect) - - check_rect = QtCore.QRectF(body_rect) - check_rect.setWidth(check_rect.height()) - offset = (check_rect.height() / 4) + 1 - check_rect.adjust(offset, offset, -(offset), -(offset)) - - check_color = colors["idle"] - - perspective_icon = icons["angle-right"] - perspective_rect = QtCore.QRectF(body_rect) - perspective_rect.setWidth(perspective_rect.height()) - perspective_rect.adjust(0, 3, 0, 0) - perspective_rect.translate( - body_rect.width() - (perspective_rect.width() / 2 + 2), - 0 - ) - - publish_states = index.data(Roles.PublishFlagsRole) - if publish_states & InstanceStates.InProgress: - check_color = colors["active"] - - elif publish_states & InstanceStates.HasError: - check_color = colors["error"] - - elif publish_states & InstanceStates.HasWarning: - check_color = colors["warning"] - - elif publish_states & InstanceStates.HasFinished: - check_color = colors["ok"] - - elif not index.data(Roles.IsEnabledRole): - check_color = colors["inactive"] - - offset = (body_rect.height() - font_metrics["h4"].height()) / 2 - label_rect = QtCore.QRectF(body_rect.adjusted( - check_rect.width() + 12, offset - 1, 0, 0 - )) - - assert label_rect.width() > 0 - - label = index.data(QtCore.Qt.DisplayRole) - label = font_metrics["h4"].elidedText( - label, - QtCore.Qt.ElideRight, - label_rect.width() - 20 - ) - - font_color = colors["idle"] - if not index.data(QtCore.Qt.CheckStateRole): - font_color = colors["inactive"] - - # Maintain reference to state, so we can restore it once we're done - painter.save() - - # Draw perspective icon - painter.setFont(fonts["awesome10"]) - painter.setPen(QtGui.QPen(font_color)) - painter.drawText(perspective_rect, perspective_icon) - - # Draw label - painter.setFont(fonts["h4"]) - painter.setPen(QtGui.QPen(font_color)) - painter.drawText(label_rect, label) - - # Draw checkbox - pen = QtGui.QPen(check_color, 1) - painter.setPen(pen) - - if index.data(Roles.IsOptionalRole): - painter.drawRect(check_rect) - - if index.data(QtCore.Qt.CheckStateRole): - optional_check_rect = QtCore.QRectF(check_rect) - optional_check_rect.adjust(2, 2, -1, -1) - painter.fillRect(optional_check_rect, check_color) - - else: - painter.fillRect(check_rect, check_color) - - if option.state & QtWidgets.QStyle.State_MouseOver: - painter.fillRect(body_rect, colors["hover"]) - - if option.state & QtWidgets.QStyle.State_Selected: - painter.fillRect(body_rect, colors["selected"]) - - # Ok, we're done, tidy up. - painter.restore() - - def sizeHint(self, option, index): - return QtCore.QSize(option.rect.width(), 20) - - -class InstanceDelegate(QtWidgets.QStyledItemDelegate): - """Generic delegate for instance header""" - - radius = 8.0 - - def __init__(self, parent): - super(InstanceDelegate, self).__init__(parent) - self.item_delegate = InstanceItemDelegate(parent) - - def paint(self, painter, option, index): - if index.data(Roles.TypeRole) in ( - model.InstanceType, model.PluginType - ): - self.item_delegate.paint(painter, option, index) - return - - self.group_item_paint(painter, option, index) - - def group_item_paint(self, painter, option, index): - """Paint text - _ - My label - """ - body_rect = QtCore.QRectF(option.rect) - bg_rect = QtCore.QRectF( - body_rect.left(), body_rect.top() + 1, - body_rect.width() - 5, body_rect.height() - 2 - ) - - expander_rect = QtCore.QRectF(bg_rect) - expander_rect.setWidth(EXPANDER_WIDTH) - - remainder_rect = QtCore.QRectF( - expander_rect.x() + expander_rect.width(), - expander_rect.y(), - bg_rect.width() - expander_rect.width(), - expander_rect.height() - ) - - width = float(expander_rect.width()) - height = float(expander_rect.height()) - - x_pos = expander_rect.x() - y_pos = expander_rect.y() - - x_radius = min(self.radius, width / 2) - y_radius = min(self.radius, height / 2) - x_radius2 = x_radius * 2 - y_radius2 = y_radius * 2 - - expander_path = QtGui.QPainterPath() - expander_path.moveTo(x_pos, y_pos + y_radius) - expander_path.arcTo( - x_pos, y_pos, - x_radius2, y_radius2, - 180.0, -90.0 - ) - expander_path.lineTo(x_pos + width, y_pos) - expander_path.lineTo(x_pos + width, y_pos + height) - expander_path.lineTo(x_pos + x_radius, y_pos + height) - expander_path.arcTo( - x_pos, y_pos + height - y_radius2, - x_radius2, y_radius2, - 270.0, -90.0 - ) - expander_path.closeSubpath() - - width = float(remainder_rect.width()) - height = float(remainder_rect.height()) - x_pos = remainder_rect.x() - y_pos = remainder_rect.y() - - x_radius = min(self.radius, width / 2) - y_radius = min(self.radius, height / 2) - x_radius2 = x_radius * 2 - y_radius2 = y_radius * 2 - - remainder_path = QtGui.QPainterPath() - remainder_path.moveTo(x_pos + width, y_pos + height - y_radius) - remainder_path.arcTo( - x_pos + width - x_radius2, y_pos + height - y_radius2, - x_radius2, y_radius2, - 0.0, -90.0 - ) - remainder_path.lineTo(x_pos, y_pos + height) - remainder_path.lineTo(x_pos, y_pos) - remainder_path.lineTo(x_pos + width - x_radius, y_pos) - remainder_path.arcTo( - x_pos + width - x_radius2, y_pos, - x_radius2, y_radius2, - 90.0, -90.0 - ) - remainder_path.closeSubpath() - - painter.fillPath(expander_path, colors["expander-bg"]) - painter.fillPath(remainder_path, colors["group"]) - - mouse_pos = option.widget.mapFromGlobal(QtGui.QCursor.pos()) - selected = option.state & QtWidgets.QStyle.State_Selected - hovered = option.state & QtWidgets.QStyle.State_MouseOver - - if selected and hovered: - if expander_rect.contains(mouse_pos): - painter.fillPath( - expander_path, colors["expander-selected-hover"] - ) - else: - painter.fillPath( - remainder_path, colors["group-selected-hover"] - ) - - elif hovered: - if expander_rect.contains(mouse_pos): - painter.fillPath(expander_path, colors["expander-hover"]) - else: - painter.fillPath(remainder_path, colors["group-hover"]) - - text_height = font_metrics["awesome6"].height() - adjust_value = (expander_rect.height() - text_height) / 2 - expander_rect.adjust( - adjust_value + 1.5, adjust_value - 0.5, - -adjust_value + 1.5, -adjust_value - 0.5 - ) - - offset = (remainder_rect.height() - font_metrics["h5"].height()) / 2 - label_rect = QtCore.QRectF(remainder_rect.adjusted( - 5, offset - 1, 0, 0 - )) - - expander_icon = icons["plus-sign"] - - expanded = self.parent().isExpanded(index) - if expanded: - expander_icon = icons["minus-sign"] - label = index.data(QtCore.Qt.DisplayRole) - label = font_metrics["h5"].elidedText( - label, QtCore.Qt.ElideRight, label_rect.width() - ) - - # Maintain reference to state, so we can restore it once we're done - painter.save() - - painter.setFont(fonts["awesome6"]) - painter.setPen(QtGui.QPen(colors["idle"])) - painter.drawText(expander_rect, QtCore.Qt.AlignCenter, expander_icon) - - # Draw label - painter.setFont(fonts["h5"]) - painter.drawText(label_rect, label) - - # Ok, we're done, tidy up. - painter.restore() - - def sizeHint(self, option, index): - return QtCore.QSize(option.rect.width(), 20) - - -class PluginDelegate(QtWidgets.QStyledItemDelegate): - """Generic delegate for plugin header""" - - def __init__(self, parent): - super(PluginDelegate, self).__init__(parent) - self.item_delegate = PluginItemDelegate(parent) - - def paint(self, painter, option, index): - if index.data(Roles.TypeRole) in ( - model.InstanceType, model.PluginType - ): - self.item_delegate.paint(painter, option, index) - return - - self.group_item_paint(painter, option, index) - - def group_item_paint(self, painter, option, index): - """Paint text - _ - My label - """ - body_rect = QtCore.QRectF(option.rect) - bg_rect = QtCore.QRectF( - body_rect.left(), body_rect.top() + 1, - body_rect.width() - 5, body_rect.height() - 2 - ) - radius = 8.0 - bg_path = QtGui.QPainterPath() - bg_path.addRoundedRect(bg_rect, radius, radius) - hovered = option.state & QtWidgets.QStyle.State_MouseOver - selected = option.state & QtWidgets.QStyle.State_Selected - if hovered and selected: - painter.fillPath(bg_path, colors["group-selected-hover"]) - elif hovered: - painter.fillPath(bg_path, colors["group-hover"]) - else: - painter.fillPath(bg_path, colors["group"]) - - expander_rect = QtCore.QRectF(bg_rect) - expander_rect.setWidth(expander_rect.height()) - text_height = font_metrics["awesome6"].height() - adjust_value = (expander_rect.height() - text_height) / 2 - expander_rect.adjust( - adjust_value + 1.5, adjust_value - 0.5, - -adjust_value + 1.5, -adjust_value - 0.5 - ) - - offset = (bg_rect.height() - font_metrics["h5"].height()) / 2 - label_rect = QtCore.QRectF(bg_rect.adjusted( - expander_rect.width() + 12, offset - 1, 0, 0 - )) - - assert label_rect.width() > 0 - - expander_icon = icons["plus-sign"] - - expanded = self.parent().isExpanded(index) - if expanded: - expander_icon = icons["minus-sign"] - label = index.data(QtCore.Qt.DisplayRole) - label = font_metrics["h5"].elidedText( - label, QtCore.Qt.ElideRight, label_rect.width() - ) - - # Maintain reference to state, so we can restore it once we're done - painter.save() - - painter.setFont(fonts["awesome6"]) - painter.setPen(QtGui.QPen(colors["idle"])) - painter.drawText(expander_rect, QtCore.Qt.AlignCenter, expander_icon) - - # Draw label - painter.setFont(fonts["h5"]) - painter.drawText(label_rect, label) - - # Ok, we're done, tidy up. - painter.restore() - - def sizeHint(self, option, index): - return QtCore.QSize(option.rect.width(), 20) - - -class TerminalItem(QtWidgets.QStyledItemDelegate): - """Delegate used exclusively for the Terminal""" - - def paint(self, painter, option, index): - super(TerminalItem, self).paint(painter, option, index) - item_type = index.data(Roles.TypeRole) - if item_type == model.TerminalDetailType: - return - - hover = QtGui.QPainterPath() - hover.addRect(QtCore.QRectF(option.rect).adjusted(0, 0, -1, -1)) - if option.state & QtWidgets.QStyle.State_Selected: - painter.fillPath(hover, colors["selected"]) - - if option.state & QtWidgets.QStyle.State_MouseOver: - painter.fillPath(hover, colors["hover"]) diff --git a/client/ayon_core/tools/pyblish_pype/font/fontawesome/fontawesome-webfont.ttf b/client/ayon_core/tools/pyblish_pype/font/fontawesome/fontawesome-webfont.ttf deleted file mode 100644 index 9d02852c14..0000000000 Binary files a/client/ayon_core/tools/pyblish_pype/font/fontawesome/fontawesome-webfont.ttf and /dev/null differ diff --git a/client/ayon_core/tools/pyblish_pype/font/opensans/LICENSE.txt b/client/ayon_core/tools/pyblish_pype/font/opensans/LICENSE.txt deleted file mode 100644 index d645695673..0000000000 --- a/client/ayon_core/tools/pyblish_pype/font/opensans/LICENSE.txt +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-Bold.ttf b/client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-Bold.ttf deleted file mode 100644 index fd79d43bea..0000000000 Binary files a/client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-Bold.ttf and /dev/null differ diff --git a/client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-BoldItalic.ttf b/client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-BoldItalic.ttf deleted file mode 100644 index 9bc800958a..0000000000 Binary files a/client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-BoldItalic.ttf and /dev/null differ diff --git a/client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-ExtraBold.ttf b/client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-ExtraBold.ttf deleted file mode 100644 index 21f6f84a07..0000000000 Binary files a/client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-ExtraBold.ttf and /dev/null differ diff --git a/client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-ExtraBoldItalic.ttf b/client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-ExtraBoldItalic.ttf deleted file mode 100644 index 31cb688340..0000000000 Binary files a/client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-ExtraBoldItalic.ttf and /dev/null differ diff --git a/client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-Italic.ttf b/client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-Italic.ttf deleted file mode 100644 index c90da48ff3..0000000000 Binary files a/client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-Italic.ttf and /dev/null differ diff --git a/client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-Light.ttf b/client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-Light.ttf deleted file mode 100644 index 0d381897da..0000000000 Binary files a/client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-Light.ttf and /dev/null differ diff --git a/client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-LightItalic.ttf b/client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-LightItalic.ttf deleted file mode 100644 index 68299c4bc6..0000000000 Binary files a/client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-LightItalic.ttf and /dev/null differ diff --git a/client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-Regular.ttf b/client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-Regular.ttf deleted file mode 100644 index db433349b7..0000000000 Binary files a/client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-Regular.ttf and /dev/null differ diff --git a/client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-Semibold.ttf b/client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-Semibold.ttf deleted file mode 100644 index 1a7679e394..0000000000 Binary files a/client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-Semibold.ttf and /dev/null differ diff --git a/client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-SemiboldItalic.ttf b/client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-SemiboldItalic.ttf deleted file mode 100644 index 59b6d16b06..0000000000 Binary files a/client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-SemiboldItalic.ttf and /dev/null differ diff --git a/client/ayon_core/tools/pyblish_pype/i18n/pyblish_lite.pro b/client/ayon_core/tools/pyblish_pype/i18n/pyblish_lite.pro deleted file mode 100644 index c8e2a5b56f..0000000000 --- a/client/ayon_core/tools/pyblish_pype/i18n/pyblish_lite.pro +++ /dev/null @@ -1,2 +0,0 @@ -SOURCES = ../window.py -TRANSLATIONS = zh_CN.ts \ No newline at end of file diff --git a/client/ayon_core/tools/pyblish_pype/i18n/zh_CN.qm b/client/ayon_core/tools/pyblish_pype/i18n/zh_CN.qm deleted file mode 100644 index fed08d8a51..0000000000 Binary files a/client/ayon_core/tools/pyblish_pype/i18n/zh_CN.qm and /dev/null differ diff --git a/client/ayon_core/tools/pyblish_pype/i18n/zh_CN.ts b/client/ayon_core/tools/pyblish_pype/i18n/zh_CN.ts deleted file mode 100644 index 18ba81f69f..0000000000 --- a/client/ayon_core/tools/pyblish_pype/i18n/zh_CN.ts +++ /dev/null @@ -1,96 +0,0 @@ - - - - Window - - - Finishing up reset.. - 完成重置.. - - - - Comment.. - 备注.. - - - - Processing - 处理 - - - - Stopped due to error(s), see Terminal. - 因错误终止, 请查看终端。 - - - - Finished successfully! - 成功完成! - - - - About to reset.. - 即将重置.. - - - - Preparing validate.. - 准备校验.. - - - - Preparing publish.. - 准备发布.. - - - - Preparing - 准备 - - - - Action prepared. - 动作已就绪。 - - - - Cleaning up models.. - 清理数据模型.. - - - - Cleaning up terminal.. - 清理终端.. - - - - Cleaning up controller.. - 清理控制器.. - - - - All clean! - 清理完成! - - - - Good bye - 再见 - - - - ..as soon as processing is finished.. - ..处理即将完成.. - - - - Stopping.. - 正在停止.. - - - - Closing.. - 正在关闭.. - - - diff --git a/client/ayon_core/tools/pyblish_pype/img/down_arrow.png b/client/ayon_core/tools/pyblish_pype/img/down_arrow.png deleted file mode 100644 index e271f7f90b..0000000000 Binary files a/client/ayon_core/tools/pyblish_pype/img/down_arrow.png and /dev/null differ diff --git a/client/ayon_core/tools/pyblish_pype/img/logo-extrasmall.png b/client/ayon_core/tools/pyblish_pype/img/logo-extrasmall.png deleted file mode 100644 index ebe45c4c6e..0000000000 Binary files a/client/ayon_core/tools/pyblish_pype/img/logo-extrasmall.png and /dev/null differ diff --git a/client/ayon_core/tools/pyblish_pype/img/tab-overview.png b/client/ayon_core/tools/pyblish_pype/img/tab-overview.png deleted file mode 100644 index 443a750a7c..0000000000 Binary files a/client/ayon_core/tools/pyblish_pype/img/tab-overview.png and /dev/null differ diff --git a/client/ayon_core/tools/pyblish_pype/img/tab-terminal.png b/client/ayon_core/tools/pyblish_pype/img/tab-terminal.png deleted file mode 100644 index ea1bcff98d..0000000000 Binary files a/client/ayon_core/tools/pyblish_pype/img/tab-terminal.png and /dev/null differ diff --git a/client/ayon_core/tools/pyblish_pype/mock.py b/client/ayon_core/tools/pyblish_pype/mock.py deleted file mode 100644 index c85ff0f2ba..0000000000 --- a/client/ayon_core/tools/pyblish_pype/mock.py +++ /dev/null @@ -1,732 +0,0 @@ -import os -import time -import subprocess - -import pyblish.api - - -class MyAction(pyblish.api.Action): - label = "My Action" - on = "processed" - - def process(self, context, plugin): - self.log.info("Running!") - - -class MyOtherAction(pyblish.api.Action): - label = "My Other Action" - - def process(self, context, plugin): - self.log.info("Running!") - - -class CollectComment(pyblish.api.ContextPlugin): - """This collector has a very long comment. - - The idea is that this comment should either be elided, or word- - wrapped in the corresponding view. - - """ - - order = pyblish.api.CollectorOrder - - def process(self, context): - context.data["comment"] = "" - - -class MyCollector(pyblish.api.ContextPlugin): - label = "My Collector" - order = pyblish.api.CollectorOrder - - def process(self, context): - context.create_instance("MyInstance 1", families=["myFamily"]) - context.create_instance("MyInstance 2", families=["myFamily 2"]) - context.create_instance( - "MyInstance 3", - families=["myFamily 2"], - publish=False - ) - - -class MyValidator(pyblish.api.InstancePlugin): - order = pyblish.api.ValidatorOrder - active = False - label = "My Validator" - actions = [MyAction, - MyOtherAction] - - def process(self, instance): - self.log.info("Validating: %s" % instance) - - -class MyExtractor(pyblish.api.InstancePlugin): - order = pyblish.api.ExtractorOrder - families = ["myFamily"] - label = "My Extractor" - - def process(self, instance): - self.log.info("Extracting: %s" % instance) - - -class CollectRenamed(pyblish.api.Collector): - def process(self, context): - i = context.create_instance("MyInstanceXYZ", family="MyFamily") - i.set_data("name", "My instance") - - -class CollectNegatron(pyblish.api.Collector): - """Negative collector adds Negatron""" - - order = pyblish.api.Collector.order - 0.49 - - def process_context(self, context): - self.log.info("Collecting Negatron") - context.create_instance("Negatron", family="MyFamily") - - -class CollectPositron(pyblish.api.Collector): - """Positive collector adds Positron""" - - order = pyblish.api.Collector.order + 0.49 - - def process_context(self, context): - self.log.info("Collecting Positron") - context.create_instance("Positron", family="MyFamily") - - -class SelectInstances(pyblish.api.Selector): - """Select debugging instances - - These instances are part of the evil plan to destroy the world. - Be weary, be vigilant, be sexy. - - """ - - def process_context(self, context): - self.log.info("Selecting instances..") - - for instance in instances[:-1]: - name, data = instance["name"], instance["data"] - self.log.info("Selecting: %s" % name) - instance = context.create_instance(name) - - for key, value in data.items(): - instance.set_data(key, value) - - -class SelectDiInstances(pyblish.api.Selector): - """Select DI instances""" - - name = "Select Dependency Instances" - - def process(self, context): - name, data = instances[-1]["name"], instances[-1]["data"] - self.log.info("Selecting: %s" % name) - instance = context.create_instance(name) - - for key, value in data.items(): - instance.set_data(key, value) - - -class SelectInstancesFailure(pyblish.api.Selector): - """Select some instances, but fail before adding anything to the context. - - That's right. I'm programmed to fail. Try me. - - """ - - __fail__ = True - - def process_context(self, context): - self.log.warning("I'm about to fail") - raise AssertionError("I was programmed to fail") - - -class SelectInstances2(pyblish.api.Selector): - def process(self, context): - self.log.warning("I'm good") - - -class ValidateNamespace(pyblish.api.Validator): - """Namespaces must be orange - - In case a namespace is not orange, report immediately to - your officer in charge, ask for a refund, do a backflip. - - This has been an example of: - - - A long doc-string - - With a list - - And plenty of newlines and tabs. - - """ - - families = ["B"] - - def process(self, instance): - self.log.info("Validating the namespace of %s" % instance.data("name")) - self.log.info("""And here's another message, quite long, in fact it's -too long to be displayed in a single row of text. -But that's how we roll down here. It's got \nnew lines\nas well. - -- And lists -- And more lists - - """) - - -class ValidateContext(pyblish.api.Validator): - families = ["A", "B"] - - def process_context(self, context): - self.log.info("Processing context..") - - -class ValidateContextFailure(pyblish.api.Validator): - optional = True - families = ["C"] - __fail__ = True - - def process_context(self, context): - self.log.info("About to fail..") - raise AssertionError("""I was programmed to fail - -The reason I failed was because the sun was not aligned with the tides, -and the moon is gray; not yellow. Try again when the moon is yellow.""") - - -class Validator1(pyblish.api.Validator): - """Test of the order attribute""" - order = pyblish.api.Validator.order + 0.1 - families = ["A"] - - def process_instance(self, instance): - pass - - -class Validator2(pyblish.api.Validator): - order = pyblish.api.Validator.order + 0.2 - families = ["B"] - - def process_instance(self, instance): - pass - - -class Validator3(pyblish.api.Validator): - order = pyblish.api.Validator.order + 0.3 - families = ["B"] - - def process_instance(self, instance): - pass - - -class ValidateFailureMock(pyblish.api.Validator): - """Plug-in that always fails""" - optional = True - order = pyblish.api.Validator.order + 0.1 - families = ["C"] - __fail__ = True - - def process_instance(self, instance): - self.log.debug("e = mc^2") - self.log.info("About to fail..") - self.log.warning("Failing.. soooon..") - self.log.critical("Ok, you're done.") - raise AssertionError("""ValidateFailureMock was destined to fail.. - -Here's some extended information about what went wrong. - -It has quite the long string associated with it, including -a few newlines and a list. - -- Item 1 -- Item 2 - -""") - - -class ValidateIsIncompatible(pyblish.api.Validator): - """This plug-in should never appear..""" - requires = False # This is invalid - - -class ValidateWithRepair(pyblish.api.Validator): - """A validator with repair functionality""" - optional = True - families = ["C"] - __fail__ = True - - def process_instance(self, instance): - raise AssertionError( - "%s is invalid, try repairing it!" % instance.name - ) - - def repair_instance(self, instance): - self.log.info("Attempting to repair..") - self.log.info("Success!") - - -class ValidateWithRepairFailure(pyblish.api.Validator): - """A validator with repair functionality that fails""" - optional = True - families = ["C"] - __fail__ = True - - def process_instance(self, instance): - raise AssertionError( - "%s is invalid, try repairing it!" % instance.name - ) - - def repair_instance(self, instance): - self.log.info("Attempting to repair..") - raise AssertionError("Could not repair due to X") - - -class ValidateWithVeryVeryVeryLongLongNaaaaame(pyblish.api.Validator): - """A validator with repair functionality that fails""" - families = ["A"] - - -class ValidateWithRepairContext(pyblish.api.Validator): - """A validator with repair functionality that fails""" - optional = True - families = ["C"] - __fail__ = True - - def process_context(self, context): - raise AssertionError("Could not validate context, try repairing it") - - def repair_context(self, context): - self.log.info("Attempting to repair..") - raise AssertionError("Could not repair") - - -class ExtractAsMa(pyblish.api.Extractor): - """Extract contents of each instance into .ma - - Serialise scene using Maya's own facilities and then put - it on the hard-disk. Once complete, this plug-in relies - on a Conformer to put it in it's final location, as this - extractor merely positions it in the users local temp- - directory. - - """ - - optional = True - __expected__ = { - "logCount": ">=4" - } - - def process_instance(self, instance): - self.log.info("About to extract scene to .ma..") - self.log.info("Extraction went well, now verifying the data..") - - if instance.name == "Richard05": - self.log.warning("You're almost running out of disk space!") - - self.log.info("About to finish up") - self.log.info("Finished successfully") - - -class ConformAsset(pyblish.api.Conformer): - """Conform the world - - Step 1: Conform all humans and Step 2: Conform all non-humans. - Once conforming has completed, rinse and repeat. - - """ - - optional = True - - def process_instance(self, instance): - self.log.info("About to conform all humans..") - - if instance.name == "Richard05": - self.log.warning("Richard05 is a conformist!") - - self.log.info("About to conform all non-humans..") - self.log.info("Conformed Successfully") - - -class ValidateInstancesDI(pyblish.api.Validator): - """Validate using the DI interface""" - families = ["diFamily"] - - def process(self, instance): - self.log.info("Validating %s.." % instance.data("name")) - - -class ValidateDIWithRepair(pyblish.api.Validator): - families = ["diFamily"] - optional = True - __fail__ = True - - def process(self, instance): - raise AssertionError("I was programmed to fail, for repair") - - def repair(self, instance): - self.log.info("Repairing %s" % instance.data("name")) - - -class ExtractInstancesDI(pyblish.api.Extractor): - """Extract using the DI interface""" - families = ["diFamily"] - - def process(self, instance): - self.log.info("Extracting %s.." % instance.data("name")) - - -class ValidateWithLabel(pyblish.api.Validator): - """Validate using the DI interface""" - label = "Validate with Label" - - -class ValidateWithLongLabel(pyblish.api.Validator): - """Validate using the DI interface""" - label = "Validate with Loooooooooooooooooooooong Label" - - -class SimplePlugin1(pyblish.api.Plugin): - """Validate using the simple-plugin interface""" - - def process(self): - self.log.info("I'm a simple plug-in, only processed once") - - -class SimplePlugin2(pyblish.api.Plugin): - """Validate using the simple-plugin interface - - It doesn't have an order, and will likely end up *before* all - other plug-ins. (due to how sorted([1, 2, 3, None]) works) - - """ - - def process(self, context): - self.log.info("Processing the context, simply: %s" % context) - - -class SimplePlugin3(pyblish.api.Plugin): - """Simply process every instance""" - - def process(self, instance): - self.log.info("Processing the instance, simply: %s" % instance) - - -class ContextAction(pyblish.api.Action): - label = "Context action" - - def process(self, context): - self.log.info("I have access to the context") - self.log.info("Context.instances: %s" % str(list(context))) - - -class FailingAction(pyblish.api.Action): - label = "Failing action" - - def process(self): - self.log.info("About to fail..") - raise Exception("I failed") - - -class LongRunningAction(pyblish.api.Action): - label = "Long-running action" - - def process(self): - self.log.info("Sleeping for 2 seconds..") - time.sleep(2) - self.log.info("Ah, that's better") - - -class IconAction(pyblish.api.Action): - label = "Icon action" - icon = "crop" - - def process(self): - self.log.info("I have an icon") - - -class PluginAction(pyblish.api.Action): - label = "Plugin action" - - def process(self, plugin): - self.log.info("I have access to my parent plug-in") - self.log.info("Which is %s" % plugin.id) - - -class LaunchExplorerAction(pyblish.api.Action): - label = "Open in Explorer" - icon = "folder-open" - - def process(self, context): - cwd = os.getcwd() - self.log.info("Opening %s in Explorer" % cwd) - result = subprocess.call("start .", cwd=cwd, shell=True) - self.log.debug(result) - - -class ProcessedAction(pyblish.api.Action): - label = "Success action" - icon = "check" - on = "processed" - - def process(self): - self.log.info("I am only available on a successful plug-in") - - -class FailedAction(pyblish.api.Action): - label = "Failure action" - icon = "close" - on = "failed" - - -class SucceededAction(pyblish.api.Action): - label = "Success action" - icon = "check" - on = "succeeded" - - def process(self): - self.log.info("I am only available on a successful plug-in") - - -class LongLabelAction(pyblish.api.Action): - label = "An incredibly, incredicly looooon label. Very long." - icon = "close" - - -class BadEventAction(pyblish.api.Action): - label = "Bad event action" - on = "not exist" - - -class InactiveAction(pyblish.api.Action): - active = False - - -class PluginWithActions(pyblish.api.Validator): - optional = True - actions = [ - pyblish.api.Category("General"), - ContextAction, - FailingAction, - LongRunningAction, - IconAction, - PluginAction, - pyblish.api.Category("Empty"), - pyblish.api.Category("OS"), - LaunchExplorerAction, - pyblish.api.Separator, - FailedAction, - SucceededAction, - pyblish.api.Category("Debug"), - BadEventAction, - InactiveAction, - LongLabelAction, - pyblish.api.Category("Empty"), - ] - - def process(self): - self.log.info("Ran PluginWithActions") - - -class FailingPluginWithActions(pyblish.api.Validator): - optional = True - actions = [ - FailedAction, - SucceededAction, - ] - - def process(self): - raise Exception("I was programmed to fail") - - -class ValidateDefaultOff(pyblish.api.Validator): - families = ["A", "B"] - active = False - optional = True - - def process(self, instance): - self.log.info("Processing instance..") - - -class ValidateWithHyperlinks(pyblish.api.Validator): - """To learn about Pyblish - - click here (http://pyblish.com) - - """ - - families = ["A", "B"] - - def process(self, instance): - self.log.info("Processing instance..") - - msg = "To learn about Pyblish, " - msg += "click here (http://pyblish.com)" - - self.log.info(msg) - - -class LongRunningCollector(pyblish.api.Collector): - """I will take at least 2 seconds...""" - def process(self, context): - self.log.info("Sleeping for 2 seconds..") - time.sleep(2) - self.log.info("Good morning") - - -class LongRunningValidator(pyblish.api.Validator): - """I will take at least 2 seconds...""" - def process(self, context): - self.log.info("Sleeping for 2 seconds..") - time.sleep(2) - self.log.info("Good morning") - - -class RearrangingPlugin(pyblish.api.ContextPlugin): - """Sort plug-ins by family, and then reverse it""" - order = pyblish.api.CollectorOrder + 0.2 - - def process(self, context): - self.log.info("Reversing instances in the context..") - context[:] = sorted( - context, - key=lambda i: i.data["family"], - reverse=True - ) - self.log.info("Reversed!") - - -class InactiveInstanceCollectorPlugin(pyblish.api.InstancePlugin): - """Special case of an InstancePlugin running as a Collector""" - order = pyblish.api.CollectorOrder + 0.1 - active = False - - def process(self, instance): - raise TypeError("I shouldn't have run in the first place") - - -class CollectWithIcon(pyblish.api.ContextPlugin): - order = pyblish.api.CollectorOrder - - def process(self, context): - instance = context.create_instance("With Icon") - instance.data["icon"] = "play" - - -instances = [ - { - "name": "Peter01", - "data": { - "family": "A", - "publish": False - } - }, - { - "name": "Richard05", - "data": { - "family": "A", - } - }, - { - "name": "Steven11", - "data": { - "family": "B", - } - }, - { - "name": "Piraya12", - "data": { - "family": "B", - } - }, - { - "name": "Marcus", - "data": { - "family": "C", - } - }, - { - "name": "Extra1", - "data": { - "family": "C", - } - }, - { - "name": "DependencyInstance", - "data": { - "family": "diFamily" - } - }, - { - "name": "NoFamily", - "data": {} - }, - { - "name": "Failure 1", - "data": { - "family": "failure", - "fail": False - } - }, - { - "name": "Failure 2", - "data": { - "family": "failure", - "fail": True - } - } -] - -plugins = [ - MyCollector, - MyValidator, - MyExtractor, - - CollectRenamed, - CollectNegatron, - CollectPositron, - SelectInstances, - SelectInstances2, - SelectDiInstances, - SelectInstancesFailure, - ValidateFailureMock, - ValidateNamespace, - # ValidateIsIncompatible, - ValidateWithVeryVeryVeryLongLongNaaaaame, - ValidateContext, - ValidateContextFailure, - Validator1, - Validator2, - Validator3, - ValidateWithRepair, - ValidateWithRepairFailure, - ValidateWithRepairContext, - ValidateWithLabel, - ValidateWithLongLabel, - ValidateDefaultOff, - ValidateWithHyperlinks, - ExtractAsMa, - ConformAsset, - - SimplePlugin1, - SimplePlugin2, - SimplePlugin3, - - ValidateInstancesDI, - ExtractInstancesDI, - ValidateDIWithRepair, - - PluginWithActions, - FailingPluginWithActions, - - # LongRunningCollector, - # LongRunningValidator, - - RearrangingPlugin, - InactiveInstanceCollectorPlugin, - - CollectComment, - CollectWithIcon, -] - -pyblish.api.sort_plugins(plugins) diff --git a/client/ayon_core/tools/pyblish_pype/model.py b/client/ayon_core/tools/pyblish_pype/model.py deleted file mode 100644 index 44f951fe14..0000000000 --- a/client/ayon_core/tools/pyblish_pype/model.py +++ /dev/null @@ -1,1116 +0,0 @@ -"""Qt models - -Description: - The model contains the original objects from Pyblish, such as - pyblish.api.Instance and pyblish.api.Plugin. The model then - provides an interface for reading and writing to those. - -GUI data: - Aside from original data, such as pyblish.api.Plugin.optional, - the GUI also hosts data internal to itself, such as whether or - not an item has processed such that it may be colored appropriately - in the view. This data is prefixed with two underscores (__). - - E.g. - - _has_processed - - This is so that the the GUI-only data doesn't accidentally overwrite - or cause confusion with existing data in plug-ins and instances. - -Roles: - Data is accessed via standard Qt "roles". You can think of a role - as the key of a dictionary, except they can only be integers. - -""" -from __future__ import unicode_literals - -import pyblish - -from . import settings, util -from .awesome import tags as awesome -from qtpy import QtCore, QtGui -import qtawesome -from .constants import PluginStates, InstanceStates, GroupStates, Roles - - -# ItemTypes -UserType = QtGui.QStandardItem.UserType -if hasattr(UserType, "value"): - UserType = UserType.value -InstanceType = UserType -PluginType = UserType + 1 -GroupType = UserType + 2 -TerminalLabelType = UserType + 3 -TerminalDetailType = UserType + 4 - - -class QAwesomeTextIconFactory: - icons = {} - - @classmethod - def icon(cls, icon_name): - if icon_name not in cls.icons: - cls.icons[icon_name] = awesome.get(icon_name) - return cls.icons[icon_name] - - -class QAwesomeIconFactory: - icons = {} - - @classmethod - def icon(cls, icon_name, icon_color): - if icon_name not in cls.icons: - cls.icons[icon_name] = {} - - if icon_color not in cls.icons[icon_name]: - cls.icons[icon_name][icon_color] = qtawesome.icon( - icon_name, - color=icon_color - ) - return cls.icons[icon_name][icon_color] - - -class IntentModel(QtGui.QStandardItemModel): - """Model for QComboBox with intents. - - It is expected that one inserted item is dictionary. - Key represents #Label and Value represent #Value. - - Example: - { - "Testing": "test", - "Publishing": "publish" - } - - First and default value is {"< Not Set >": None} - """ - - default_empty_label = "< Not set >" - - def __init__(self, parent=None): - super(IntentModel, self).__init__(parent) - self._item_count = 0 - self.default_index = 0 - - @property - def has_items(self): - return self._item_count > 0 - - def reset(self): - self.clear() - self._item_count = 0 - self.default_index = 0 - - # Intent settings are not available in core addon - intent_settings = {} - - items = intent_settings.get("items", {}) - if not items: - return - - allow_empty_intent = intent_settings.get("allow_empty_intent", True) - empty_intent_label = ( - intent_settings.get("empty_intent_label") - or self.default_empty_label - ) - listed_items = list(items.items()) - if allow_empty_intent: - listed_items.insert(0, ("", empty_intent_label)) - - default = intent_settings.get("default") - - for idx, item in enumerate(listed_items): - item_value = item[0] - if item_value == default: - self.default_index = idx - break - - self._add_items(listed_items) - - def _add_items(self, items): - for item in items: - value, label = item - new_item = QtGui.QStandardItem() - new_item.setData(label, QtCore.Qt.DisplayRole) - new_item.setData(value, Roles.IntentItemValue) - - self.setItem(self._item_count, new_item) - self._item_count += 1 - - -class PluginItem(QtGui.QStandardItem): - """Plugin item implementation.""" - - def __init__(self, plugin): - super(PluginItem, self).__init__() - - item_text = plugin.__name__ - if settings.UseLabel: - if hasattr(plugin, "label") and plugin.label: - item_text = plugin.label - - self.plugin = plugin - - self.setData(item_text, QtCore.Qt.DisplayRole) - self.setData(False, Roles.IsEnabledRole) - self.setData(0, Roles.PublishFlagsRole) - self.setData(0, Roles.PluginActionProgressRole) - icon_name = "" - if hasattr(plugin, "icon") and plugin.icon: - icon_name = plugin.icon - icon = QAwesomeTextIconFactory.icon(icon_name) - self.setData(icon, QtCore.Qt.DecorationRole) - - actions = [] - if hasattr(plugin, "actions") and plugin.actions: - actions = list(plugin.actions) - plugin.actions = actions - - is_checked = True - is_optional = getattr(plugin, "optional", False) - if is_optional: - is_checked = getattr(plugin, "active", True) - - plugin.active = is_checked - plugin.optional = is_optional - - self.setData( - "{}.{}".format(plugin.__module__, plugin.__name__), - Roles.ObjectUIdRole - ) - - self.setFlags( - QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled - ) - - def type(self): - return PluginType - - def data(self, role=QtCore.Qt.DisplayRole): - if role == Roles.IsOptionalRole: - return self.plugin.optional - - if role == Roles.ObjectIdRole: - return self.plugin.id - - if role == Roles.TypeRole: - return self.type() - - if role == QtCore.Qt.CheckStateRole: - return self.plugin.active - - if role == Roles.PathModuleRole: - return self.plugin.__module__ - - if role == Roles.FamiliesRole: - return self.plugin.families - - if role == Roles.DocstringRole: - return self.plugin.__doc__ - - if role == Roles.PluginActionsVisibleRole: - return self._data_actions_visible() - - if role == Roles.PluginValidActionsRole: - return self._data_valid_actions() - - return super(PluginItem, self).data(role) - - def _data_actions_visible(self): - # Can only run actions on active plug-ins. - if not self.plugin.active or not self.plugin.actions: - return False - - publish_states = self.data(Roles.PublishFlagsRole) - if ( - not publish_states & PluginStates.IsCompatible - or publish_states & PluginStates.WasSkipped - ): - return False - - # Context specific actions - for action in self.plugin.actions: - if action.on == "failed": - if publish_states & PluginStates.HasError: - return True - - elif action.on == "succeeded": - if ( - publish_states & PluginStates.WasProcessed - and not publish_states & PluginStates.HasError - ): - return True - - elif action.on == "processed": - if publish_states & PluginStates.WasProcessed: - return True - - elif action.on == "notProcessed": - if not publish_states & PluginStates.WasProcessed: - return True - return False - - def _data_valid_actions(self): - valid_actions = [] - - # Can only run actions on active plug-ins. - if not self.plugin.active or not self.plugin.actions: - return valid_actions - - if not self.plugin.active or not self.plugin.actions: - return False - - publish_states = self.data(Roles.PublishFlagsRole) - if ( - not publish_states & PluginStates.IsCompatible - or publish_states & PluginStates.WasSkipped - ): - return False - - # Context specific actions - for action in self.plugin.actions: - valid = False - if action.on == "failed": - if publish_states & PluginStates.HasError: - valid = True - - elif action.on == "succeeded": - if ( - publish_states & PluginStates.WasProcessed - and not publish_states & PluginStates.HasError - ): - valid = True - - elif action.on == "processed": - if publish_states & PluginStates.WasProcessed: - valid = True - - elif action.on == "notProcessed": - if not publish_states & PluginStates.WasProcessed: - valid = True - - if valid: - valid_actions.append(action) - - if not valid_actions: - return valid_actions - - actions_len = len(valid_actions) - # Discard empty groups - indexex_to_remove = [] - for idx, action in enumerate(valid_actions): - if action.__type__ != "category": - continue - - next_id = idx + 1 - if next_id >= actions_len: - indexex_to_remove.append(idx) - continue - - next = valid_actions[next_id] - if next.__type__ != "action": - indexex_to_remove.append(idx) - - for idx in reversed(indexex_to_remove): - valid_actions.pop(idx) - - return valid_actions - - def setData(self, value, role=None): - if role is None: - role = QtCore.Qt.UserRole + 1 - - if role == QtCore.Qt.CheckStateRole: - if not self.data(Roles.IsEnabledRole): - return False - self.plugin.active = value - self.emitDataChanged() - return - - elif role == Roles.PluginActionProgressRole: - if isinstance(value, list): - _value = self.data(Roles.PluginActionProgressRole) - for flag in value: - _value |= flag - value = _value - - elif isinstance(value, dict): - _value = self.data(Roles.PluginActionProgressRole) - for flag, _bool in value.items(): - if _bool is True: - _value |= flag - elif _value & flag: - _value ^= flag - value = _value - - elif role == Roles.PublishFlagsRole: - if isinstance(value, list): - _value = self.data(Roles.PublishFlagsRole) - for flag in value: - _value |= flag - value = _value - - elif isinstance(value, dict): - _value = self.data(Roles.PublishFlagsRole) - for flag, _bool in value.items(): - if _bool is True: - _value |= flag - elif _value & flag: - _value ^= flag - value = _value - - if value & PluginStates.HasWarning: - if self.parent(): - self.parent().setData( - {GroupStates.HasWarning: True}, - Roles.PublishFlagsRole - ) - if value & PluginStates.HasError: - if self.parent(): - self.parent().setData( - {GroupStates.HasError: True}, - Roles.PublishFlagsRole - ) - - return super(PluginItem, self).setData(value, role) - - -class GroupItem(QtGui.QStandardItem): - def __init__(self, *args, **kwargs): - self.order = kwargs.pop("order", None) - self.publish_states = 0 - super(GroupItem, self).__init__(*args, **kwargs) - - def flags(self): - return QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled - - def data(self, role=QtCore.Qt.DisplayRole): - if role == Roles.PublishFlagsRole: - return self.publish_states - - if role == Roles.TypeRole: - return self.type() - - return super(GroupItem, self).data(role) - - def setData(self, value, role=(QtCore.Qt.UserRole + 1)): - if role == Roles.PublishFlagsRole: - if isinstance(value, list): - _value = self.data(Roles.PublishFlagsRole) - for flag in value: - _value |= flag - value = _value - - elif isinstance(value, dict): - _value = self.data(Roles.PublishFlagsRole) - for flag, _bool in value.items(): - if _bool is True: - _value |= flag - elif _value & flag: - _value ^= flag - value = _value - self.publish_states = value - self.emitDataChanged() - return True - - return super(GroupItem, self).setData(value, role) - - def type(self): - return GroupType - - -class PluginModel(QtGui.QStandardItemModel): - def __init__(self, controller, *args, **kwargs): - super(PluginModel, self).__init__(*args, **kwargs) - - self.controller = controller - self.checkstates = {} - self.group_items = {} - self.plugin_items = {} - - def reset(self): - self.group_items = {} - self.plugin_items = {} - self.clear() - - def append(self, plugin): - plugin_groups = self.controller.order_groups.groups - label = None - order = None - for _order, item in reversed(plugin_groups.items()): - if _order is None or plugin.order < _order: - label = item["label"] - order = _order - else: - break - - if label is None: - label = "Other" - - group_item = self.group_items.get(label) - if not group_item: - group_item = GroupItem(label, order=order) - self.appendRow(group_item) - self.group_items[label] = group_item - - new_item = PluginItem(plugin) - group_item.appendRow(new_item) - - self.plugin_items[plugin._id] = new_item - - def store_checkstates(self): - self.checkstates.clear() - - for plugin_item in self.plugin_items.values(): - if not plugin_item.plugin.optional: - continue - - uid = plugin_item.data(Roles.ObjectUIdRole) - self.checkstates[uid] = plugin_item.data(QtCore.Qt.CheckStateRole) - - def restore_checkstates(self): - for plugin_item in self.plugin_items.values(): - if not plugin_item.plugin.optional: - continue - - uid = plugin_item.data(Roles.ObjectUIdRole) - state = self.checkstates.get(uid) - if state is not None: - plugin_item.setData(state, QtCore.Qt.CheckStateRole) - - def update_with_result(self, result): - plugin = result["plugin"] - item = self.plugin_items[plugin.id] - - new_flag_states = { - PluginStates.InProgress: False, - PluginStates.WasProcessed: True - } - - publish_states = item.data(Roles.PublishFlagsRole) - - has_warning = publish_states & PluginStates.HasWarning - new_records = result.get("records") or [] - if not has_warning: - for record in new_records: - level_no = record.get("levelno") - if level_no and level_no >= 30: - new_flag_states[PluginStates.HasWarning] = True - break - - if ( - not publish_states & PluginStates.HasError - and not result["success"] - ): - new_flag_states[PluginStates.HasError] = True - - if not publish_states & PluginStates.IsCompatible: - new_flag_states[PluginStates.IsCompatible] = True - - item.setData(new_flag_states, Roles.PublishFlagsRole) - - records = item.data(Roles.LogRecordsRole) or [] - records.extend(new_records) - - item.setData(records, Roles.LogRecordsRole) - - return item - - def update_compatibility(self): - context = self.controller.context - - families = util.collect_families_from_instances(context, True) - for plugin_item in self.plugin_items.values(): - publish_states = plugin_item.data(Roles.PublishFlagsRole) - if ( - publish_states & PluginStates.WasProcessed - or publish_states & PluginStates.WasSkipped - ): - continue - - is_compatible = False - # A plugin should always show if it has processed. - if plugin_item.plugin.__instanceEnabled__: - compatible_instances = pyblish.logic.instances_by_plugin( - context, plugin_item.plugin - ) - for instance in context: - if not instance.data.get("publish"): - continue - - if instance in compatible_instances: - is_compatible = True - break - else: - plugins = pyblish.logic.plugins_by_families( - [plugin_item.plugin], families - ) - if plugins: - is_compatible = True - - current_is_compatible = publish_states & PluginStates.IsCompatible - if ( - (is_compatible and not current_is_compatible) - or (not is_compatible and current_is_compatible) - ): - new_flag = { - PluginStates.IsCompatible: is_compatible - } - plugin_item.setData(new_flag, Roles.PublishFlagsRole) - - -class PluginFilterProxy(QtCore.QSortFilterProxyModel): - def filterAcceptsRow(self, source_row, source_parent): - index = self.sourceModel().index(source_row, 0, source_parent) - item_type = index.data(Roles.TypeRole) - if item_type != PluginType: - return True - - publish_states = index.data(Roles.PublishFlagsRole) - if ( - publish_states & PluginStates.WasSkipped - or not publish_states & PluginStates.IsCompatible - ): - return False - return True - - -class InstanceItem(QtGui.QStandardItem): - """Instance item implementation.""" - - def __init__(self, instance): - super(InstanceItem, self).__init__() - - self.instance = instance - self.is_context = False - publish_states = getattr(instance, "_publish_states", 0) - if publish_states & InstanceStates.ContextType: - self.is_context = True - - instance._publish_states = publish_states - instance._logs = [] - instance.optional = getattr(instance, "optional", True) - instance.data["publish"] = instance.data.get("publish", True) - - family = self.data(Roles.FamiliesRole)[0] - self.setData( - "{}.{}".format(family, self.instance.data["name"]), - Roles.ObjectUIdRole - ) - - def flags(self): - return QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled - - def type(self): - return InstanceType - - def data(self, role=QtCore.Qt.DisplayRole): - if role == QtCore.Qt.DisplayRole: - label = None - if settings.UseLabel: - label = self.instance.data.get("label") - - if not label: - if self.is_context: - label = "Context" - else: - label = self.instance.data["name"] - return label - - if role == QtCore.Qt.DecorationRole: - icon_name = self.instance.data.get("icon") or "file" - return QAwesomeTextIconFactory.icon(icon_name) - - if role == Roles.TypeRole: - return self.type() - - if role == Roles.ObjectIdRole: - return self.instance.id - - if role == Roles.FamiliesRole: - if self.is_context: - return ["Context"] - - families = [] - family = self.instance.data.get("family") - if family: - families.append(family) - - _families = self.instance.data.get("families") or [] - for _family in _families: - if _family not in families: - families.append(_family) - - return families - - if role == Roles.IsOptionalRole: - return self.instance.optional - - if role == QtCore.Qt.CheckStateRole: - return self.instance.data["publish"] - - if role == Roles.PublishFlagsRole: - return self.instance._publish_states - - if role == Roles.LogRecordsRole: - return self.instance._logs - - return super(InstanceItem, self).data(role) - - def setData(self, value, role=(QtCore.Qt.UserRole + 1)): - if role == QtCore.Qt.CheckStateRole: - if not self.data(Roles.IsEnabledRole): - return - self.instance.data["publish"] = value - self.emitDataChanged() - return - - if role == Roles.IsEnabledRole: - if not self.instance.optional: - return - - if role == Roles.PublishFlagsRole: - if isinstance(value, list): - _value = self.instance._publish_states - for flag in value: - _value |= flag - value = _value - - elif isinstance(value, dict): - _value = self.instance._publish_states - for flag, _bool in value.items(): - if _bool is True: - _value |= flag - elif _value & flag: - _value ^= flag - value = _value - - if value & InstanceStates.HasWarning: - if self.parent(): - self.parent().setData( - {GroupStates.HasWarning: True}, - Roles.PublishFlagsRole - ) - if value & InstanceStates.HasError: - if self.parent(): - self.parent().setData( - {GroupStates.HasError: True}, - Roles.PublishFlagsRole - ) - - self.instance._publish_states = value - self.emitDataChanged() - return - - if role == Roles.LogRecordsRole: - self.instance._logs = value - self.emitDataChanged() - return - - return super(InstanceItem, self).setData(value, role) - - -class InstanceModel(QtGui.QStandardItemModel): - - group_created = QtCore.Signal(QtCore.QModelIndex) - - def __init__(self, controller, *args, **kwargs): - super(InstanceModel, self).__init__(*args, **kwargs) - - self.controller = controller - self.checkstates = {} - self.group_items = {} - self.instance_items = {} - - def reset(self): - self.group_items = {} - self.instance_items = {} - self.clear() - - def append(self, instance): - new_item = InstanceItem(instance) - if new_item.is_context: - self.appendRow(new_item) - else: - families = new_item.data(Roles.FamiliesRole) - group_item = self.group_items.get(families[0]) - if not group_item: - group_item = GroupItem(families[0]) - self.appendRow(group_item) - self.group_items[families[0]] = group_item - self.group_created.emit(group_item.index()) - - group_item.appendRow(new_item) - instance_id = instance.id - self.instance_items[instance_id] = new_item - - def remove(self, instance_id): - instance_item = self.instance_items.pop(instance_id) - parent_item = instance_item.parent() - parent_item.removeRow(instance_item.row()) - if parent_item.rowCount(): - return - - self.group_items.pop(parent_item.data(QtCore.Qt.DisplayRole)) - self.removeRow(parent_item.row()) - - def store_checkstates(self): - self.checkstates.clear() - - for instance_item in self.instance_items.values(): - if not instance_item.instance.optional: - continue - - uid = instance_item.data(Roles.ObjectUIdRole) - self.checkstates[uid] = instance_item.data( - QtCore.Qt.CheckStateRole - ) - - def restore_checkstates(self): - for instance_item in self.instance_items.values(): - if not instance_item.instance.optional: - continue - - uid = instance_item.data(Roles.ObjectUIdRole) - state = self.checkstates.get(uid) - if state is not None: - instance_item.setData(state, QtCore.Qt.CheckStateRole) - - def update_with_result(self, result): - instance = result["instance"] - if instance is None: - instance_id = self.controller.context.id - else: - instance_id = instance.id - - item = self.instance_items.get(instance_id) - if not item: - return - - new_flag_states = { - InstanceStates.InProgress: False - } - - publish_states = item.data(Roles.PublishFlagsRole) - has_warning = publish_states & InstanceStates.HasWarning - new_records = result.get("records") or [] - if not has_warning: - for record in new_records: - level_no = record.get("levelno") - if level_no and level_no >= 30: - new_flag_states[InstanceStates.HasWarning] = True - break - - if ( - not publish_states & InstanceStates.HasError - and not result["success"] - ): - new_flag_states[InstanceStates.HasError] = True - - item.setData(new_flag_states, Roles.PublishFlagsRole) - - records = item.data(Roles.LogRecordsRole) or [] - records.extend(new_records) - - item.setData(records, Roles.LogRecordsRole) - - return item - - def update_compatibility(self, context, instances): - families = util.collect_families_from_instances(context, True) - for plugin_item in self.plugin_items.values(): - publish_states = plugin_item.data(Roles.PublishFlagsRole) - if ( - publish_states & PluginStates.WasProcessed - or publish_states & PluginStates.WasSkipped - ): - continue - - is_compatible = False - # A plugin should always show if it has processed. - if plugin_item.plugin.__instanceEnabled__: - compatibleInstances = pyblish.logic.instances_by_plugin( - context, plugin_item.plugin - ) - for instance in instances: - if not instance.data.get("publish"): - continue - - if instance in compatibleInstances: - is_compatible = True - break - else: - plugins = pyblish.logic.plugins_by_families( - [plugin_item.plugin], families - ) - if plugins: - is_compatible = True - - current_is_compatible = publish_states & PluginStates.IsCompatible - if ( - (is_compatible and not current_is_compatible) - or (not is_compatible and current_is_compatible) - ): - plugin_item.setData( - {PluginStates.IsCompatible: is_compatible}, - Roles.PublishFlagsRole - ) - - -class InstanceSortProxy(QtCore.QSortFilterProxyModel): - def __init__(self, *args, **kwargs): - super(InstanceSortProxy, self).__init__(*args, **kwargs) - # Do not care about lower/upper case - self.setSortCaseSensitivity(QtCore.Qt.CaseInsensitive) - - def lessThan(self, x_index, y_index): - x_type = x_index.data(Roles.TypeRole) - y_type = y_index.data(Roles.TypeRole) - if x_type != y_type: - if x_type == GroupType: - return False - return True - return super(InstanceSortProxy, self).lessThan(x_index, y_index) - - -class TerminalDetailItem(QtGui.QStandardItem): - key_label_record_map = ( - ("instance", "Instance"), - ("msg", "Message"), - ("name", "Plugin"), - ("pathname", "Path"), - ("lineno", "Line"), - ("traceback", "Traceback"), - ("levelname", "Level"), - ("threadName", "Thread"), - ("msecs", "Millis") - ) - - def __init__(self, record_item): - self.record_item = record_item - self.msg = None - msg = record_item.get("msg") - if msg is None: - msg = record_item["label"].split("\n")[0] - - super(TerminalDetailItem, self).__init__(msg) - - def data(self, role=QtCore.Qt.DisplayRole): - if role in (QtCore.Qt.DisplayRole, QtCore.Qt.EditRole): - if self.msg is None: - self.msg = self.compute_detail_text(self.record_item) - return self.msg - return super(TerminalDetailItem, self).data(role) - - def compute_detail_text(self, item_data): - if item_data["type"] == "info": - return item_data["label"] - - html_text = "" - for key, title in self.key_label_record_map: - if key not in item_data: - continue - value = item_data[key] - text = ( - str(value) - .replace("<", "<") - .replace(">", ">") - .replace('\n', '
') - .replace(' ', ' ') - ) - - title_tag = ( - '{}: ' - ' color:#fff;\" >{}: ' - ).format(title) - - html_text += ( - '{}' - '{}' - ).format(title_tag, text) - - html_text = '{}
'.format( - html_text - ) - return html_text - - -class TerminalModel(QtGui.QStandardItemModel): - item_icon_name = { - "info": "fa.info", - "record": "fa.circle", - "error": "fa.exclamation-triangle", - } - - item_icon_colors = { - "info": "#ffffff", - "error": "#ff4a4a", - "log_debug": "#ff66e8", - "log_info": "#66abff", - "log_warning": "#ffba66", - "log_error": "#ff4d58", - "log_critical": "#ff4f75", - None: "#333333" - } - - level_to_record = ( - (10, "log_debug"), - (20, "log_info"), - (30, "log_warning"), - (40, "log_error"), - (50, "log_critical") - - ) - - def __init__(self, *args, **kwargs): - super(TerminalModel, self).__init__(*args, **kwargs) - self.reset() - - def reset(self): - self.clear() - - def prepare_records(self, result, suspend_logs): - prepared_records = [] - instance_name = None - instance = result["instance"] - if instance is not None: - instance_name = instance.data["name"] - - if not suspend_logs: - for record in result.get("records") or []: - if isinstance(record, dict): - record_item = record - else: - record_item = { - "label": str(record.msg), - "type": "record", - "levelno": record.levelno, - "threadName": record.threadName, - "name": record.name, - "filename": record.filename, - "pathname": record.pathname, - "lineno": record.lineno, - "msg": str(record.msg), - "msecs": record.msecs, - "levelname": record.levelname - } - - if instance_name is not None: - record_item["instance"] = instance_name - - prepared_records.append(record_item) - - error = result.get("error") - if error: - fname, line_no, func, exc = error.traceback - error_item = { - "label": str(error), - "type": "error", - "filename": str(fname), - "lineno": str(line_no), - "func": str(func), - "traceback": error.formatted_traceback, - } - - if instance_name is not None: - error_item["instance"] = instance_name - - prepared_records.append(error_item) - - return prepared_records - - def append(self, record_items): - all_record_items = [] - for record_item in record_items: - record_type = record_item["type"] - # Add error message to detail - if record_type == "error": - record_item["msg"] = record_item["label"] - terminal_item_type = None - if record_type == "record": - for level, _type in self.level_to_record: - if level > record_item["levelno"]: - break - terminal_item_type = _type - - else: - terminal_item_type = record_type - - icon_color = self.item_icon_colors.get(terminal_item_type) - icon_name = self.item_icon_name.get(record_type) - - top_item_icon = None - if icon_color and icon_name: - top_item_icon = QAwesomeIconFactory.icon(icon_name, icon_color) - - label = record_item["label"].split("\n")[0] - - top_item = QtGui.QStandardItem() - all_record_items.append(top_item) - - detail_item = TerminalDetailItem(record_item) - top_item.appendRow(detail_item) - - top_item.setData(TerminalLabelType, Roles.TypeRole) - top_item.setData(terminal_item_type, Roles.TerminalItemTypeRole) - top_item.setData(label, QtCore.Qt.DisplayRole) - top_item.setFlags( - QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled - ) - - if top_item_icon: - top_item.setData(top_item_icon, QtCore.Qt.DecorationRole) - - detail_item.setData(TerminalDetailType, Roles.TypeRole) - - self.invisibleRootItem().appendRows(all_record_items) - - def update_with_result(self, result): - self.append(result["records"]) - - -class TerminalProxy(QtCore.QSortFilterProxyModel): - filter_buttons_checks = { - "info": settings.TerminalFilters.get("info", True), - "log_debug": settings.TerminalFilters.get("log_debug", True), - "log_info": settings.TerminalFilters.get("log_info", True), - "log_warning": settings.TerminalFilters.get("log_warning", True), - "log_error": settings.TerminalFilters.get("log_error", True), - "log_critical": settings.TerminalFilters.get("log_critical", True), - "error": settings.TerminalFilters.get("error", True) - } - - instances = [] - - def __init__(self, view, *args, **kwargs): - super(self.__class__, self).__init__(*args, **kwargs) - self.__class__.instances.append(self) - # Store parent because by own `QSortFilterProxyModel` has `parent` - # method not returning parent QObject in PySide and PyQt4 - self.view = view - - @classmethod - def change_filter(cls, name, value): - cls.filter_buttons_checks[name] = value - - for instance in cls.instances: - try: - instance.invalidate() - if instance.view: - instance.view.updateGeometry() - - except RuntimeError: - # C++ Object was deleted - cls.instances.remove(instance) - - def filterAcceptsRow(self, source_row, source_parent): - index = self.sourceModel().index(source_row, 0, source_parent) - item_type = index.data(Roles.TypeRole) - if not item_type == TerminalLabelType: - return True - terminal_item_type = index.data(Roles.TerminalItemTypeRole) - return self.__class__.filter_buttons_checks.get( - terminal_item_type, True - ) diff --git a/client/ayon_core/tools/pyblish_pype/settings.py b/client/ayon_core/tools/pyblish_pype/settings.py deleted file mode 100644 index 5b69eb6a50..0000000000 --- a/client/ayon_core/tools/pyblish_pype/settings.py +++ /dev/null @@ -1,30 +0,0 @@ -from .util import env_variable_to_bool - -# Customize the window of the pyblish-lite window. -WindowTitle = "Pyblish" - -# Customize whether to show label names for plugins. -UseLabel = True - -# Customize which tab to start on. Possible choices are: "artist", "overview" -# and "terminal". -InitialTab = "overview" - -# Customize the window size. -WindowSize = (430, 600) - -TerminalFilters = { - "info": True, - "log_debug": True, - "log_info": True, - "log_warning": True, - "log_error": True, - "log_critical": True, - "traceback": True, -} - -# Allow animations in GUI -Animated = env_variable_to_bool("AYON_PYBLISH_ANIMATED", True) - -# Print UI info message to console -PrintInfo = env_variable_to_bool("AYON_PYBLISH_PRINT_INFO", True) diff --git a/client/ayon_core/tools/pyblish_pype/util.py b/client/ayon_core/tools/pyblish_pype/util.py deleted file mode 100644 index 081f7775d5..0000000000 --- a/client/ayon_core/tools/pyblish_pype/util.py +++ /dev/null @@ -1,144 +0,0 @@ -from __future__ import ( - absolute_import, - division, - print_function, - unicode_literals -) - -import os -import sys -import collections - -from qtpy import QtCore -import pyblish.api - -root = os.path.dirname(__file__) - - -def get_asset(*path): - """Return path to asset, relative the install directory - - Usage: - >>> path = get_asset("dir", "to", "asset.png") - >>> path == os.path.join(root, "dir", "to", "asset.png") - True - - Arguments: - path (str): One or more paths, to be concatenated - - """ - - return os.path.join(root, *path) - - -def defer(delay, func): - """Append artificial delay to `func` - - This aids in keeping the GUI responsive, but complicates logic - when producing tests. To combat this, the environment variable ensures - that every operation is synchronous. - - Arguments: - delay (float): Delay multiplier; default 1, 0 means no delay - func (callable): Any callable - - """ - - delay *= float(os.getenv("PYBLISH_DELAY", 1)) - if delay > 0: - return QtCore.QTimer.singleShot(delay, func) - else: - return func() - - -def u_print(msg, **kwargs): - """`print` with encoded unicode. - - `print` unicode may cause UnicodeEncodeError - or non-readable result when `PYTHONIOENCODING` is not set. - this will fix it. - - Arguments: - msg (unicode): Message to print. - **kwargs: Keyword argument for `print` function. - """ - - if isinstance(msg, str): - encoding = None - try: - encoding = os.getenv('PYTHONIOENCODING', sys.stdout.encoding) - except AttributeError: - # `sys.stdout.encoding` may not exists. - pass - msg = msg.encode(encoding or 'utf-8', 'replace') - print(msg, **kwargs) - - -def collect_families_from_instances(instances, only_active=False): - all_families = set() - for instance in instances: - if only_active: - if instance.data.get("publish") is False: - continue - family = instance.data.get("family") - if family: - all_families.add(family) - - families = instance.data.get("families") or tuple() - for family in families: - all_families.add(family) - - return list(all_families) - - -class OrderGroups: - validation_order = pyblish.api.ValidatorOrder + 0.5 - groups = collections.OrderedDict(( - ( - pyblish.api.CollectorOrder + 0.5, - { - "label": "Collect", - "state": "Collecting.." - } - ), - ( - pyblish.api.ValidatorOrder + 0.5, - { - "label": "Validate", - "state": "Validating.." - } - ), - ( - pyblish.api.ExtractorOrder + 0.5, - { - "label": "Extract", - "state": "Extracting.." - } - ), - ( - pyblish.api.IntegratorOrder + 0.5, - { - "label": "Integrate", - "state": "Integrating.." - } - ), - ( - None, - { - "label": "Other", - "state": "Finishing.." - } - ) - )) - - -def env_variable_to_bool(env_key, default=False): - """Boolean based on environment variable value.""" - value = os.environ.get(env_key) - if value is not None: - value = value.lower() - if value in ("true", "1", "yes", "on"): - return True - elif value in ("false", "0", "no", "off"): - return False - return default diff --git a/client/ayon_core/tools/pyblish_pype/vendor/__init__.py b/client/ayon_core/tools/pyblish_pype/vendor/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/client/ayon_core/tools/pyblish_pype/vendor/qtawesome/__init__.py b/client/ayon_core/tools/pyblish_pype/vendor/qtawesome/__init__.py deleted file mode 100644 index 4a0001ebb7..0000000000 --- a/client/ayon_core/tools/pyblish_pype/vendor/qtawesome/__init__.py +++ /dev/null @@ -1,39 +0,0 @@ -""" -qtawesome - use font-awesome in PyQt / PySide applications - -This is a port to Python of the C++ QtAwesome library by Rick Blommers -""" -from .iconic_font import IconicFont, set_global_defaults -from .animation import Pulse, Spin -from ._version import version_info, __version__ - -_resource = {'iconic': None, } - - -def _instance(): - if _resource['iconic'] is None: - _resource['iconic'] = IconicFont(('fa', 'fontawesome-webfont.ttf', 'fontawesome-webfont-charmap.json'), - ('ei', 'elusiveicons-webfont.ttf', 'elusiveicons-webfont-charmap.json')) - return _resource['iconic'] - - -def icon(*args, **kwargs): - return _instance().icon(*args, **kwargs) - - -def load_font(*args, **kwargs): - return _instance().load_font(*args, **kwargs) - - -def charmap(prefixed_name): - prefix, name = prefixed_name.split('.') - return _instance().charmap[prefix][name] - - -def font(*args, **kwargs): - return _instance().font(*args, **kwargs) - - -def set_defaults(**kwargs): - return set_global_defaults(**kwargs) - diff --git a/client/ayon_core/tools/pyblish_pype/vendor/qtawesome/_version.py b/client/ayon_core/tools/pyblish_pype/vendor/qtawesome/_version.py deleted file mode 100644 index 7af886d1a0..0000000000 --- a/client/ayon_core/tools/pyblish_pype/vendor/qtawesome/_version.py +++ /dev/null @@ -1,2 +0,0 @@ -version_info = (0, 3, 0, 'dev') -__version__ = '.'.join(map(str, version_info)) diff --git a/client/ayon_core/tools/pyblish_pype/vendor/qtawesome/animation.py b/client/ayon_core/tools/pyblish_pype/vendor/qtawesome/animation.py deleted file mode 100644 index ac69507444..0000000000 --- a/client/ayon_core/tools/pyblish_pype/vendor/qtawesome/animation.py +++ /dev/null @@ -1,41 +0,0 @@ -from qtpy import QtCore - - -class Spin: - - def __init__(self, parent_widget, interval=10, step=1): - self.parent_widget = parent_widget - self.interval, self.step = interval, step - self.info = {} - - def _update(self, parent_widget): - if self.parent_widget in self.info: - timer, angle, step = self.info[self.parent_widget] - - if angle >= 360: - angle = 0 - - angle += step - self.info[parent_widget] = timer, angle, step - parent_widget.update() - - def setup(self, icon_painter, painter, rect): - - if self.parent_widget not in self.info: - timer = QtCore.QTimer() - timer.timeout.connect(lambda: self._update(self.parent_widget)) - self.info[self.parent_widget] = [timer, 0, self.step] - timer.start(self.interval) - else: - timer, angle, self.step = self.info[self.parent_widget] - x_center = rect.width() * 0.5 - y_center = rect.height() * 0.5 - painter.translate(x_center, y_center) - painter.rotate(angle) - painter.translate(-x_center, -y_center) - - -class Pulse(Spin): - - def __init__(self, parent_widget): - Spin.__init__(self, parent_widget, interval=300, step=45) diff --git a/client/ayon_core/tools/pyblish_pype/vendor/qtawesome/fonts/elusiveicons-webfont-charmap.json b/client/ayon_core/tools/pyblish_pype/vendor/qtawesome/fonts/elusiveicons-webfont-charmap.json deleted file mode 100644 index 099bcb818c..0000000000 --- a/client/ayon_core/tools/pyblish_pype/vendor/qtawesome/fonts/elusiveicons-webfont-charmap.json +++ /dev/null @@ -1,306 +0,0 @@ -{ - "address-book": "0xf102", - "address-book-alt": "0xf101", - "adjust": "0xf104", - "adjust-alt": "0xf103", - "adult": "0xf105", - "align-center": "0xf106", - "align-justify": "0xf107", - "align-left": "0xf108", - "align-right": "0xf109", - "arrow-down": "0xf10a", - "arrow-left": "0xf10b", - "arrow-right": "0xf10c", - "arrow-up": "0xf10d", - "asl": "0xf10e", - "asterisk": "0xf10f", - "backward": "0xf110", - "ban-circle": "0xf111", - "barcode": "0xf112", - "behance": "0xf113", - "bell": "0xf114", - "blind": "0xf115", - "blogger": "0xf116", - "bold": "0xf117", - "book": "0xf118", - "bookmark": "0xf11a", - "bookmark-empty": "0xf119", - "braille": "0xf11b", - "briefcase": "0xf11c", - "broom": "0xf11d", - "brush": "0xf11e", - "bulb": "0xf11f", - "bullhorn": "0xf120", - "calendar": "0xf122", - "calendar-sign": "0xf121", - "camera": "0xf123", - "car": "0xf124", - "caret-down": "0xf125", - "caret-left": "0xf126", - "caret-right": "0xf127", - "caret-up": "0xf128", - "cc": "0xf129", - "certificate": "0xf12a", - "check": "0xf12c", - "check-empty": "0xf12b", - "chevron-down": "0xf12d", - "chevron-left": "0xf12e", - "chevron-right": "0xf12f", - "chevron-up": "0xf130", - "child": "0xf131", - "circle-arrow-down": "0xf132", - "circle-arrow-left": "0xf133", - "circle-arrow-right": "0xf134", - "circle-arrow-up": "0xf135", - "cloud": "0xf137", - "cloud-alt": "0xf136", - "cog": "0xf139", - "cog-alt": "0xf138", - "cogs": "0xf13a", - "comment": "0xf13c", - "comment-alt": "0xf13b", - "compass": "0xf13e", - "compass-alt": "0xf13d", - "credit-card": "0xf13f", - "css": "0xf140", - "dashboard": "0xf141", - "delicious": "0xf142", - "deviantart": "0xf143", - "digg": "0xf144", - "download": "0xf146", - "download-alt": "0xf145", - "dribbble": "0xf147", - "edit": "0xf148", - "eject": "0xf149", - "envelope": "0xf14b", - "envelope-alt": "0xf14a", - "error": "0xf14d", - "error-alt": "0xf14c", - "eur": "0xf14e", - "exclamation-sign": "0xf14f", - "eye-close": "0xf150", - "eye-open": "0xf151", - "facebook": "0xf152", - "facetime-video": "0xf153", - "fast-backward": "0xf154", - "fast-forward": "0xf155", - "female": "0xf156", - "file": "0xf15c", - "file-alt": "0xf157", - "file-edit": "0xf159", - "file-edit-alt": "0xf158", - "file-new": "0xf15b", - "file-new-alt": "0xf15a", - "film": "0xf15d", - "filter": "0xf15e", - "fire": "0xf15f", - "flag": "0xf161", - "flag-alt": "0xf160", - "flickr": "0xf162", - "folder": "0xf166", - "folder-close": "0xf163", - "folder-open": "0xf164", - "folder-sign": "0xf165", - "font": "0xf167", - "fontsize": "0xf168", - "fork": "0xf169", - "forward": "0xf16b", - "forward-alt": "0xf16a", - "foursquare": "0xf16c", - "friendfeed": "0xf16e", - "friendfeed-rect": "0xf16d", - "fullscreen": "0xf16f", - "gbp": "0xf170", - "gift": "0xf171", - "github": "0xf173", - "github-text": "0xf172", - "glass": "0xf174", - "glasses": "0xf175", - "globe": "0xf177", - "globe-alt": "0xf176", - "googleplus": "0xf178", - "graph": "0xf17a", - "graph-alt": "0xf179", - "group": "0xf17c", - "group-alt": "0xf17b", - "guidedog": "0xf17d", - "hand-down": "0xf17e", - "hand-left": "0xf17f", - "hand-right": "0xf180", - "hand-up": "0xf181", - "hdd": "0xf182", - "headphones": "0xf183", - "hearing-impaired": "0xf184", - "heart": "0xf187", - "heart-alt": "0xf185", - "heart-empty": "0xf186", - "home": "0xf189", - "home-alt": "0xf188", - "hourglass": "0xf18a", - "idea": "0xf18c", - "idea-alt": "0xf18b", - "inbox": "0xf18f", - "inbox-alt": "0xf18d", - "inbox-box": "0xf18e", - "indent-left": "0xf190", - "indent-right": "0xf191", - "info-circle": "0xf192", - "instagram": "0xf193", - "iphone-home": "0xf194", - "italic": "0xf195", - "key": "0xf196", - "laptop": "0xf198", - "laptop-alt": "0xf197", - "lastfm": "0xf199", - "leaf": "0xf19a", - "lines": "0xf19b", - "link": "0xf19c", - "linkedin": "0xf19d", - "list": "0xf19f", - "list-alt": "0xf19e", - "livejournal": "0xf1a0", - "lock": "0xf1a2", - "lock-alt": "0xf1a1", - "magic": "0xf1a3", - "magnet": "0xf1a4", - "male": "0xf1a5", - "map-marker": "0xf1a7", - "map-marker-alt": "0xf1a6", - "mic": "0xf1a9", - "mic-alt": "0xf1a8", - "minus": "0xf1ab", - "minus-sign": "0xf1aa", - "move": "0xf1ac", - "music": "0xf1ad", - "myspace": "0xf1ae", - "network": "0xf1af", - "off": "0xf1b0", - "ok": "0xf1b3", - "ok-circle": "0xf1b1", - "ok-sign": "0xf1b2", - "opensource": "0xf1b4", - "paper-clip": "0xf1b6", - "paper-clip-alt": "0xf1b5", - "path": "0xf1b7", - "pause": "0xf1b9", - "pause-alt": "0xf1b8", - "pencil": "0xf1bb", - "pencil-alt": "0xf1ba", - "person": "0xf1bc", - "phone": "0xf1be", - "phone-alt": "0xf1bd", - "photo": "0xf1c0", - "photo-alt": "0xf1bf", - "picasa": "0xf1c1", - "picture": "0xf1c2", - "pinterest": "0xf1c3", - "plane": "0xf1c4", - "play": "0xf1c7", - "play-alt": "0xf1c5", - "play-circle": "0xf1c6", - "plurk": "0xf1c9", - "plurk-alt": "0xf1c8", - "plus": "0xf1cb", - "plus-sign": "0xf1ca", - "podcast": "0xf1cc", - "print": "0xf1cd", - "puzzle": "0xf1ce", - "qrcode": "0xf1cf", - "question": "0xf1d1", - "question-sign": "0xf1d0", - "quote-alt": "0xf1d2", - "quote-right": "0xf1d4", - "quote-right-alt": "0xf1d3", - "quotes": "0xf1d5", - "random": "0xf1d6", - "record": "0xf1d7", - "reddit": "0xf1d8", - "redux": "0xf1d9", - "refresh": "0xf1da", - "remove": "0xf1dd", - "remove-circle": "0xf1db", - "remove-sign": "0xf1dc", - "repeat": "0xf1df", - "repeat-alt": "0xf1de", - "resize-full": "0xf1e0", - "resize-horizontal": "0xf1e1", - "resize-small": "0xf1e2", - "resize-vertical": "0xf1e3", - "return-key": "0xf1e4", - "retweet": "0xf1e5", - "reverse-alt": "0xf1e6", - "road": "0xf1e7", - "rss": "0xf1e8", - "scissors": "0xf1e9", - "screen": "0xf1eb", - "screen-alt": "0xf1ea", - "screenshot": "0xf1ec", - "search": "0xf1ee", - "search-alt": "0xf1ed", - "share": "0xf1f0", - "share-alt": "0xf1ef", - "shopping-cart": "0xf1f2", - "shopping-cart-sign": "0xf1f1", - "signal": "0xf1f3", - "skype": "0xf1f4", - "slideshare": "0xf1f5", - "smiley": "0xf1f7", - "smiley-alt": "0xf1f6", - "soundcloud": "0xf1f8", - "speaker": "0xf1f9", - "spotify": "0xf1fa", - "stackoverflow": "0xf1fb", - "star": "0xf1fe", - "star-alt": "0xf1fc", - "star-empty": "0xf1fd", - "step-backward": "0xf1ff", - "step-forward": "0xf200", - "stop": "0xf202", - "stop-alt": "0xf201", - "stumbleupon": "0xf203", - "tag": "0xf204", - "tags": "0xf205", - "tasks": "0xf206", - "text-height": "0xf207", - "text-width": "0xf208", - "th": "0xf20b", - "th-large": "0xf209", - "th-list": "0xf20a", - "thumbs-down": "0xf20c", - "thumbs-up": "0xf20d", - "time": "0xf20f", - "time-alt": "0xf20e", - "tint": "0xf210", - "torso": "0xf211", - "trash": "0xf213", - "trash-alt": "0xf212", - "tumblr": "0xf214", - "twitter": "0xf215", - "universal-access": "0xf216", - "unlock": "0xf218", - "unlock-alt": "0xf217", - "upload": "0xf219", - "usd": "0xf21a", - "user": "0xf21b", - "viadeo": "0xf21c", - "video": "0xf21f", - "video-alt": "0xf21d", - "video-chat": "0xf21e", - "view-mode": "0xf220", - "vimeo": "0xf221", - "vkontakte": "0xf222", - "volume-down": "0xf223", - "volume-off": "0xf224", - "volume-up": "0xf225", - "w3c": "0xf226", - "warning-sign": "0xf227", - "website": "0xf229", - "website-alt": "0xf228", - "wheelchair": "0xf22a", - "wordpress": "0xf22b", - "wrench": "0xf22d", - "wrench-alt": "0xf22c", - "youtube": "0xf22e", - "zoom-in": "0xf22f", - "zoom-out": "0xf230" -} diff --git a/client/ayon_core/tools/pyblish_pype/vendor/qtawesome/fonts/elusiveicons-webfont.ttf b/client/ayon_core/tools/pyblish_pype/vendor/qtawesome/fonts/elusiveicons-webfont.ttf deleted file mode 100644 index b6fe85d4b2..0000000000 Binary files a/client/ayon_core/tools/pyblish_pype/vendor/qtawesome/fonts/elusiveicons-webfont.ttf and /dev/null differ diff --git a/client/ayon_core/tools/pyblish_pype/vendor/qtawesome/fonts/fontawesome-webfont-charmap.json b/client/ayon_core/tools/pyblish_pype/vendor/qtawesome/fonts/fontawesome-webfont-charmap.json deleted file mode 100644 index 0e97d031e6..0000000000 --- a/client/ayon_core/tools/pyblish_pype/vendor/qtawesome/fonts/fontawesome-webfont-charmap.json +++ /dev/null @@ -1,696 +0,0 @@ -{ - "500px": "f26e", - "adjust": "f042", - "adn": "f170", - "align-center": "f037", - "align-justify": "f039", - "align-left": "f036", - "align-right": "f038", - "amazon": "f270", - "ambulance": "f0f9", - "anchor": "f13d", - "android": "f17b", - "angellist": "f209", - "angle-double-down": "f103", - "angle-double-left": "f100", - "angle-double-right": "f101", - "angle-double-up": "f102", - "angle-down": "f107", - "angle-left": "f104", - "angle-right": "f105", - "angle-up": "f106", - "apple": "f179", - "archive": "f187", - "area-chart": "f1fe", - "arrow-circle-down": "f0ab", - "arrow-circle-left": "f0a8", - "arrow-circle-o-down": "f01a", - "arrow-circle-o-left": "f190", - "arrow-circle-o-right": "f18e", - "arrow-circle-o-up": "f01b", - "arrow-circle-right": "f0a9", - "arrow-circle-up": "f0aa", - "arrow-down": "f063", - "arrow-left": "f060", - "arrow-right": "f061", - "arrow-up": "f062", - "arrows": "f047", - "arrows-alt": "f0b2", - "arrows-h": "f07e", - "arrows-v": "f07d", - "asterisk": "f069", - "at": "f1fa", - "automobile": "f1b9", - "backward": "f04a", - "balance-scale": "f24e", - "ban": "f05e", - "bank": "f19c", - "bar-chart": "f080", - "bar-chart-o": "f080", - "barcode": "f02a", - "bars": "f0c9", - "battery-0": "f244", - "battery-1": "f243", - "battery-2": "f242", - "battery-3": "f241", - "battery-4": "f240", - "battery-empty": "f244", - "battery-full": "f240", - "battery-half": "f242", - "battery-quarter": "f243", - "battery-three-quarters": "f241", - "bed": "f236", - "beer": "f0fc", - "behance": "f1b4", - "behance-square": "f1b5", - "bell": "f0f3", - "bell-o": "f0a2", - "bell-slash": "f1f6", - "bell-slash-o": "f1f7", - "bicycle": "f206", - "binoculars": "f1e5", - "birthday-cake": "f1fd", - "bitbucket": "f171", - "bitbucket-square": "f172", - "bitcoin": "f15a", - "black-tie": "f27e", - "bluetooth": "f293", - "bluetooth-b": "f294", - "bold": "f032", - "bolt": "f0e7", - "bomb": "f1e2", - "book": "f02d", - "bookmark": "f02e", - "bookmark-o": "f097", - "briefcase": "f0b1", - "btc": "f15a", - "bug": "f188", - "building": "f1ad", - "building-o": "f0f7", - "bullhorn": "f0a1", - "bullseye": "f140", - "bus": "f207", - "buysellads": "f20d", - "cab": "f1ba", - "calculator": "f1ec", - "calendar": "f073", - "calendar-check-o": "f274", - "calendar-minus-o": "f272", - "calendar-o": "f133", - "calendar-plus-o": "f271", - "calendar-times-o": "f273", - "camera": "f030", - "camera-retro": "f083", - "car": "f1b9", - "caret-down": "f0d7", - "caret-left": "f0d9", - "caret-right": "f0da", - "caret-square-o-down": "f150", - "caret-square-o-left": "f191", - "caret-square-o-right": "f152", - "caret-square-o-up": "f151", - "caret-up": "f0d8", - "cart-arrow-down": "f218", - "cart-plus": "f217", - "cc": "f20a", - "cc-amex": "f1f3", - "cc-diners-club": "f24c", - "cc-discover": "f1f2", - "cc-jcb": "f24b", - "cc-mastercard": "f1f1", - "cc-paypal": "f1f4", - "cc-stripe": "f1f5", - "cc-visa": "f1f0", - "certificate": "f0a3", - "chain": "f0c1", - "chain-broken": "f127", - "check": "f00c", - "check-circle": "f058", - "check-circle-o": "f05d", - "check-square": "f14a", - "check-square-o": "f046", - "chevron-circle-down": "f13a", - "chevron-circle-left": "f137", - "chevron-circle-right": "f138", - "chevron-circle-up": "f139", - "chevron-down": "f078", - "chevron-left": "f053", - "chevron-right": "f054", - "chevron-up": "f077", - "child": "f1ae", - "chrome": "f268", - "circle": "f111", - "circle-o": "f10c", - "circle-o-notch": "f1ce", - "circle-thin": "f1db", - "clipboard": "f0ea", - "clock-o": "f017", - "clone": "f24d", - "close": "f00d", - "cloud": "f0c2", - "cloud-download": "f0ed", - "cloud-upload": "f0ee", - "cny": "f157", - "code": "f121", - "code-fork": "f126", - "codepen": "f1cb", - "codiepie": "f284", - "coffee": "f0f4", - "cog": "f013", - "cogs": "f085", - "columns": "f0db", - "comment": "f075", - "comment-o": "f0e5", - "commenting": "f27a", - "commenting-o": "f27b", - "comments": "f086", - "comments-o": "f0e6", - "compass": "f14e", - "compress": "f066", - "connectdevelop": "f20e", - "contao": "f26d", - "copy": "f0c5", - "copyright": "f1f9", - "creative-commons": "f25e", - "credit-card": "f09d", - "credit-card-alt": "f283", - "crop": "f125", - "crosshairs": "f05b", - "css3": "f13c", - "cube": "f1b2", - "cubes": "f1b3", - "cut": "f0c4", - "cutlery": "f0f5", - "dashboard": "f0e4", - "dashcube": "f210", - "database": "f1c0", - "dedent": "f03b", - "delicious": "f1a5", - "desktop": "f108", - "deviantart": "f1bd", - "diamond": "f219", - "digg": "f1a6", - "dollar": "f155", - "dot-circle-o": "f192", - "download": "f019", - "dribbble": "f17d", - "dropbox": "f16b", - "drupal": "f1a9", - "edge": "f282", - "edit": "f044", - "eject": "f052", - "ellipsis-h": "f141", - "ellipsis-v": "f142", - "empire": "f1d1", - "envelope": "f0e0", - "envelope-o": "f003", - "envelope-square": "f199", - "eraser": "f12d", - "eur": "f153", - "euro": "f153", - "exchange": "f0ec", - "exclamation": "f12a", - "exclamation-circle": "f06a", - "exclamation-triangle": "f071", - "expand": "f065", - "expeditedssl": "f23e", - "external-link": "f08e", - "external-link-square": "f14c", - "eye": "f06e", - "eye-slash": "f070", - "eyedropper": "f1fb", - "facebook": "f09a", - "facebook-f": "f09a", - "facebook-official": "f230", - "facebook-square": "f082", - "fast-backward": "f049", - "fast-forward": "f050", - "fax": "f1ac", - "feed": "f09e", - "female": "f182", - "fighter-jet": "f0fb", - "file": "f15b", - "file-archive-o": "f1c6", - "file-audio-o": "f1c7", - "file-code-o": "f1c9", - "file-excel-o": "f1c3", - "file-image-o": "f1c5", - "file-movie-o": "f1c8", - "file-o": "f016", - "file-pdf-o": "f1c1", - "file-photo-o": "f1c5", - "file-picture-o": "f1c5", - "file-powerpoint-o": "f1c4", - "file-sound-o": "f1c7", - "file-text": "f15c", - "file-text-o": "f0f6", - "file-video-o": "f1c8", - "file-word-o": "f1c2", - "file-zip-o": "f1c6", - "files-o": "f0c5", - "film": "f008", - "filter": "f0b0", - "fire": "f06d", - "fire-extinguisher": "f134", - "firefox": "f269", - "flag": "f024", - "flag-checkered": "f11e", - "flag-o": "f11d", - "flash": "f0e7", - "flask": "f0c3", - "flickr": "f16e", - "floppy-o": "f0c7", - "folder": "f07b", - "folder-o": "f114", - "folder-open": "f07c", - "folder-open-o": "f115", - "font": "f031", - "fonticons": "f280", - "fort-awesome": "f286", - "forumbee": "f211", - "forward": "f04e", - "foursquare": "f180", - "frown-o": "f119", - "futbol-o": "f1e3", - "gamepad": "f11b", - "gavel": "f0e3", - "gbp": "f154", - "ge": "f1d1", - "gear": "f013", - "gears": "f085", - "genderless": "f22d", - "get-pocket": "f265", - "gg": "f260", - "gg-circle": "f261", - "gift": "f06b", - "git": "f1d3", - "git-square": "f1d2", - "github": "f09b", - "github-alt": "f113", - "github-square": "f092", - "gittip": "f184", - "glass": "f000", - "globe": "f0ac", - "google": "f1a0", - "google-plus": "f0d5", - "google-plus-square": "f0d4", - "google-wallet": "f1ee", - "graduation-cap": "f19d", - "gratipay": "f184", - "group": "f0c0", - "h-square": "f0fd", - "hacker-news": "f1d4", - "hand-grab-o": "f255", - "hand-lizard-o": "f258", - "hand-o-down": "f0a7", - "hand-o-left": "f0a5", - "hand-o-right": "f0a4", - "hand-o-up": "f0a6", - "hand-paper-o": "f256", - "hand-peace-o": "f25b", - "hand-pointer-o": "f25a", - "hand-rock-o": "f255", - "hand-scissors-o": "f257", - "hand-spock-o": "f259", - "hand-stop-o": "f256", - "hashtag": "f292", - "hdd-o": "f0a0", - "header": "f1dc", - "headphones": "f025", - "heart": "f004", - "heart-o": "f08a", - "heartbeat": "f21e", - "history": "f1da", - "home": "f015", - "hospital-o": "f0f8", - "hotel": "f236", - "hourglass": "f254", - "hourglass-1": "f251", - "hourglass-2": "f252", - "hourglass-3": "f253", - "hourglass-end": "f253", - "hourglass-half": "f252", - "hourglass-o": "f250", - "hourglass-start": "f251", - "houzz": "f27c", - "html5": "f13b", - "i-cursor": "f246", - "ils": "f20b", - "image": "f03e", - "inbox": "f01c", - "indent": "f03c", - "industry": "f275", - "info": "f129", - "info-circle": "f05a", - "inr": "f156", - "instagram": "f16d", - "institution": "f19c", - "internet-explorer": "f26b", - "intersex": "f224", - "ioxhost": "f208", - "italic": "f033", - "joomla": "f1aa", - "jpy": "f157", - "jsfiddle": "f1cc", - "key": "f084", - "keyboard-o": "f11c", - "krw": "f159", - "language": "f1ab", - "laptop": "f109", - "lastfm": "f202", - "lastfm-square": "f203", - "leaf": "f06c", - "leanpub": "f212", - "legal": "f0e3", - "lemon-o": "f094", - "level-down": "f149", - "level-up": "f148", - "life-bouy": "f1cd", - "life-buoy": "f1cd", - "life-ring": "f1cd", - "life-saver": "f1cd", - "lightbulb-o": "f0eb", - "line-chart": "f201", - "link": "f0c1", - "linkedin": "f0e1", - "linkedin-square": "f08c", - "linux": "f17c", - "list": "f03a", - "list-alt": "f022", - "list-ol": "f0cb", - "list-ul": "f0ca", - "location-arrow": "f124", - "lock": "f023", - "long-arrow-down": "f175", - "long-arrow-left": "f177", - "long-arrow-right": "f178", - "long-arrow-up": "f176", - "magic": "f0d0", - "magnet": "f076", - "mail-forward": "f064", - "mail-reply": "f112", - "mail-reply-all": "f122", - "male": "f183", - "map": "f279", - "map-marker": "f041", - "map-o": "f278", - "map-pin": "f276", - "map-signs": "f277", - "mars": "f222", - "mars-double": "f227", - "mars-stroke": "f229", - "mars-stroke-h": "f22b", - "mars-stroke-v": "f22a", - "maxcdn": "f136", - "meanpath": "f20c", - "medium": "f23a", - "medkit": "f0fa", - "meh-o": "f11a", - "mercury": "f223", - "microphone": "f130", - "microphone-slash": "f131", - "minus": "f068", - "minus-circle": "f056", - "minus-square": "f146", - "minus-square-o": "f147", - "mixcloud": "f289", - "mobile": "f10b", - "mobile-phone": "f10b", - "modx": "f285", - "money": "f0d6", - "moon-o": "f186", - "mortar-board": "f19d", - "motorcycle": "f21c", - "mouse-pointer": "f245", - "music": "f001", - "navicon": "f0c9", - "neuter": "f22c", - "newspaper-o": "f1ea", - "object-group": "f247", - "object-ungroup": "f248", - "odnoklassniki": "f263", - "odnoklassniki-square": "f264", - "opencart": "f23d", - "openid": "f19b", - "opera": "f26a", - "optin-monster": "f23c", - "outdent": "f03b", - "pagelines": "f18c", - "paint-brush": "f1fc", - "paper-plane": "f1d8", - "paper-plane-o": "f1d9", - "paperclip": "f0c6", - "paragraph": "f1dd", - "paste": "f0ea", - "pause": "f04c", - "pause-circle": "f28b", - "pause-circle-o": "f28c", - "paw": "f1b0", - "paypal": "f1ed", - "pencil": "f040", - "pencil-square": "f14b", - "pencil-square-o": "f044", - "percent": "f295", - "phone": "f095", - "phone-square": "f098", - "photo": "f03e", - "picture-o": "f03e", - "pie-chart": "f200", - "pied-piper": "f1a7", - "pied-piper-alt": "f1a8", - "pinterest": "f0d2", - "pinterest-p": "f231", - "pinterest-square": "f0d3", - "plane": "f072", - "play": "f04b", - "play-circle": "f144", - "play-circle-o": "f01d", - "plug": "f1e6", - "plus": "f067", - "plus-circle": "f055", - "plus-square": "f0fe", - "plus-square-o": "f196", - "power-off": "f011", - "print": "f02f", - "product-hunt": "f288", - "puzzle-piece": "f12e", - "qq": "f1d6", - "qrcode": "f029", - "question": "f128", - "question-circle": "f059", - "quote-left": "f10d", - "quote-right": "f10e", - "ra": "f1d0", - "random": "f074", - "rebel": "f1d0", - "recycle": "f1b8", - "reddit": "f1a1", - "reddit-alien": "f281", - "reddit-square": "f1a2", - "refresh": "f021", - "registered": "f25d", - "remove": "f00d", - "renren": "f18b", - "reorder": "f0c9", - "repeat": "f01e", - "reply": "f112", - "reply-all": "f122", - "retweet": "f079", - "rmb": "f157", - "road": "f018", - "rocket": "f135", - "rotate-left": "f0e2", - "rotate-right": "f01e", - "rouble": "f158", - "rss": "f09e", - "rss-square": "f143", - "rub": "f158", - "ruble": "f158", - "rupee": "f156", - "safari": "f267", - "save": "f0c7", - "scissors": "f0c4", - "scribd": "f28a", - "search": "f002", - "search-minus": "f010", - "search-plus": "f00e", - "sellsy": "f213", - "send": "f1d8", - "send-o": "f1d9", - "server": "f233", - "share": "f064", - "share-alt": "f1e0", - "share-alt-square": "f1e1", - "share-square": "f14d", - "share-square-o": "f045", - "shekel": "f20b", - "sheqel": "f20b", - "shield": "f132", - "ship": "f21a", - "shirtsinbulk": "f214", - "shopping-bag": "f290", - "shopping-basket": "f291", - "shopping-cart": "f07a", - "sign-in": "f090", - "sign-out": "f08b", - "signal": "f012", - "simplybuilt": "f215", - "sitemap": "f0e8", - "skyatlas": "f216", - "skype": "f17e", - "slack": "f198", - "sliders": "f1de", - "slideshare": "f1e7", - "smile-o": "f118", - "soccer-ball-o": "f1e3", - "sort": "f0dc", - "sort-alpha-asc": "f15d", - "sort-alpha-desc": "f15e", - "sort-amount-asc": "f160", - "sort-amount-desc": "f161", - "sort-asc": "f0de", - "sort-desc": "f0dd", - "sort-down": "f0dd", - "sort-numeric-asc": "f162", - "sort-numeric-desc": "f163", - "sort-up": "f0de", - "soundcloud": "f1be", - "space-shuttle": "f197", - "spinner": "f110", - "spoon": "f1b1", - "spotify": "f1bc", - "square": "f0c8", - "square-o": "f096", - "stack-exchange": "f18d", - "stack-overflow": "f16c", - "star": "f005", - "star-half": "f089", - "star-half-empty": "f123", - "star-half-full": "f123", - "star-half-o": "f123", - "star-o": "f006", - "steam": "f1b6", - "steam-square": "f1b7", - "step-backward": "f048", - "step-forward": "f051", - "stethoscope": "f0f1", - "sticky-note": "f249", - "sticky-note-o": "f24a", - "stop": "f04d", - "stop-circle": "f28d", - "stop-circle-o": "f28e", - "street-view": "f21d", - "strikethrough": "f0cc", - "stumbleupon": "f1a4", - "stumbleupon-circle": "f1a3", - "subscript": "f12c", - "subway": "f239", - "suitcase": "f0f2", - "sun-o": "f185", - "superscript": "f12b", - "support": "f1cd", - "table": "f0ce", - "tablet": "f10a", - "tachometer": "f0e4", - "tag": "f02b", - "tags": "f02c", - "tasks": "f0ae", - "taxi": "f1ba", - "television": "f26c", - "tencent-weibo": "f1d5", - "terminal": "f120", - "text-height": "f034", - "text-width": "f035", - "th": "f00a", - "th-large": "f009", - "th-list": "f00b", - "thumb-tack": "f08d", - "thumbs-down": "f165", - "thumbs-o-down": "f088", - "thumbs-o-up": "f087", - "thumbs-up": "f164", - "ticket": "f145", - "times": "f00d", - "times-circle": "f057", - "times-circle-o": "f05c", - "tint": "f043", - "toggle-down": "f150", - "toggle-left": "f191", - "toggle-off": "f204", - "toggle-on": "f205", - "toggle-right": "f152", - "toggle-up": "f151", - "trademark": "f25c", - "train": "f238", - "transgender": "f224", - "transgender-alt": "f225", - "trash": "f1f8", - "trash-o": "f014", - "tree": "f1bb", - "trello": "f181", - "tripadvisor": "f262", - "trophy": "f091", - "truck": "f0d1", - "try": "f195", - "tty": "f1e4", - "tumblr": "f173", - "tumblr-square": "f174", - "turkish-lira": "f195", - "tv": "f26c", - "twitch": "f1e8", - "twitter": "f099", - "twitter-square": "f081", - "umbrella": "f0e9", - "underline": "f0cd", - "undo": "f0e2", - "university": "f19c", - "unlink": "f127", - "unlock": "f09c", - "unlock-alt": "f13e", - "unsorted": "f0dc", - "upload": "f093", - "usb": "f287", - "usd": "f155", - "user": "f007", - "user-md": "f0f0", - "user-plus": "f234", - "user-secret": "f21b", - "user-times": "f235", - "users": "f0c0", - "venus": "f221", - "venus-double": "f226", - "venus-mars": "f228", - "viacoin": "f237", - "video-camera": "f03d", - "vimeo": "f27d", - "vimeo-square": "f194", - "vine": "f1ca", - "vk": "f189", - "volume-down": "f027", - "volume-off": "f026", - "volume-up": "f028", - "warning": "f071", - "wechat": "f1d7", - "weibo": "f18a", - "weixin": "f1d7", - "whatsapp": "f232", - "wheelchair": "f193", - "wifi": "f1eb", - "wikipedia-w": "f266", - "windows": "f17a", - "won": "f159", - "wordpress": "f19a", - "wrench": "f0ad", - "xing": "f168", - "xing-square": "f169", - "y-combinator": "f23b", - "y-combinator-square": "f1d4", - "yahoo": "f19e", - "yc": "f23b", - "yc-square": "f1d4", - "yelp": "f1e9", - "yen": "f157", - "youtube": "f167", - "youtube-play": "f16a", - "youtube-square": "f166" -} \ No newline at end of file diff --git a/client/ayon_core/tools/pyblish_pype/vendor/qtawesome/fonts/fontawesome-webfont.ttf b/client/ayon_core/tools/pyblish_pype/vendor/qtawesome/fonts/fontawesome-webfont.ttf deleted file mode 100644 index 26dea7951a..0000000000 Binary files a/client/ayon_core/tools/pyblish_pype/vendor/qtawesome/fonts/fontawesome-webfont.ttf and /dev/null differ diff --git a/client/ayon_core/tools/pyblish_pype/vendor/qtawesome/iconic_font.py b/client/ayon_core/tools/pyblish_pype/vendor/qtawesome/iconic_font.py deleted file mode 100644 index ce95f9e74f..0000000000 --- a/client/ayon_core/tools/pyblish_pype/vendor/qtawesome/iconic_font.py +++ /dev/null @@ -1,286 +0,0 @@ -"""Classes handling iconic fonts""" - -from __future__ import print_function - -import json -import os - -from qtpy import QtCore, QtGui - - -_default_options = { - 'color': QtGui.QColor(50, 50, 50), - 'color_disabled': QtGui.QColor(150, 150, 150), - 'opacity': 1.0, - 'scale_factor': 1.0, -} - - -def set_global_defaults(**kwargs): - """Set global defaults for all icons""" - valid_options = ['active', 'animation', 'color', 'color_active', - 'color_disabled', 'color_selected', 'disabled', 'offset', - 'scale_factor', 'selected'] - for kw in kwargs: - if kw in valid_options: - _default_options[kw] = kwargs[kw] - else: - error = "Invalid option '{0}'".format(kw) - raise KeyError(error) - - -class CharIconPainter: - - """Char icon painter""" - - def paint(self, iconic, painter, rect, mode, state, options): - """Main paint method""" - for opt in options: - self._paint_icon(iconic, painter, rect, mode, state, opt) - - def _paint_icon(self, iconic, painter, rect, mode, state, options): - """Paint a single icon""" - painter.save() - color, char = options['color'], options['char'] - - if mode == QtGui.QIcon.Disabled: - color = options.get('color_disabled', color) - char = options.get('disabled', char) - elif mode == QtGui.QIcon.Active: - color = options.get('color_active', color) - char = options.get('active', char) - elif mode == QtGui.QIcon.Selected: - color = options.get('color_selected', color) - char = options.get('selected', char) - - painter.setPen(QtGui.QColor(color)) - # A 16 pixel-high icon yields a font size of 14, which is pixel perfect - # for font-awesome. 16 * 0.875 = 14 - # The reason for not using full-sized glyphs is the negative bearing of - # fonts. - draw_size = 0.875 * round(rect.height() * options['scale_factor']) - prefix = options['prefix'] - - # Animation setup hook - animation = options.get('animation') - if animation is not None: - animation.setup(self, painter, rect) - - painter.setFont(iconic.font(prefix, draw_size)) - if 'offset' in options: - rect = QtCore.QRect(rect) - rect.translate(options['offset'][0] * rect.width(), - options['offset'][1] * rect.height()) - - painter.setOpacity(options.get('opacity', 1.0)) - - painter.drawText(rect, - QtCore.Qt.AlignCenter | QtCore.Qt.AlignVCenter, - char) - painter.restore() - - -class CharIconEngine(QtGui.QIconEngine): - - """Specialization of QtGui.QIconEngine used to draw font-based icons""" - - def __init__(self, iconic, painter, options): - super(CharIconEngine, self).__init__() - self.iconic = iconic - self.painter = painter - self.options = options - - def paint(self, painter, rect, mode, state): - self.painter.paint( - self.iconic, painter, rect, mode, state, self.options) - - def pixmap(self, size, mode, state): - pm = QtGui.QPixmap(size) - pm.fill(QtCore.Qt.transparent) - self.paint(QtGui.QPainter(pm), - QtCore.QRect(QtCore.QPoint(0, 0), size), - mode, - state) - return pm - - -class IconicFont(QtCore.QObject): - - """Main class for managing iconic fonts""" - - def __init__(self, *args): - """Constructor - - :param *args: tuples - Each positional argument is a tuple of 3 or 4 values - - The prefix string to be used when accessing a given font set - - The ttf font filename - - The json charmap filename - - Optionally, the directory containing these files. When not - provided, the files will be looked up in ./fonts/ - """ - super(IconicFont, self).__init__() - self.painter = CharIconPainter() - self.painters = {} - self.fontname = {} - self.charmap = {} - for fargs in args: - self.load_font(*fargs) - - def load_font(self, - prefix, - ttf_filename, - charmap_filename, - directory=None): - """Loads a font file and the associated charmap - - If `directory` is None, the files will be looked up in ./fonts/ - - Arguments - --------- - prefix: str - prefix string to be used when accessing a given font set - ttf_filename: str - ttf font filename - charmap_filename: str - charmap filename - directory: str or None, optional - directory for font and charmap files - """ - - def hook(obj): - result = {} - for key in obj: - result[key] = chr(int(obj[key], 16)) - return result - - if directory is None: - directory = os.path.join( - os.path.dirname(os.path.realpath(__file__)), 'fonts') - - with open(os.path.join(directory, charmap_filename), 'r') as codes: - self.charmap[prefix] = json.load(codes, object_hook=hook) - - id_ = QtGui.QFontDatabase.addApplicationFont( - os.path.join(directory, ttf_filename)) - - loadedFontFamilies = QtGui.QFontDatabase.applicationFontFamilies(id_) - - if(loadedFontFamilies): - self.fontname[prefix] = loadedFontFamilies[0] - else: - print('Font is empty') - - def icon(self, *names, **kwargs): - """Returns a QtGui.QIcon object corresponding to the provided icon name - (including prefix) - - Arguments - --------- - names: list of str - icon name, of the form PREFIX.NAME - - options: dict - options to be passed to the icon painter - """ - options_list = kwargs.pop('options', [{}] * len(names)) - general_options = kwargs - - if len(options_list) != len(names): - error = '"options" must be a list of size {0}'.format(len(names)) - raise Exception(error) - - parsed_options = [] - for i in range(len(options_list)): - specific_options = options_list[i] - parsed_options.append(self._parse_options(specific_options, - general_options, - names[i])) - - # Process high level API - api_options = parsed_options - - return self._icon_by_painter(self.painter, api_options) - - def _parse_options(self, specific_options, general_options, name): - """ """ - options = dict(_default_options, **general_options) - options.update(specific_options) - - # Handle icons for states - icon_kw = ['disabled', 'active', 'selected', 'char'] - names = [options.get(kw, name) for kw in icon_kw] - prefix, chars = self._get_prefix_chars(names) - options.update(dict(zip(*(icon_kw, chars)))) - options.update({'prefix': prefix}) - - # Handle colors for states - color_kw = ['color_active', 'color_selected'] - colors = [options.get(kw, options['color']) for kw in color_kw] - options.update(dict(zip(*(color_kw, colors)))) - - return options - - def _get_prefix_chars(self, names): - """ """ - chars = [] - for name in names: - if '.' in name: - prefix, n = name.split('.') - if prefix in self.charmap: - if n in self.charmap[prefix]: - chars.append(self.charmap[prefix][n]) - else: - error = 'Invalid icon name "{0}" in font "{1}"'.format( - n, prefix) - raise Exception(error) - else: - error = 'Invalid font prefix "{0}"'.format(prefix) - raise Exception(error) - else: - raise Exception('Invalid icon name') - - return prefix, chars - - def font(self, prefix, size): - """Returns QtGui.QFont corresponding to the given prefix and size - - Arguments - --------- - prefix: str - prefix string of the loaded font - size: int - size for the font - """ - font = QtGui.QFont(self.fontname[prefix]) - font.setPixelSize(size) - return font - - def set_custom_icon(self, name, painter): - """Associates a user-provided CharIconPainter to an icon name - The custom icon can later be addressed by calling - icon('custom.NAME') where NAME is the provided name for that icon. - - Arguments - --------- - name: str - name of the custom icon - painter: CharIconPainter - The icon painter, implementing - `paint(self, iconic, painter, rect, mode, state, options)` - """ - self.painters[name] = painter - - def _custom_icon(self, name, **kwargs): - """Returns the custom icon corresponding to the given name""" - options = dict(_default_options, **kwargs) - if name in self.painters: - painter = self.painters[name] - return self._icon_by_painter(painter, options) - else: - return QtGui.QIcon() - - def _icon_by_painter(self, painter, options): - """Returns the icon corresponding to the given painter""" - engine = CharIconEngine(self, painter, options) - return QtGui.QIcon(engine) diff --git a/client/ayon_core/tools/pyblish_pype/version.py b/client/ayon_core/tools/pyblish_pype/version.py deleted file mode 100644 index 5f1dce8011..0000000000 --- a/client/ayon_core/tools/pyblish_pype/version.py +++ /dev/null @@ -1,11 +0,0 @@ - -VERSION_MAJOR = 2 -VERSION_MINOR = 9 -VERSION_PATCH = 0 - - -version_info = (VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH) -version = '%i.%i.%i' % version_info -__version__ = version - -__all__ = ['version', 'version_info', '__version__'] diff --git a/client/ayon_core/tools/pyblish_pype/view.py b/client/ayon_core/tools/pyblish_pype/view.py deleted file mode 100644 index cc6604fc63..0000000000 --- a/client/ayon_core/tools/pyblish_pype/view.py +++ /dev/null @@ -1,334 +0,0 @@ -from qtpy import QtCore, QtWidgets -from . import model -from .constants import Roles, EXPANDER_WIDTH -# Imported when used -widgets = None - - -def _import_widgets(): - global widgets - if widgets is None: - from . import widgets - - -class OverviewView(QtWidgets.QTreeView): - # An item is requesting to be toggled, with optional forced-state - toggled = QtCore.Signal(QtCore.QModelIndex, object) - show_perspective = QtCore.Signal(QtCore.QModelIndex) - - def __init__(self, parent=None): - super(OverviewView, self).__init__(parent) - - self.horizontalScrollBar().hide() - self.viewport().setAttribute(QtCore.Qt.WA_Hover, True) - self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) - self.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) - self.setItemsExpandable(True) - self.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel) - self.setHeaderHidden(True) - self.setRootIsDecorated(False) - self.setIndentation(0) - - def event(self, event): - if not event.type() == QtCore.QEvent.KeyPress: - return super(OverviewView, self).event(event) - - elif event.key() == QtCore.Qt.Key_Space: - for index in self.selectionModel().selectedIndexes(): - self.toggled.emit(index, None) - - return True - - elif event.key() == QtCore.Qt.Key_Backspace: - for index in self.selectionModel().selectedIndexes(): - self.toggled.emit(index, False) - - return True - - elif event.key() == QtCore.Qt.Key_Return: - for index in self.selectionModel().selectedIndexes(): - self.toggled.emit(index, True) - - return True - - return super(OverviewView, self).event(event) - - def focusOutEvent(self, event): - self.selectionModel().clear() - - def mouseReleaseEvent(self, event): - if event.button() in (QtCore.Qt.LeftButton, QtCore.Qt.RightButton): - # Deselect all group labels - indexes = self.selectionModel().selectedIndexes() - for index in indexes: - if index.data(Roles.TypeRole) == model.GroupType: - self.selectionModel().select( - index, QtCore.QItemSelectionModel.Deselect - ) - - return super(OverviewView, self).mouseReleaseEvent(event) - - -class PluginView(OverviewView): - def __init__(self, *args, **kwargs): - super(PluginView, self).__init__(*args, **kwargs) - self.clicked.connect(self.item_expand) - - def item_expand(self, index): - if index.data(Roles.TypeRole) == model.GroupType: - if self.isExpanded(index): - self.collapse(index) - else: - self.expand(index) - - def mouseReleaseEvent(self, event): - if event.button() == QtCore.Qt.LeftButton: - indexes = self.selectionModel().selectedIndexes() - if len(indexes) == 1: - index = indexes[0] - pos_index = self.indexAt(event.pos()) - # If instance or Plugin and is selected - if ( - index == pos_index - and index.data(Roles.TypeRole) == model.PluginType - ): - if event.pos().x() < 20: - self.toggled.emit(index, None) - elif event.pos().x() > self.width() - 20: - self.show_perspective.emit(index) - - return super(PluginView, self).mouseReleaseEvent(event) - - -class InstanceView(OverviewView): - def __init__(self, *args, **kwargs): - super(InstanceView, self).__init__(*args, **kwargs) - self.setSortingEnabled(True) - self.sortByColumn(0, QtCore.Qt.AscendingOrder) - self.viewport().setMouseTracking(True) - self._pressed_group_index = None - self._pressed_expander = None - - def mouseMoveEvent(self, event): - index = self.indexAt(event.pos()) - if index.data(Roles.TypeRole) == model.GroupType: - self.update(index) - super(InstanceView, self).mouseMoveEvent(event) - - def item_expand(self, index, expand=None): - if expand is None: - expand = not self.isExpanded(index) - - if expand: - self.expand(index) - else: - self.collapse(index) - - def group_toggle(self, index): - if not index.isValid(): - return - model = index.model() - - chilren_indexes_checked = [] - chilren_indexes_unchecked = [] - for idx in range(model.rowCount(index)): - child_index = model.index(idx, 0, index) - if not child_index.data(Roles.IsEnabledRole): - continue - - if child_index.data(QtCore.Qt.CheckStateRole): - chilren_indexes_checked.append(child_index) - else: - chilren_indexes_unchecked.append(child_index) - - if chilren_indexes_checked: - to_change_indexes = chilren_indexes_checked - new_state = False - else: - to_change_indexes = chilren_indexes_unchecked - new_state = True - - for index in to_change_indexes: - model.setData(index, new_state, QtCore.Qt.CheckStateRole) - self.toggled.emit(index, new_state) - - def _mouse_press(self, event): - if event.button() != QtCore.Qt.LeftButton: - return - - self._pressed_group_index = None - self._pressed_expander = None - - pos_index = self.indexAt(event.pos()) - if not pos_index.isValid(): - return - - if pos_index.data(Roles.TypeRole) != model.InstanceType: - self._pressed_group_index = pos_index - if event.pos().x() < 20: - self._pressed_expander = True - else: - self._pressed_expander = False - - elif event.pos().x() < 20: - indexes = self.selectionModel().selectedIndexes() - any_checked = False - if len(indexes) <= 1: - return - - if pos_index in indexes: - for index in indexes: - if index.data(QtCore.Qt.CheckStateRole): - any_checked = True - break - - for index in indexes: - self.toggled.emit(index, not any_checked) - return True - self.toggled.emit(pos_index, not any_checked) - - def mousePressEvent(self, event): - if self._mouse_press(event): - return - return super(InstanceView, self).mousePressEvent(event) - - def _mouse_release(self, event, pressed_expander, pressed_index): - if event.button() != QtCore.Qt.LeftButton: - return - - pos_index = self.indexAt(event.pos()) - if not pos_index.isValid(): - return - - if pos_index.data(Roles.TypeRole) == model.InstanceType: - indexes = self.selectionModel().selectedIndexes() - if len(indexes) == 1 and indexes[0] == pos_index: - if event.pos().x() < 20: - self.toggled.emit(indexes[0], None) - elif event.pos().x() > self.width() - 20: - self.show_perspective.emit(indexes[0]) - return True - return - - if pressed_index != pos_index: - return - - if self.state() == QtWidgets.QTreeView.State.DragSelectingState: - indexes = self.selectionModel().selectedIndexes() - if len(indexes) != 1 or indexes[0] != pos_index: - return - - if event.pos().x() < EXPANDER_WIDTH: - if pressed_expander is True: - self.item_expand(pos_index) - return True - else: - if pressed_expander is False: - self.group_toggle(pos_index) - self.item_expand(pos_index, True) - return True - - def mouseReleaseEvent(self, event): - pressed_index = self._pressed_group_index - pressed_expander = self._pressed_expander is True - self._pressed_group_index = None - self._pressed_expander = None - result = self._mouse_release(event, pressed_expander, pressed_index) - if result: - return - return super(InstanceView, self).mouseReleaseEvent(event) - - -class TerminalView(QtWidgets.QTreeView): - # An item is requesting to be toggled, with optional forced-state - def __init__(self, parent=None): - super(TerminalView, self).__init__(parent) - self.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) - self.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) - self.setAutoScroll(False) - self.setHeaderHidden(True) - self.setIndentation(0) - self.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel) - self.verticalScrollBar().setSingleStep(10) - self.setRootIsDecorated(False) - - self.clicked.connect(self.item_expand) - - _import_widgets() - - def event(self, event): - if not event.type() == QtCore.QEvent.KeyPress: - return super(TerminalView, self).event(event) - - elif event.key() == QtCore.Qt.Key_Space: - for index in self.selectionModel().selectedIndexes(): - if self.isExpanded(index): - self.collapse(index) - else: - self.expand(index) - - elif event.key() == QtCore.Qt.Key_Backspace: - for index in self.selectionModel().selectedIndexes(): - self.collapse(index) - - elif event.key() == QtCore.Qt.Key_Return: - for index in self.selectionModel().selectedIndexes(): - self.expand(index) - - return super(TerminalView, self).event(event) - - def focusOutEvent(self, event): - self.selectionModel().clear() - - def item_expand(self, index): - if index.data(Roles.TypeRole) == model.TerminalLabelType: - if self.isExpanded(index): - self.collapse(index) - else: - self.expand(index) - self.model().layoutChanged.emit() - self.updateGeometry() - - def rowsInserted(self, parent, start, end): - """Automatically scroll to bottom on each new item added.""" - super(TerminalView, self).rowsInserted(parent, start, end) - self.updateGeometry() - self.scrollToBottom() - - def expand(self, index): - """Wrapper to set widget for expanded index.""" - model = index.model() - row_count = model.rowCount(index) - is_new = False - for child_idx in range(row_count): - child_index = model.index(child_idx, index.column(), index) - widget = self.indexWidget(child_index) - if widget is None: - is_new = True - msg = child_index.data(QtCore.Qt.DisplayRole) - widget = widgets.TerminalDetail(msg) - self.setIndexWidget(child_index, widget) - super(TerminalView, self).expand(index) - if is_new: - self.updateGeometries() - - def resizeEvent(self, event): - super(self.__class__, self).resizeEvent(event) - self.model().layoutChanged.emit() - - def sizeHint(self): - size = super(TerminalView, self).sizeHint() - height = ( - self.contentsMargins().top() - + self.contentsMargins().bottom() - ) - for idx_i in range(self.model().rowCount()): - index = self.model().index(idx_i, 0) - height += self.rowHeight(index) - if self.isExpanded(index): - for idx_j in range(index.model().rowCount(index)): - child_index = index.child(idx_j, 0) - height += self.rowHeight(child_index) - - size.setHeight(height) - return size diff --git a/client/ayon_core/tools/pyblish_pype/widgets.py b/client/ayon_core/tools/pyblish_pype/widgets.py deleted file mode 100644 index 6adcc55f06..0000000000 --- a/client/ayon_core/tools/pyblish_pype/widgets.py +++ /dev/null @@ -1,555 +0,0 @@ -import sys -from qtpy import QtCore, QtWidgets, QtGui -from . import model, delegate, view, awesome -from .constants import PluginStates, InstanceStates, Roles - - -class EllidableLabel(QtWidgets.QLabel): - def __init__(self, *args, **kwargs): - super(EllidableLabel, self).__init__(*args, **kwargs) - self.setObjectName("EllidableLabel") - - def paintEvent(self, event): - painter = QtGui.QPainter(self) - - metrics = QtGui.QFontMetrics(self.font()) - elided = metrics.elidedText( - self.text(), QtCore.Qt.ElideRight, self.width() - ) - painter.drawText(self.rect(), self.alignment(), elided) - - -class PerspectiveLabel(QtWidgets.QTextEdit): - def __init__(self, parent=None): - super(PerspectiveLabel, self).__init__(parent) - self.setObjectName("PerspectiveLabel") - - size_policy = self.sizePolicy() - size_policy.setHeightForWidth(True) - size_policy.setVerticalPolicy(QtWidgets.QSizePolicy.Preferred) - self.setSizePolicy(size_policy) - - self.textChanged.connect(self.on_text_changed) - - def on_text_changed(self, *args, **kwargs): - self.updateGeometry() - - def hasHeightForWidth(self): - return True - - def heightForWidth(self, width): - margins = self.contentsMargins() - - document_width = 0 - if width >= margins.left() + margins.right(): - document_width = width - margins.left() - margins.right() - - document = self.document().clone() - document.setTextWidth(document_width) - - return margins.top() + document.size().height() + margins.bottom() - - def sizeHint(self): - width = super(PerspectiveLabel, self).sizeHint().width() - return QtCore.QSize(width, self.heightForWidth(width)) - - -class PerspectiveWidget(QtWidgets.QWidget): - l_doc = "Documentation" - l_rec = "Records" - l_path = "Path" - - def __init__(self, parent): - super(PerspectiveWidget, self).__init__(parent) - - self.parent_widget = parent - main_layout = QtWidgets.QVBoxLayout(self) - - header_widget = QtWidgets.QWidget() - toggle_button = QtWidgets.QPushButton(parent=header_widget) - toggle_button.setObjectName("PerspectiveToggleBtn") - toggle_button.setText(delegate.icons["angle-left"]) - toggle_button.setMinimumHeight(50) - toggle_button.setFixedWidth(40) - - indicator = QtWidgets.QLabel("", parent=header_widget) - indicator.setFixedWidth(30) - indicator.setAlignment(QtCore.Qt.AlignCenter) - indicator.setObjectName("PerspectiveIndicator") - - name = EllidableLabel('*Name of inspected', parent=header_widget) - - header_layout = QtWidgets.QHBoxLayout(header_widget) - header_layout.setAlignment(QtCore.Qt.AlignLeft) - header_layout.addWidget(toggle_button) - header_layout.addWidget(indicator) - header_layout.addWidget(name) - header_layout.setContentsMargins(0, 0, 0, 0) - header_layout.setSpacing(10) - header_widget.setLayout(header_layout) - - main_layout.setAlignment(QtCore.Qt.AlignTop) - main_layout.addWidget(header_widget) - - scroll_widget = QtWidgets.QScrollArea(self) - scroll_widget.setObjectName("PerspectiveScrollContent") - - contents_widget = QtWidgets.QWidget(scroll_widget) - contents_widget.setObjectName("PerspectiveWidgetContent") - - layout = QtWidgets.QVBoxLayout() - layout.setAlignment(QtCore.Qt.AlignTop) - layout.setContentsMargins(0, 0, 0, 0) - - documentation = ExpandableWidget(self, self.l_doc) - doc_label = PerspectiveLabel() - documentation.set_content(doc_label) - layout.addWidget(documentation) - - path = ExpandableWidget(self, self.l_path) - path_label = PerspectiveLabel() - path.set_content(path_label) - layout.addWidget(path) - - records = ExpandableWidget(self, self.l_rec) - layout.addWidget(records) - - contents_widget.setLayout(layout) - - terminal_view = view.TerminalView() - terminal_view.setObjectName("TerminalView") - terminal_model = model.TerminalModel() - terminal_proxy = model.TerminalProxy(terminal_view) - terminal_proxy.setSourceModel(terminal_model) - - terminal_view.setModel(terminal_proxy) - terminal_delegate = delegate.TerminalItem() - terminal_view.setItemDelegate(terminal_delegate) - records.set_content(terminal_view) - - scroll_widget.setWidgetResizable(True) - scroll_widget.setWidget(contents_widget) - - main_layout.setContentsMargins(0, 0, 0, 0) - main_layout.setSpacing(0) - main_layout.addWidget(scroll_widget) - self.setLayout(main_layout) - - self.terminal_view = terminal_view - self.terminal_model = terminal_model - self.terminal_proxy = terminal_proxy - - self.indicator = indicator - self.scroll_widget = scroll_widget - self.contents_widget = contents_widget - self.toggle_button = toggle_button - self.name_widget = name - self.documentation = documentation - self.path = path - self.records = records - - self.toggle_button.clicked.connect(self.toggle_me) - - self.last_type = None - self.last_item_id = None - self.last_id = None - - def trim(self, docstring): - if not docstring: - return "" - # Convert tabs to spaces (following the normal Python rules) - # and split into a list of lines: - lines = docstring.expandtabs().splitlines() - # Determine minimum indentation (first line doesn't count): - try: - indent = sys.maxint - max = sys.maxint - except Exception: - indent = sys.maxsize - max = sys.maxsize - - for line in lines[1:]: - stripped = line.lstrip() - if stripped: - indent = min(indent, len(line) - len(stripped)) - # Remove indentation (first line is special): - trimmed = [lines[0].strip()] - if indent < max: - for line in lines[1:]: - trimmed.append(line[indent:].rstrip()) - # Strip off trailing and leading blank lines: - while trimmed and not trimmed[-1]: - trimmed.pop() - while trimmed and not trimmed[0]: - trimmed.pop(0) - # Return a single string: - return "\n".join(trimmed) - - def set_indicator_state(self, state): - self.indicator.setProperty("state", state) - self.indicator.style().polish(self.indicator) - - def reset(self): - self.last_id = None - self.set_records(list()) - self.set_indicator_state(None) - - def update_context(self, plugin_item, instance_item): - if not self.last_item_id or not self.last_type: - return - - if self.last_type == model.PluginType: - if not self.last_id: - _item_id = plugin_item.data(Roles.ObjectUIdRole) - if _item_id != self.last_item_id: - return - self.last_id = plugin_item.plugin.id - - elif self.last_id != plugin_item.plugin.id: - return - - self.set_context(plugin_item.index()) - return - - if self.last_type == model.InstanceType: - if not self.last_id: - _item_id = instance_item.data(Roles.ObjectUIdRole) - if _item_id != self.last_item_id: - return - self.last_id = instance_item.instance.id - - elif self.last_id != instance_item.instance.id: - return - - self.set_context(instance_item.index()) - return - - def set_context(self, index): - if not index or not index.isValid(): - index_type = None - else: - index_type = index.data(Roles.TypeRole) - - if index_type == model.InstanceType: - item_id = index.data(Roles.ObjectIdRole) - publish_states = index.data(Roles.PublishFlagsRole) - if publish_states & InstanceStates.ContextType: - type_indicator = "C" - else: - type_indicator = "I" - - if publish_states & InstanceStates.InProgress: - self.set_indicator_state("active") - - elif publish_states & InstanceStates.HasError: - self.set_indicator_state("error") - - elif publish_states & InstanceStates.HasWarning: - self.set_indicator_state("warning") - - elif publish_states & InstanceStates.HasFinished: - self.set_indicator_state("ok") - else: - self.set_indicator_state(None) - - self.documentation.setVisible(False) - self.path.setVisible(False) - - elif index_type == model.PluginType: - item_id = index.data(Roles.ObjectIdRole) - type_indicator = "P" - - doc = index.data(Roles.DocstringRole) - doc_str = "" - if doc: - doc_str = self.trim(doc) - - publish_states = index.data(Roles.PublishFlagsRole) - if publish_states & PluginStates.InProgress: - self.set_indicator_state("active") - - elif publish_states & PluginStates.HasError: - self.set_indicator_state("error") - - elif publish_states & PluginStates.HasWarning: - self.set_indicator_state("warning") - - elif publish_states & PluginStates.WasProcessed: - self.set_indicator_state("ok") - - else: - self.set_indicator_state(None) - - self.documentation.toggle_content(bool(doc_str)) - self.documentation.content.setText(doc_str) - - path = index.data(Roles.PathModuleRole) or "" - self.path.toggle_content(path.strip() != "") - self.path.content.setText(path) - - self.documentation.setVisible(True) - self.path.setVisible(True) - - else: - self.last_type = None - self.last_id = None - self.indicator.setText("?") - self.set_indicator_state(None) - self.documentation.setVisible(False) - self.path.setVisible(False) - self.records.setVisible(False) - return - - self.last_type = index_type - self.last_id = item_id - self.last_item_id = index.data(Roles.ObjectUIdRole) - - self.indicator.setText(type_indicator) - - label = index.data(QtCore.Qt.DisplayRole) - self.name_widget.setText(label) - self.records.setVisible(True) - - records = index.data(Roles.LogRecordsRole) or [] - self.set_records(records) - - def set_records(self, records): - len_records = 0 - if records: - len_records += len(records) - - data = {"records": records} - self.terminal_model.reset() - self.terminal_model.update_with_result(data) - - self.records.button_toggle_text.setText( - "{} ({})".format(self.l_rec, len_records) - ) - self.records.toggle_content(len_records > 0) - - def toggle_me(self): - self.parent_widget.parent().toggle_perspective_widget() - - -class ClickableWidget(QtWidgets.QLabel): - clicked = QtCore.Signal() - - def mouseReleaseEvent(self, event): - if event.button() == QtCore.Qt.LeftButton: - self.clicked.emit() - super(ClickableWidget, self).mouseReleaseEvent(event) - - -class ExpandableWidget(QtWidgets.QWidget): - - content = None - - def __init__(self, parent, title): - super(ExpandableWidget, self).__init__(parent) - - top_part = ClickableWidget(parent=self) - top_part.setObjectName("ExpandableHeader") - - button_size = QtCore.QSize(5, 5) - button_toggle = QtWidgets.QToolButton(parent=top_part) - button_toggle.setIconSize(button_size) - button_toggle.setArrowType(QtCore.Qt.RightArrow) - button_toggle.setCheckable(True) - button_toggle.setChecked(False) - - button_toggle_text = QtWidgets.QLabel(title, parent=top_part) - - layout = QtWidgets.QHBoxLayout(top_part) - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(5) - layout.addWidget(button_toggle) - layout.addWidget(button_toggle_text) - top_part.setLayout(layout) - - main_layout = QtWidgets.QVBoxLayout(self) - main_layout.setContentsMargins(9, 9, 9, 0) - - content = QtWidgets.QFrame(self) - content.setObjectName("ExpandableWidgetContent") - content.setVisible(False) - - content_layout = QtWidgets.QVBoxLayout(content) - - main_layout.addWidget(top_part) - main_layout.addWidget(content) - self.setLayout(main_layout) - - self.setAttribute(QtCore.Qt.WA_StyledBackground) - - self.top_part = top_part - self.button_toggle = button_toggle - self.button_toggle_text = button_toggle_text - - self.content_widget = content - self.content_layout = content_layout - - self.top_part.clicked.connect(self.top_part_clicked) - self.button_toggle.clicked.connect(self.toggle_content) - - def top_part_clicked(self): - self.toggle_content(not self.button_toggle.isChecked()) - - def toggle_content(self, *args): - if len(args) > 0: - checked = args[0] - else: - checked = self.button_toggle.isChecked() - arrow_type = QtCore.Qt.RightArrow - if checked: - arrow_type = QtCore.Qt.DownArrow - self.button_toggle.setChecked(checked) - self.button_toggle.setArrowType(arrow_type) - self.content_widget.setVisible(checked) - - def resizeEvent(self, event): - super(ExpandableWidget, self).resizeEvent(event) - self.content.updateGeometry() - - def set_content(self, in_widget): - if self.content: - self.content.hide() - self.content_layout.removeWidget(self.content) - self.content_layout.addWidget(in_widget) - self.content = in_widget - - -class ButtonWithMenu(QtWidgets.QWidget): - def __init__(self, button_title, parent=None): - super(ButtonWithMenu, self).__init__(parent=parent) - self.setSizePolicy(QtWidgets.QSizePolicy( - QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum - )) - - self.layout = QtWidgets.QHBoxLayout(self) - self.layout.setContentsMargins(0, 0, 0, 0) - self.layout.setSpacing(0) - - self.menu = QtWidgets.QMenu() - # TODO move to stylesheets - self.menu.setStyleSheet(""" - *{color: #fff; background-color: #555; border: 1px solid #222;} - ::item {background-color: transparent;padding: 5px; - padding-left: 10px;padding-right: 10px;} - ::item:selected {background-color: #666;} - """) - - self.button = QtWidgets.QPushButton(button_title) - self.button.setObjectName("ButtonWithMenu") - - self.layout.addWidget(self.button) - - self.button.clicked.connect(self.btn_clicked) - - def btn_clicked(self): - self.menu.popup(self.button.mapToGlobal( - QtCore.QPoint(0, self.button.height()) - )) - - def addItem(self, text, callback): - self.menu.addAction(text, callback) - self.button.setToolTip("Select to apply predefined presets") - - def clearMenu(self): - self.menu.clear() - self.button.setToolTip("Presets not found") - - -class CommentBox(QtWidgets.QLineEdit): - - def __init__(self, placeholder_text, parent=None): - super(CommentBox, self).__init__(parent=parent) - self.placeholder = QtWidgets.QLabel(placeholder_text, self) - self.placeholder.move(2, 2) - - def focusInEvent(self, event): - self.placeholder.setVisible(False) - return super(CommentBox, self).focusInEvent(event) - - def focusOutEvent(self, event): - current_text = self.text() - current_text = current_text.strip(" ") - self.setText(current_text) - if not self.text(): - self.placeholder.setVisible(True) - return super(CommentBox, self).focusOutEvent(event) - - -class TerminalDetail(QtWidgets.QTextEdit): - def __init__(self, text, *args, **kwargs): - super(TerminalDetail, self).__init__(*args, **kwargs) - - self.setReadOnly(True) - self.setHtml(text) - self.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction) - self.setWordWrapMode( - QtGui.QTextOption.WrapAtWordBoundaryOrAnywhere - ) - - def sizeHint(self): - content_margins = ( - self.contentsMargins().top() - + self.contentsMargins().bottom() - ) - size = self.document().documentLayout().documentSize().toSize() - size.setHeight(size.height() + content_margins) - return size - - -class FilterButton(QtWidgets.QPushButton): - def __init__(self, name, *args, **kwargs): - self.filter_name = name - - super(FilterButton, self).__init__(*args, **kwargs) - - self.toggled.connect(self.on_toggle) - - self.setProperty("type", name) - self.setObjectName("TerminalFilerBtn") - self.setCheckable(True) - self.setChecked( - model.TerminalProxy.filter_buttons_checks[name] - ) - - def on_toggle(self, toggle_state): - model.TerminalProxy.change_filter(self.filter_name, toggle_state) - - -class TerminalFilterWidget(QtWidgets.QWidget): - # timer.timeout.connect(lambda: self._update(self.parent_widget)) - def __init__(self, *args, **kwargs): - super(TerminalFilterWidget, self).__init__(*args, **kwargs) - self.setObjectName("TerminalFilterWidget") - self.filter_changed = QtCore.Signal() - - info_icon = awesome.tags["info"] - log_icon = awesome.tags["circle"] - error_icon = awesome.tags["exclamation-triangle"] - - filter_buttons = ( - FilterButton("info", info_icon, self), - FilterButton("log_debug", log_icon, self), - FilterButton("log_info", log_icon, self), - FilterButton("log_warning", log_icon, self), - FilterButton("log_error", log_icon, self), - FilterButton("log_critical", log_icon, self), - FilterButton("error", error_icon, self) - ) - - layout = QtWidgets.QHBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - # Add spacers - spacer = QtWidgets.QWidget() - spacer.setAttribute(QtCore.Qt.WA_TranslucentBackground) - layout.addWidget(spacer, 1) - - for btn in filter_buttons: - layout.addWidget(btn) - - self.setLayout(layout) - - self.filter_buttons = filter_buttons diff --git a/client/ayon_core/tools/pyblish_pype/window.py b/client/ayon_core/tools/pyblish_pype/window.py deleted file mode 100644 index 01d373d841..0000000000 --- a/client/ayon_core/tools/pyblish_pype/window.py +++ /dev/null @@ -1,1315 +0,0 @@ -"""Main Window - -States: - These are all possible states and their transitions. - - - reset - ' - ' - ' - ___v__ - | | reset - | Idle |--------------------. - | |<-------------------' - | | - | | _____________ - | | validate | | reset # TODO - | |----------------->| In-progress |-----------. - | | |_____________| ' - | |<-------------------------------------------' - | | - | | _____________ - | | publish | | - | |----------------->| In-progress |---. - | | |_____________| ' - | |<-----------------------------------' - |______| - - -Todo: - There are notes spread throughout this project with the syntax: - - - TODO(username) - - The `username` is a quick and dirty indicator of who made the note - and is by no means exclusive to that person in terms of seeing it - done. Feel free to do, or make your own TODO's as you code. Just - make sure the description is sufficient for anyone reading it for - the first time to understand how to actually to it! - -""" -import sys -from functools import partial - -from . import delegate, model, settings, util, view, widgets -from .awesome import tags as awesome - -from qtpy import QtCore, QtGui, QtWidgets -from .constants import ( - PluginStates, PluginActionStates, InstanceStates, GroupStates, Roles -) -if sys.version_info[0] == 3: - from queue import Queue -else: - from Queue import Queue - - -class Window(QtWidgets.QDialog): - def __init__(self, controller, parent=None): - super(Window, self).__init__(parent=parent) - - self._suspend_logs = False - - # Use plastique style for specific ocations - # TODO set style name via environment variable - low_keys = { - key.lower(): key - for key in QtWidgets.QStyleFactory.keys() - } - if "plastique" in low_keys: - self.setStyle( - QtWidgets.QStyleFactory.create(low_keys["plastique"]) - ) - - icon = QtGui.QIcon(util.get_asset("img", "logo-extrasmall.png")) - if parent is None: - on_top_flag = QtCore.Qt.WindowStaysOnTopHint - else: - on_top_flag = QtCore.Qt.Dialog - - self.setWindowFlags( - self.windowFlags() - | QtCore.Qt.WindowTitleHint - | QtCore.Qt.WindowMaximizeButtonHint - | QtCore.Qt.WindowMinimizeButtonHint - | QtCore.Qt.WindowCloseButtonHint - | on_top_flag - ) - self.setWindowIcon(icon) - self.setAttribute(QtCore.Qt.WA_DeleteOnClose) - - self.controller = controller - - main_widget = QtWidgets.QWidget(self) - - # General layout - header_widget = QtWidgets.QWidget(parent=main_widget) - - header_tab_widget = QtWidgets.QWidget(header_widget) - header_tab_overview = QtWidgets.QRadioButton(header_tab_widget) - header_tab_terminal = QtWidgets.QRadioButton(header_tab_widget) - header_spacer = QtWidgets.QWidget(header_tab_widget) - - button_suspend_logs_widget = QtWidgets.QWidget() - button_suspend_logs_widget_layout = QtWidgets.QHBoxLayout( - button_suspend_logs_widget - ) - button_suspend_logs_widget_layout.setContentsMargins(0, 10, 0, 10) - button_suspend_logs = QtWidgets.QPushButton(header_widget) - button_suspend_logs.setFixedWidth(7) - button_suspend_logs.setSizePolicy( - QtWidgets.QSizePolicy.Preferred, - QtWidgets.QSizePolicy.Expanding - ) - button_suspend_logs_widget_layout.addWidget(button_suspend_logs) - header_aditional_btns = QtWidgets.QWidget(header_tab_widget) - - aditional_btns_layout = QtWidgets.QHBoxLayout(header_aditional_btns) - - presets_button = widgets.ButtonWithMenu(awesome["filter"]) - presets_button.setEnabled(False) - aditional_btns_layout.addWidget(presets_button) - - layout_tab = QtWidgets.QHBoxLayout(header_tab_widget) - layout_tab.setContentsMargins(0, 0, 0, 0) - layout_tab.setSpacing(0) - layout_tab.addWidget(header_tab_overview, 0) - layout_tab.addWidget(header_tab_terminal, 0) - layout_tab.addWidget(button_suspend_logs_widget, 0) - - # Compress items to the left - layout_tab.addWidget(header_spacer, 1) - layout_tab.addWidget(header_aditional_btns, 0) - - layout = QtWidgets.QHBoxLayout(header_widget) - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(0) - layout.addWidget(header_tab_widget) - - header_widget.setLayout(layout) - - # Overview Page - # TODO add parent - overview_page = QtWidgets.QWidget() - - overview_instance_view = view.InstanceView(parent=overview_page) - overview_instance_view.setAnimated(settings.Animated) - overview_instance_delegate = delegate.InstanceDelegate( - parent=overview_instance_view - ) - instance_model = model.InstanceModel(controller) - instance_sort_proxy = model.InstanceSortProxy() - instance_sort_proxy.setSourceModel(instance_model) - - overview_instance_view.setItemDelegate(overview_instance_delegate) - overview_instance_view.setModel(instance_sort_proxy) - - overview_plugin_view = view.PluginView(parent=overview_page) - overview_plugin_view.setAnimated(settings.Animated) - overview_plugin_delegate = delegate.PluginDelegate( - parent=overview_plugin_view - ) - overview_plugin_view.setItemDelegate(overview_plugin_delegate) - plugin_model = model.PluginModel(controller) - plugin_proxy = model.PluginFilterProxy() - plugin_proxy.setSourceModel(plugin_model) - overview_plugin_view.setModel(plugin_proxy) - - layout = QtWidgets.QHBoxLayout(overview_page) - layout.addWidget(overview_instance_view, 1) - layout.addWidget(overview_plugin_view, 1) - layout.setContentsMargins(5, 5, 5, 5) - layout.setSpacing(0) - overview_page.setLayout(layout) - - # Terminal - terminal_container = QtWidgets.QWidget() - - terminal_view = view.TerminalView() - terminal_model = model.TerminalModel() - terminal_proxy = model.TerminalProxy(terminal_view) - terminal_proxy.setSourceModel(terminal_model) - - terminal_view.setModel(terminal_proxy) - terminal_delegate = delegate.TerminalItem() - terminal_view.setItemDelegate(terminal_delegate) - - layout = QtWidgets.QVBoxLayout(terminal_container) - layout.addWidget(terminal_view) - layout.setContentsMargins(5, 5, 5, 5) - layout.setSpacing(0) - - terminal_container.setLayout(layout) - - terminal_page = QtWidgets.QWidget() - layout = QtWidgets.QVBoxLayout(terminal_page) - layout.addWidget(terminal_container) - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(0) - - # Add some room between window borders and contents - body_widget = QtWidgets.QWidget(main_widget) - layout = QtWidgets.QHBoxLayout(body_widget) - layout.setContentsMargins(5, 5, 5, 1) - layout.addWidget(overview_page) - layout.addWidget(terminal_page) - - # Comment Box - comment_box = widgets.CommentBox("Comment...", self) - - intent_box = QtWidgets.QComboBox() - - intent_model = model.IntentModel() - intent_box.setModel(intent_model) - - comment_intent_widget = QtWidgets.QWidget() - comment_intent_layout = QtWidgets.QHBoxLayout(comment_intent_widget) - comment_intent_layout.setContentsMargins(0, 0, 0, 0) - comment_intent_layout.setSpacing(5) - comment_intent_layout.addWidget(comment_box) - comment_intent_layout.addWidget(intent_box) - - # Terminal filtering - terminal_filters_widget = widgets.TerminalFilterWidget() - - # Footer - footer_widget = QtWidgets.QWidget(main_widget) - - footer_info = QtWidgets.QLabel(footer_widget) - footer_spacer = QtWidgets.QWidget(footer_widget) - - footer_button_stop = QtWidgets.QPushButton( - awesome["stop"], footer_widget - ) - footer_button_stop.setToolTip("Stop publishing") - footer_button_reset = QtWidgets.QPushButton( - awesome["refresh"], footer_widget - ) - footer_button_reset.setToolTip("Restart publishing") - footer_button_validate = QtWidgets.QPushButton( - awesome["flask"], footer_widget - ) - footer_button_validate.setToolTip("Run validations") - footer_button_play = QtWidgets.QPushButton( - awesome["play"], footer_widget - ) - footer_button_play.setToolTip("Publish") - layout = QtWidgets.QHBoxLayout() - layout.setContentsMargins(5, 5, 5, 5) - layout.addWidget(footer_info, 0) - layout.addWidget(footer_spacer, 1) - - layout.addWidget(footer_button_stop, 0) - layout.addWidget(footer_button_reset, 0) - layout.addWidget(footer_button_validate, 0) - layout.addWidget(footer_button_play, 0) - - footer_layout = QtWidgets.QVBoxLayout(footer_widget) - footer_layout.addWidget(terminal_filters_widget) - footer_layout.addWidget(comment_intent_widget) - footer_layout.addLayout(layout) - - footer_widget.setProperty("success", -1) - - # Placeholder for when GUI is closing - # TODO(marcus): Fade to black and the the user about what's happening - closing_placeholder = QtWidgets.QWidget(main_widget) - closing_placeholder.setSizePolicy( - QtWidgets.QSizePolicy.Expanding, - QtWidgets.QSizePolicy.Expanding - ) - closing_placeholder.hide() - - perspective_widget = widgets.PerspectiveWidget(main_widget) - perspective_widget.hide() - - pages_widget = QtWidgets.QWidget(main_widget) - layout = QtWidgets.QVBoxLayout(pages_widget) - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(0) - layout.addWidget(header_widget, 0) - layout.addWidget(body_widget, 1) - - # Main layout - layout = QtWidgets.QVBoxLayout(main_widget) - layout.addWidget(pages_widget, 3) - layout.addWidget(perspective_widget, 3) - layout.addWidget(closing_placeholder, 1) - layout.addWidget(footer_widget, 0) - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(0) - main_widget.setLayout(layout) - - self.main_layout = QtWidgets.QVBoxLayout(self) - self.main_layout.setContentsMargins(0, 0, 0, 0) - self.main_layout.setSpacing(0) - self.main_layout.addWidget(main_widget) - - """Setup - - Widgets are referred to in CSS via their object-name. We - use the same mechanism internally to refer to objects; so rather - than storing widgets as self.my_widget, it is referred to as: - - >>> my_widget = self.findChild(QtWidgets.QWidget, "MyWidget") - - This way there is only ever a single method of referring to any widget. - """ - - names = { - # Main - "Header": header_widget, - "Body": body_widget, - "Footer": footer_widget, - - # Pages - "Overview": overview_page, - "Terminal": terminal_page, - - # Tabs - "OverviewTab": header_tab_overview, - "TerminalTab": header_tab_terminal, - - # Views - "TerminalView": terminal_view, - - # Buttons - "SuspendLogsBtn": button_suspend_logs, - "Stop": footer_button_stop, - "Reset": footer_button_reset, - "Validate": footer_button_validate, - "Play": footer_button_play, - - # Misc - "HeaderSpacer": header_spacer, - "FooterSpacer": footer_spacer, - "FooterInfo": footer_info, - "CommentIntentWidget": comment_intent_widget, - "CommentBox": comment_box, - "CommentPlaceholder": comment_box.placeholder, - "ClosingPlaceholder": closing_placeholder, - "IntentBox": intent_box - } - - for name, _widget in names.items(): - _widget.setObjectName(name) - - # Enable CSS on plain QWidget objects - for _widget in ( - pages_widget, - header_widget, - body_widget, - comment_box, - overview_page, - terminal_page, - footer_widget, - button_suspend_logs, - footer_button_stop, - footer_button_reset, - footer_button_validate, - footer_button_play, - footer_spacer, - closing_placeholder - ): - _widget.setAttribute(QtCore.Qt.WA_StyledBackground) - - # Signals - header_tab_overview.toggled.connect( - lambda: self.on_tab_changed("overview") - ) - header_tab_terminal.toggled.connect( - lambda: self.on_tab_changed("terminal") - ) - - overview_instance_view.show_perspective.connect( - self.toggle_perspective_widget - ) - overview_plugin_view.show_perspective.connect( - self.toggle_perspective_widget - ) - - controller.switch_toggleability.connect(self.change_toggleability) - - controller.was_reset.connect(self.on_was_reset) - # This is called synchronously on each process - controller.was_processed.connect(self.on_was_processed) - controller.passed_group.connect(self.on_passed_group) - controller.was_stopped.connect(self.on_was_stopped) - controller.was_finished.connect(self.on_was_finished) - - controller.was_skipped.connect(self.on_was_skipped) - controller.was_acted.connect(self.on_was_acted) - - # NOTE: Listeners to this signal are run in the main thread - controller.about_to_process.connect( - self.on_about_to_process, - QtCore.Qt.DirectConnection - ) - - overview_instance_view.toggled.connect(self.on_instance_toggle) - overview_plugin_view.toggled.connect(self.on_plugin_toggle) - - button_suspend_logs.clicked.connect(self.on_suspend_clicked) - footer_button_stop.clicked.connect(self.on_stop_clicked) - footer_button_reset.clicked.connect(self.on_reset_clicked) - footer_button_validate.clicked.connect(self.on_validate_clicked) - footer_button_play.clicked.connect(self.on_play_clicked) - - comment_box.textChanged.connect(self.on_comment_entered) - comment_box.returnPressed.connect(self.on_play_clicked) - overview_plugin_view.customContextMenuRequested.connect( - self.on_plugin_action_menu_requested - ) - - instance_model.group_created.connect(self.on_instance_group_created) - - self.main_widget = main_widget - - self.pages_widget = pages_widget - self.header_widget = header_widget - self.body_widget = body_widget - - self.terminal_filters_widget = terminal_filters_widget - - self.footer_widget = footer_widget - self.button_suspend_logs = button_suspend_logs - self.footer_button_stop = footer_button_stop - self.footer_button_reset = footer_button_reset - self.footer_button_validate = footer_button_validate - self.footer_button_play = footer_button_play - - self.footer_info = footer_info - - self.overview_instance_view = overview_instance_view - self.overview_plugin_view = overview_plugin_view - self.plugin_model = plugin_model - self.plugin_proxy = plugin_proxy - self.instance_model = instance_model - self.instance_sort_proxy = instance_sort_proxy - - self.presets_button = presets_button - - self.terminal_model = terminal_model - self.terminal_proxy = terminal_proxy - self.terminal_view = terminal_view - - self.comment_main_widget = comment_intent_widget - self.comment_box = comment_box - self.intent_box = intent_box - self.intent_model = intent_model - - self.perspective_widget = perspective_widget - - self.tabs = { - "overview": header_tab_overview, - "terminal": header_tab_terminal - } - self.pages = ( - ("overview", overview_page), - ("terminal", terminal_page) - ) - - current_page = settings.InitialTab or "overview" - self.comment_main_widget.setVisible( - not current_page == "terminal" - ) - self.terminal_filters_widget.setVisible( - current_page == "terminal" - ) - - self._current_page = current_page - self._hidden_for_plugin_process = False - - self.tabs[current_page].setChecked(True) - - self.apply_log_suspend_value( - util.env_variable_to_bool("PYBLISH_SUSPEND_LOGS") - ) - - # ------------------------------------------------------------------------- - # - # Event handlers - # - # ------------------------------------------------------------------------- - def set_presets(self, key): - plugin_settings = self.controller.possible_presets.get(key) - if not plugin_settings: - return - - for plugin_item in self.plugin_model.plugin_items.values(): - if not plugin_item.plugin.optional: - continue - - value = plugin_settings.get( - plugin_item.plugin.__name__, - # if plugin is not in presets then set default value - self.controller.optional_default.get( - plugin_item.plugin.__name__ - ) - ) - if value is None: - continue - - plugin_item.setData(value, QtCore.Qt.CheckStateRole) - - def toggle_perspective_widget(self, index=None): - show = False - if index: - show = True - self.perspective_widget.set_context(index) - - self.pages_widget.setVisible(not show) - self.perspective_widget.setVisible(show) - self.footer_items_visibility() - - def change_toggleability(self, enable_value): - for plugin_item in self.plugin_model.plugin_items.values(): - plugin_item.setData(enable_value, Roles.IsEnabledRole) - - for instance_item in ( - self.instance_model.instance_items.values() - ): - instance_item.setData(enable_value, Roles.IsEnabledRole) - - def _add_intent_to_context(self): - context_value = None - if ( - self.intent_model.has_items - and "intent" not in self.controller.context.data - ): - idx = self.intent_model.index(self.intent_box.currentIndex(), 0) - intent_value = self.intent_model.data(idx, Roles.IntentItemValue) - intent_label = self.intent_model.data(idx, QtCore.Qt.DisplayRole) - if intent_value: - context_value = { - "value": intent_value, - "label": intent_label - } - - # Unset intent if is set to empty value - if context_value is None: - self.controller.context.data.pop("intent", None) - else: - self.controller.context.data["intent"] = context_value - - def on_instance_toggle(self, index, state=None): - """An item is requesting to be toggled""" - if not index.data(Roles.IsOptionalRole): - return self.info("This item is mandatory") - - if self.controller.collect_state != 1: - return self.info("Cannot toggle") - - current_state = index.data(QtCore.Qt.CheckStateRole) - if state is None: - state = not current_state - - instance_id = index.data(Roles.ObjectIdRole) - instance_item = self.instance_model.instance_items[instance_id] - instance_item.setData(state, QtCore.Qt.CheckStateRole) - - self.controller.instance_toggled.emit( - instance_item.instance, current_state, state - ) - - self.update_compatibility() - - def on_instance_group_created(self, index): - _index = self.instance_sort_proxy.mapFromSource(index) - self.overview_instance_view.expand(_index) - - def on_plugin_toggle(self, index, state=None): - """An item is requesting to be toggled""" - if not index.data(Roles.IsOptionalRole): - return self.info("This item is mandatory") - - if self.controller.collect_state != 1: - return self.info("Cannot toggle") - - if state is None: - state = not index.data(QtCore.Qt.CheckStateRole) - - plugin_id = index.data(Roles.ObjectIdRole) - plugin_item = self.plugin_model.plugin_items[plugin_id] - plugin_item.setData(state, QtCore.Qt.CheckStateRole) - - self.update_compatibility() - - def on_tab_changed(self, target): - previous_page = None - target_page = None - direction = None - for name, page in self.pages: - if name == target: - target_page = page - if direction is None: - direction = -1 - elif name == self._current_page: - previous_page = page - if direction is None: - direction = 1 - else: - page.setVisible(False) - - self._current_page = target - self.slide_page(previous_page, target_page, direction) - - def slide_page(self, previous_page, target_page, direction): - if previous_page is None: - for name, page in self.pages: - for _name, _page in self.pages: - if name != _name: - _page.hide() - page.show() - page.hide() - - if ( - previous_page == target_page - or previous_page is None - ): - if not target_page.isVisible(): - target_page.show() - return - - if not settings.Animated: - previous_page.setVisible(False) - target_page.setVisible(True) - return - - width = previous_page.frameGeometry().width() - offset = QtCore.QPoint(direction * width, 0) - - previous_rect = ( - previous_page.frameGeometry().x(), - previous_page.frameGeometry().y(), - width, - previous_page.frameGeometry().height() - ) - curr_pos = previous_page.pos() - - previous_page.hide() - target_page.show() - target_page.update() - target_rect = ( - target_page.frameGeometry().x(), - target_page.frameGeometry().y(), - target_page.frameGeometry().width(), - target_page.frameGeometry().height() - ) - previous_page.show() - - target_page.raise_() - previous_page.setGeometry(*previous_rect) - target_page.setGeometry(*target_rect) - - target_page.move(curr_pos + offset) - - duration = 250 - - anim_old = QtCore.QPropertyAnimation( - previous_page, b"pos", self - ) - anim_old.setDuration(duration) - anim_old.setStartValue(curr_pos) - anim_old.setEndValue(curr_pos - offset) - anim_old.setEasingCurve(QtCore.QEasingCurve.OutQuad) - - anim_new = QtCore.QPropertyAnimation( - target_page, b"pos", self - ) - anim_new.setDuration(duration) - anim_new.setStartValue(curr_pos + offset) - anim_new.setEndValue(curr_pos) - anim_new.setEasingCurve(QtCore.QEasingCurve.OutQuad) - - anim_group = QtCore.QParallelAnimationGroup(self) - anim_group.addAnimation(anim_old) - anim_group.addAnimation(anim_new) - - def slide_finished(): - previous_page.hide() - self.footer_items_visibility() - - anim_group.finished.connect(slide_finished) - anim_group.start() - - def footer_items_visibility( - self, - comment_visible=None, - terminal_filters_visibile=None - ): - target = self._current_page - comment_visibility = ( - not self.perspective_widget.isVisible() - and not target == "terminal" - and self.comment_box.isEnabled() - ) - terminal_filters_visibility = ( - target == "terminal" - or self.perspective_widget.isVisible() - ) - - if comment_visible is not None and comment_visibility: - comment_visibility = comment_visible - - if ( - terminal_filters_visibile is not None - and terminal_filters_visibility - ): - terminal_filters_visibility = terminal_filters_visibile - - duration = 150 - - hiding_widgets = [] - showing_widgets = [] - if (comment_visibility != ( - self.comment_main_widget.isVisible() - )): - if self.comment_main_widget.isVisible(): - hiding_widgets.append(self.comment_main_widget) - else: - showing_widgets.append(self.comment_main_widget) - - if (terminal_filters_visibility != ( - self.terminal_filters_widget.isVisible() - )): - if self.terminal_filters_widget.isVisible(): - hiding_widgets.append(self.terminal_filters_widget) - else: - showing_widgets.append(self.terminal_filters_widget) - - if not hiding_widgets and not showing_widgets: - return - - hiding_widgets_queue = Queue() - showing_widgets_queue = Queue() - widgets_by_pos_y = {} - for widget in hiding_widgets: - key = widget.mapToGlobal(widget.rect().topLeft()).x() - widgets_by_pos_y[key] = widget - - for key in sorted(widgets_by_pos_y.keys()): - widget = widgets_by_pos_y[key] - hiding_widgets_queue.put((widget, )) - - for widget in hiding_widgets: - widget.hide() - - for widget in showing_widgets: - widget.show() - - self.footer_widget.updateGeometry() - widgets_by_pos_y = {} - for widget in showing_widgets: - key = widget.mapToGlobal(widget.rect().topLeft()).x() - widgets_by_pos_y[key] = widget - - for key in reversed(sorted(widgets_by_pos_y.keys())): - widget = widgets_by_pos_y[key] - showing_widgets_queue.put(widget) - - for widget in showing_widgets: - widget.hide() - - for widget in hiding_widgets: - widget.show() - - def process_showing(): - if showing_widgets_queue.empty(): - return - - widget = showing_widgets_queue.get() - widget.show() - - widget_rect = widget.frameGeometry() - second_rect = QtCore.QRect(widget_rect) - second_rect.setTopLeft(second_rect.bottomLeft()) - - animation = QtCore.QPropertyAnimation( - widget, b"geometry", self - ) - animation.setDuration(duration) - animation.setStartValue(second_rect) - animation.setEndValue(widget_rect) - animation.setEasingCurve(QtCore.QEasingCurve.OutQuad) - - animation.finished.connect(process_showing) - animation.start() - - def process_hiding(): - if hiding_widgets_queue.empty(): - return process_showing() - - item = hiding_widgets_queue.get() - if isinstance(item, tuple): - widget = item[0] - hiding_widgets_queue.put(widget) - widget_rect = widget.frameGeometry() - second_rect = QtCore.QRect(widget_rect) - second_rect.setTopLeft(second_rect.bottomLeft()) - - anim = QtCore.QPropertyAnimation( - widget, b"geometry", self - ) - anim.setDuration(duration) - anim.setStartValue(widget_rect) - anim.setEndValue(second_rect) - anim.setEasingCurve(QtCore.QEasingCurve.OutQuad) - - anim.finished.connect(process_hiding) - anim.start() - else: - item.hide() - return process_hiding() - - process_hiding() - - def on_validate_clicked(self): - self.comment_box.setEnabled(False) - self.footer_items_visibility() - self.intent_box.setEnabled(False) - - self._add_intent_to_context() - - self.validate() - - def on_play_clicked(self): - self.comment_box.setEnabled(False) - self.footer_items_visibility() - self.intent_box.setEnabled(False) - - self._add_intent_to_context() - - self.publish() - - def on_reset_clicked(self): - self.reset() - - def on_stop_clicked(self): - self.info("Stopping..") - self.controller.stop() - - # TODO checks - self.footer_button_reset.setEnabled(True) - self.footer_button_play.setEnabled(False) - self.footer_button_stop.setEnabled(False) - - def on_suspend_clicked(self, value=None): - self.apply_log_suspend_value(not self._suspend_logs) - - def apply_log_suspend_value(self, value): - self._suspend_logs = value - if self._current_page == "terminal": - self.tabs["overview"].setChecked(True) - - self.tabs["terminal"].setVisible(not self._suspend_logs) - - def on_comment_entered(self): - """The user has typed a comment.""" - self.controller.context.data["comment"] = self.comment_box.text() - - def on_about_to_process(self, plugin, instance): - """Reflect currently running pair in GUI""" - if instance is None: - instance_id = self.controller.context.id - else: - instance_id = instance.id - - instance_item = ( - self.instance_model.instance_items[instance_id] - ) - instance_item.setData( - {InstanceStates.InProgress: True}, - Roles.PublishFlagsRole - ) - - plugin_item = self.plugin_model.plugin_items[plugin._id] - plugin_item.setData( - {PluginStates.InProgress: True}, - Roles.PublishFlagsRole - ) - - self.info("{} {}".format( - self.tr("Processing"), plugin_item.data(QtCore.Qt.DisplayRole) - )) - - visibility = True - if hasattr(plugin, "hide_ui_on_process") and plugin.hide_ui_on_process: - visibility = False - self._hidden_for_plugin_process = not visibility - - self._ensure_visible(visibility) - - def _ensure_visible(self, visible): - if self.isVisible() == visible: - return - - if not visible: - self.setVisible(visible) - else: - self.show() - self.raise_() - self.activateWindow() - self.showNormal() - - def on_plugin_action_menu_requested(self, pos): - """The user right-clicked on a plug-in - __________ - | | - | Action 1 | - | Action 2 | - | Action 3 | - | | - |__________| - - """ - - index = self.overview_plugin_view.indexAt(pos) - actions = index.data(Roles.PluginValidActionsRole) - - if not actions: - return - - menu = QtWidgets.QMenu(self) - plugin_id = index.data(Roles.ObjectIdRole) - plugin_item = self.plugin_model.plugin_items[plugin_id] - print("plugin is: %s" % plugin_item.plugin) - - for action in actions: - qaction = QtWidgets.QAction(action.label or action.__name__, self) - qaction.triggered.connect(partial(self.act, plugin_item, action)) - menu.addAction(qaction) - - menu.popup(self.overview_plugin_view.viewport().mapToGlobal(pos)) - - def update_compatibility(self): - self.plugin_model.update_compatibility() - self.plugin_proxy.invalidateFilter() - - def on_was_reset(self): - # Append context object to instances model - self.instance_model.append(self.controller.context) - - for plugin in self.controller.plugins: - self.plugin_model.append(plugin) - - self.overview_instance_view.expandAll() - self.overview_plugin_view.expandAll() - - self.presets_button.clearMenu() - if self.controller.possible_presets: - self.presets_button.setEnabled(True) - for key in self.controller.possible_presets: - self.presets_button.addItem( - key, partial(self.set_presets, key) - ) - - self.instance_model.restore_checkstates() - self.plugin_model.restore_checkstates() - - self.perspective_widget.reset() - - # Append placeholder comment from Context - # This allows users to inject a comment from elsewhere, - # or to perhaps provide a placeholder comment/template - # for artists to fill in. - comment = self.controller.context.data.get("comment") - self.comment_box.setText(comment or None) - self.comment_box.setEnabled(True) - self.footer_items_visibility() - - self.intent_box.setEnabled(True) - - # Refresh tab - self.on_tab_changed(self._current_page) - self.update_compatibility() - - self.button_suspend_logs.setEnabled(False) - - self.footer_button_validate.setEnabled(False) - self.footer_button_reset.setEnabled(False) - self.footer_button_stop.setEnabled(True) - self.footer_button_play.setEnabled(False) - - self._update_state() - - def on_passed_group(self, order): - for group_item in self.instance_model.group_items.values(): - group_index = self.instance_sort_proxy.mapFromSource( - group_item.index() - ) - if self.overview_instance_view.isExpanded(group_index): - continue - - if group_item.publish_states & GroupStates.HasError: - self.overview_instance_view.expand(group_index) - - for group_item in self.plugin_model.group_items.values(): - # TODO check only plugins from the group - if group_item.publish_states & GroupStates.HasFinished: - continue - - if order != group_item.order: - continue - - group_index = self.plugin_proxy.mapFromSource(group_item.index()) - if group_item.publish_states & GroupStates.HasError: - self.overview_plugin_view.expand(group_index) - continue - - group_item.setData( - {GroupStates.HasFinished: True}, - Roles.PublishFlagsRole - ) - self.overview_plugin_view.setAnimated(False) - self.overview_plugin_view.collapse(group_index) - - self._update_state() - - def on_was_stopped(self): - self.overview_plugin_view.setAnimated(settings.Animated) - errored = self.controller.errored - if self.controller.collect_state == 0: - self.footer_button_play.setEnabled(False) - self.footer_button_validate.setEnabled(False) - else: - self.footer_button_play.setEnabled(not errored) - self.footer_button_validate.setEnabled( - not errored and not self.controller.validated - ) - self.footer_button_play.setFocus() - - self.footer_button_reset.setEnabled(True) - self.footer_button_stop.setEnabled(False) - if errored: - self.footer_widget.setProperty("success", 0) - self.footer_widget.style().polish(self.footer_widget) - - suspend_log_bool = ( - self.controller.collect_state == 1 - and not self.controller.stopped - ) - self.button_suspend_logs.setEnabled(suspend_log_bool) - - self._update_state() - - if self._hidden_for_plugin_process: - self._hidden_for_plugin_process = False - self._ensure_visible(True) - - def on_was_skipped(self, plugin): - plugin_item = self.plugin_model.plugin_items[plugin.id] - plugin_item.setData( - {PluginStates.WasSkipped: True}, - Roles.PublishFlagsRole - ) - - def on_was_finished(self): - self.overview_plugin_view.setAnimated(settings.Animated) - self.footer_button_play.setEnabled(False) - self.footer_button_validate.setEnabled(False) - self.footer_button_reset.setEnabled(True) - self.footer_button_stop.setEnabled(False) - - if self.controller.errored: - success_val = 0 - self.info(self.tr("Stopped due to error(s), see Terminal.")) - self.comment_box.setEnabled(False) - self.intent_box.setEnabled(False) - - else: - success_val = 1 - self.info(self.tr("Finished successfully!")) - - self.footer_widget.setProperty("success", success_val) - self.footer_widget.style().polish(self.footer_widget) - - for instance_item in ( - self.instance_model.instance_items.values() - ): - instance_item.setData( - {InstanceStates.HasFinished: True}, - Roles.PublishFlagsRole - ) - - for group_item in self.instance_model.group_items.values(): - group_item.setData( - {GroupStates.HasFinished: True}, - Roles.PublishFlagsRole - ) - - self.update_compatibility() - self._update_state() - - def on_was_processed(self, result): - existing_ids = set(self.instance_model.instance_items.keys()) - existing_ids.remove(self.controller.context.id) - for instance in self.controller.context: - if instance.id not in existing_ids: - self.instance_model.append(instance) - else: - existing_ids.remove(instance.id) - - for instance_id in existing_ids: - self.instance_model.remove(instance_id) - - result["records"] = self.terminal_model.prepare_records( - result, - self._suspend_logs - ) - - plugin_item = self.plugin_model.update_with_result(result) - instance_item = self.instance_model.update_with_result(result) - - self.terminal_model.update_with_result(result) - - self.update_compatibility() - - if self.perspective_widget.isVisible(): - self.perspective_widget.update_context( - plugin_item, instance_item - ) - - if self._hidden_for_plugin_process: - self._hidden_for_plugin_process = False - self._ensure_visible(True) - - # ------------------------------------------------------------------------- - # - # Functions - # - # ------------------------------------------------------------------------- - - def reset(self): - """Prepare GUI for reset""" - self.info(self.tr("About to reset..")) - - self.presets_button.setEnabled(False) - self.footer_widget.setProperty("success", -1) - self.footer_widget.style().polish(self.footer_widget) - - self.instance_model.store_checkstates() - self.plugin_model.store_checkstates() - - # Reset current ids to secure no previous instances get mixed in. - self.instance_model.reset() - self.plugin_model.reset() - self.intent_model.reset() - self.terminal_model.reset() - - self.footer_button_stop.setEnabled(False) - self.footer_button_reset.setEnabled(False) - self.footer_button_validate.setEnabled(False) - self.footer_button_play.setEnabled(False) - - self.intent_box.setVisible(self.intent_model.has_items) - if self.intent_model.has_items: - self.intent_box.setCurrentIndex(self.intent_model.default_index) - - self.comment_box.placeholder.setVisible(False) - # Launch controller reset - self.controller.reset() - if not self.comment_box.text(): - self.comment_box.placeholder.setVisible(True) - - def validate(self): - self.info(self.tr("Preparing validate..")) - self.footer_button_stop.setEnabled(True) - self.footer_button_reset.setEnabled(False) - self.footer_button_validate.setEnabled(False) - self.footer_button_play.setEnabled(False) - - self.button_suspend_logs.setEnabled(False) - - self.controller.validate() - - self._update_state() - - def publish(self): - self.info(self.tr("Preparing publish..")) - self.footer_button_stop.setEnabled(True) - self.footer_button_reset.setEnabled(False) - self.footer_button_validate.setEnabled(False) - self.footer_button_play.setEnabled(False) - - self.button_suspend_logs.setEnabled(False) - - self.controller.publish() - - self._update_state() - - def act(self, plugin_item, action): - self.info("%s %s.." % (self.tr("Preparing"), action)) - - self.footer_button_stop.setEnabled(True) - self.footer_button_reset.setEnabled(False) - self.footer_button_validate.setEnabled(False) - self.footer_button_play.setEnabled(False) - - # Cause view to update, but it won't visually - # happen until Qt is given time to idle.. - plugin_item.setData( - PluginActionStates.InProgress, Roles.PluginActionProgressRole - ) - - # Give Qt time to draw - self.controller.act(plugin_item.plugin, action) - - self.info(self.tr("Action prepared.")) - - def on_was_acted(self, result): - self.footer_button_reset.setEnabled(True) - self.footer_button_stop.setEnabled(False) - - # Update action with result - plugin_item = self.plugin_model.plugin_items[result["plugin"].id] - action_state = plugin_item.data(Roles.PluginActionProgressRole) - action_state |= PluginActionStates.HasFinished - result["records"] = self.terminal_model.prepare_records( - result, - self._suspend_logs - ) - - if result.get("error"): - action_state |= PluginActionStates.HasFailed - - plugin_item.setData(action_state, Roles.PluginActionProgressRole) - - self.terminal_model.update_with_result(result) - plugin_item = self.plugin_model.update_with_result(result) - instance_item = self.instance_model.update_with_result(result) - - if self.perspective_widget.isVisible(): - self.perspective_widget.update_context( - plugin_item, instance_item - ) - - def closeEvent(self, event): - """Perform post-flight checks before closing - - Make sure processing of any kind is wrapped up before closing - - """ - - self.info(self.tr("Closing..")) - - if self.controller.is_running: - self.info(self.tr("..as soon as processing is finished..")) - self.controller.stop() - - self.info(self.tr("Cleaning up controller..")) - self.controller.cleanup() - - self.overview_instance_view.setModel(None) - self.overview_plugin_view.setModel(None) - self.terminal_view.setModel(None) - - event.accept() - - def reject(self): - """Handle ESC key""" - - if self.controller.is_running: - self.info(self.tr("Stopping..")) - self.controller.stop() - - # ------------------------------------------------------------------------- - # - # Feedback - # - # ------------------------------------------------------------------------- - - def _update_state(self): - self.footer_info.setText(self.controller.current_state) - - def info(self, message): - """Print user-facing information - - Arguments: - message (str): Text message for the user - - """ - # Include message in terminal - self.terminal_model.append([{ - "label": message, - "type": "info" - }]) - - if settings.PrintInfo: - # Print message to console - util.u_print(message) - - def warning(self, message): - """Block processing and print warning until user hits "Continue" - - Arguments: - message (str): Message to display - - """ - - # TODO(marcus): Implement this. - self.info(message) - - def heads_up(self, title, message, command=None): - """Provide a front-and-center message with optional command - - Arguments: - title (str): Bold and short message - message (str): Extended message - command (optional, callable): Function is provided as a button - - """ - - # TODO(marcus): Implement this. - self.info(message) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index e533e08fe4..de5c199428 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.1.3+dev" +__version__ = "1.1.5+dev" diff --git a/package.py b/package.py index 02e2f25384..250b77fe52 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.1.3+dev" +version = "1.1.5+dev" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index 591c13bb2a..0700be5f57 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.1.3+dev" +version = "1.1.5+dev" description = "" authors = ["Ynput Team "] readme = "README.md"