From 22132f6e4db34e3989621c8a30281d6c6dbdac66 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 16 Sep 2020 16:11:13 +0200 Subject: [PATCH 01/92] feat(global): adding burnin suffix only if more filtered burnin profil --- pype/plugins/global/publish/extract_burnin.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/pype/plugins/global/publish/extract_burnin.py b/pype/plugins/global/publish/extract_burnin.py index 4443cfe223..6e8da1b054 100644 --- a/pype/plugins/global/publish/extract_burnin.py +++ b/pype/plugins/global/publish/extract_burnin.py @@ -195,11 +195,14 @@ class ExtractBurnin(pype.api.Extractor): if "delete" in new_repre["tags"]: new_repre["tags"].remove("delete") - # Update name and outputName to be able have multiple outputs - # Join previous "outputName" with filename suffix - new_name = "_".join([new_repre["outputName"], filename_suffix]) - new_repre["name"] = new_name - new_repre["outputName"] = new_name + if len(repre_burnin_defs.keys()) > 1: + # Update name and outputName to be + # able have multiple outputs in case of more burnin presets + # Join previous "outputName" with filename suffix + new_name = "_".join( + [new_repre["outputName"], filename_suffix]) + new_repre["name"] = new_name + new_repre["outputName"] = new_name # Prepare paths and files for process. self.input_output_paths(new_repre, temp_data, filename_suffix) From 4e5226cdc581e05b41730bf4780e982b48b06f49 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 22 Sep 2020 11:42:23 +0200 Subject: [PATCH 02/92] fixed clipboard copy in global loaders when Qt is runnign QCoreApplication --- pype/plugins/global/load/copy_file.py | 5 ++--- pype/plugins/global/load/copy_file_path.py | 7 +++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/pype/plugins/global/load/copy_file.py b/pype/plugins/global/load/copy_file.py index bbb8e1d6f7..1acacf6b27 100644 --- a/pype/plugins/global/load/copy_file.py +++ b/pype/plugins/global/load/copy_file.py @@ -20,8 +20,8 @@ class CopyFile(api.Loader): def copy_file_to_clipboard(path): from avalon.vendor.Qt import QtCore, QtWidgets - app = QtWidgets.QApplication.instance() - assert app, "Must have running QApplication instance" + clipboard = QtWidgets.QApplication.clipboard() + assert clipboard, "Must have running QApplication instance" # Build mime data for clipboard data = QtCore.QMimeData() @@ -29,5 +29,4 @@ class CopyFile(api.Loader): data.setUrls([url]) # Set to Clipboard - clipboard = app.clipboard() clipboard.setMimeData(data) diff --git a/pype/plugins/global/load/copy_file_path.py b/pype/plugins/global/load/copy_file_path.py index cfda9dc271..f64f3e76d8 100644 --- a/pype/plugins/global/load/copy_file_path.py +++ b/pype/plugins/global/load/copy_file_path.py @@ -19,11 +19,10 @@ class CopyFilePath(api.Loader): @staticmethod def copy_path_to_clipboard(path): - from avalon.vendor.Qt import QtCore, QtWidgets + from avalon.vendor.Qt import QtWidgets - app = QtWidgets.QApplication.instance() - assert app, "Must have running QApplication instance" + clipboard = QtWidgets.QApplication.clipboard() + assert clipboard, "Must have running QApplication instance" # Set to Clipboard - clipboard = app.clipboard() clipboard.setText(os.path.normpath(path)) From 066349a3cb4424c4a0317002cdb5918c16614491 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 22 Sep 2020 12:02:39 +0200 Subject: [PATCH 03/92] implemented base pype module class in modules --- pype/modules/__init__.py | 5 +++++ pype/modules/base.py | 26 ++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 pype/modules/base.py diff --git a/pype/modules/__init__.py b/pype/modules/__init__.py index e69de29bb2..32b7426ba5 100644 --- a/pype/modules/__init__.py +++ b/pype/modules/__init__.py @@ -0,0 +1,5 @@ +from .base import PypeModule + +__all__ = ( + "PypeModule", +) diff --git a/pype/modules/base.py b/pype/modules/base.py new file mode 100644 index 0000000000..2d97bc5e14 --- /dev/null +++ b/pype/modules/base.py @@ -0,0 +1,26 @@ +from uuid import uuid4 +from pype.api import Logger + + +class PypeModule: + """Base class of pype module.""" + enabled = False + name = None + _id = None + + def __init__(self, settings): + if self.name is None: + self.name = self.__class__.__name__ + + self.log = Logger().get_logger(self.name) + + self.settings = settings.get(self.name) + self.enabled = settings.get("enabled", False) + self._id = uuid4() + + @property + def id(self): + return self._id + + def startup_environments(self): + return {} From b50438a66cebbbb1dc7767a6973ff781c5b429b4 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 22 Sep 2020 12:03:35 +0200 Subject: [PATCH 04/92] implemented basic pype modules manager which does not much do at this time --- pype/modules_manager.py | 104 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 pype/modules_manager.py diff --git a/pype/modules_manager.py b/pype/modules_manager.py new file mode 100644 index 0000000000..073b153f08 --- /dev/null +++ b/pype/modules_manager.py @@ -0,0 +1,104 @@ +import os +import sys +import inspect + +import pype.modules +from pype.modules import PypeModule +from pype.settings import system_settings +from pype.api import Logger + + +class PypeModuleManager: + skip_module_names = ("__pycache__", ) + + def __init__(self): + self.log = Logger().get_logger( + "{}.{}".format(__name__, self.__class__.__name__) + ) + + self.pype_modules = self.find_pype_modules() + + def modules_environments(self): + environments = {} + for pype_module in self.pype_modules.values(): + environments.update(pype_module.startup_environments()) + return environments + + def find_pype_modules(self): + settings = system_settings() + modules = [] + dirpath = os.path.dirname(pype.modules.__file__) + for module_name in os.listdir(dirpath): + # Check if path lead to a folder + full_path = os.path.join(dirpath, module_name) + if not os.path.isdir(full_path): + continue + + # Skip known invalid names + if module_name in self.skip_module_names: + continue + + import_name = "pype.modules.{}".format(module_name) + try: + modules.append( + __import__(import_name, fromlist=[""]) + ) + print(import_name, sys.modules.get("PyQt5")) + + except Exception: + self.log.warning( + "Couldn't import {}".format(import_name), exc_info=True + ) + + pype_module_classes = [] + for module in modules: + try: + pype_module_classes.extend( + self._classes_from_module(PypeModule, module) + ) + except Exception: + self.log.warning( + "Couldn't import {}".format(import_name), exc_info=True + ) + + pype_modules = {} + for pype_module_class in pype_module_classes: + try: + pype_module = pype_module_class(settings) + if pype_module.enabled: + pype_modules[pype_module.id] = pype_module + except Exception: + self.log.warning( + "Couldn't create instance of {}".format( + pype_module_class.__class__.__name__ + ), + exc_info=True + ) + return pype_modules + + def _classes_from_module(self, superclass, module): + classes = list() + + def recursive_bases(klass): + output = [] + output.extend(klass.__bases__) + for base in klass.__bases__: + output.extend(recursive_bases(base)) + return output + + for name in dir(module): + # It could be anything at this point + obj = getattr(module, name) + + if not inspect.isclass(obj) or not len(obj.__bases__) > 0: + continue + + # Use string comparison rather than `issubclass` + # in order to support reloading of this module. + bases = recursive_bases(obj) + if not any(base.__name__ == superclass.__name__ for base in bases): + continue + + classes.append(obj) + + return classes From d5c0fa464ae7b0fdc61367e8b14719aa972a589a Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 22 Sep 2020 14:05:36 +0200 Subject: [PATCH 05/92] removed debug prints --- pype/modules_manager.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pype/modules_manager.py b/pype/modules_manager.py index 073b153f08..4b337c075b 100644 --- a/pype/modules_manager.py +++ b/pype/modules_manager.py @@ -43,7 +43,6 @@ class PypeModuleManager: modules.append( __import__(import_name, fromlist=[""]) ) - print(import_name, sys.modules.get("PyQt5")) except Exception: self.log.warning( From 4db75b45e5c26dac46ddbf75a11676adee5d9c44 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 22 Sep 2020 14:06:43 +0200 Subject: [PATCH 06/92] PypeModule inherit from abstract class --- pype/modules/base.py | 3 ++- pype/modules_manager.py | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/modules/base.py b/pype/modules/base.py index 2d97bc5e14..ede2f41bd5 100644 --- a/pype/modules/base.py +++ b/pype/modules/base.py @@ -1,8 +1,9 @@ from uuid import uuid4 +from abc import ABC from pype.api import Logger -class PypeModule: +class PypeModule(ABC): """Base class of pype module.""" enabled = False name = None diff --git a/pype/modules_manager.py b/pype/modules_manager.py index 4b337c075b..6538187ea9 100644 --- a/pype/modules_manager.py +++ b/pype/modules_manager.py @@ -1,5 +1,4 @@ import os -import sys import inspect import pype.modules From 3cf6962d3c344a45a809b2ee7450fc56bf763d43 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 22 Sep 2020 14:44:00 +0200 Subject: [PATCH 07/92] location configrations are refreshed if is triggered session.rollback() in ftrack plugins --- pype/plugins/ftrack/publish/integrate_ftrack_api.py | 8 ++++++++ .../plugins/ftrack/publish/integrate_ftrack_note.py | 1 + .../ftrack/publish/integrate_hierarchy_ftrack.py | 13 +++++++++++-- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/pype/plugins/ftrack/publish/integrate_ftrack_api.py b/pype/plugins/ftrack/publish/integrate_ftrack_api.py index 0c4c6d49b5..2c8e06a099 100644 --- a/pype/plugins/ftrack/publish/integrate_ftrack_api.py +++ b/pype/plugins/ftrack/publish/integrate_ftrack_api.py @@ -97,6 +97,7 @@ class IntegrateFtrackApi(pyblish.api.InstancePlugin): except Exception: tp, value, tb = sys.exc_info() session.rollback() + session._configure_locations() six.reraise(tp, value, tb) def process(self, instance): @@ -178,6 +179,7 @@ class IntegrateFtrackApi(pyblish.api.InstancePlugin): except Exception: tp, value, tb = sys.exc_info() session.rollback() + session._configure_locations() six.reraise(tp, value, tb) # Adding metadata @@ -228,6 +230,7 @@ class IntegrateFtrackApi(pyblish.api.InstancePlugin): except Exception: tp, value, tb = sys.exc_info() session.rollback() + session._configure_locations() six.reraise(tp, value, tb) # Adding metadata @@ -242,6 +245,7 @@ class IntegrateFtrackApi(pyblish.api.InstancePlugin): session.commit() except Exception: session.rollback() + session._configure_locations() self.log.warning(( "Comment was not possible to set for AssetVersion" "\"{0}\". Can't set it's value to: \"{1}\"" @@ -258,6 +262,7 @@ class IntegrateFtrackApi(pyblish.api.InstancePlugin): continue except Exception: session.rollback() + session._configure_locations() self.log.warning(( "Custom Attrubute \"{0}\"" @@ -272,6 +277,7 @@ class IntegrateFtrackApi(pyblish.api.InstancePlugin): except Exception: tp, value, tb = sys.exc_info() session.rollback() + session._configure_locations() six.reraise(tp, value, tb) # Component @@ -316,6 +322,7 @@ class IntegrateFtrackApi(pyblish.api.InstancePlugin): except Exception: tp, value, tb = sys.exc_info() session.rollback() + session._configure_locations() six.reraise(tp, value, tb) # Reset members in memory @@ -432,6 +439,7 @@ class IntegrateFtrackApi(pyblish.api.InstancePlugin): except Exception: tp, value, tb = sys.exc_info() session.rollback() + session._configure_locations() six.reraise(tp, value, tb) if assetversion_entity not in used_asset_versions: diff --git a/pype/plugins/ftrack/publish/integrate_ftrack_note.py b/pype/plugins/ftrack/publish/integrate_ftrack_note.py index 9566207145..acd295854d 100644 --- a/pype/plugins/ftrack/publish/integrate_ftrack_note.py +++ b/pype/plugins/ftrack/publish/integrate_ftrack_note.py @@ -145,4 +145,5 @@ class IntegrateFtrackNote(pyblish.api.InstancePlugin): except Exception: tp, value, tb = sys.exc_info() session.rollback() + session._configure_locations() six.reraise(tp, value, tb) diff --git a/pype/plugins/ftrack/publish/integrate_hierarchy_ftrack.py b/pype/plugins/ftrack/publish/integrate_hierarchy_ftrack.py index c4d8f2f705..95408e90ee 100644 --- a/pype/plugins/ftrack/publish/integrate_hierarchy_ftrack.py +++ b/pype/plugins/ftrack/publish/integrate_hierarchy_ftrack.py @@ -128,6 +128,7 @@ class IntegrateHierarchyToFtrack(pyblish.api.ContextPlugin): except Exception: tp, value, tb = sys.exc_info() self.session.rollback() + self.session._configure_locations() six.reraise(tp, value, tb) # TASKS @@ -156,6 +157,7 @@ class IntegrateHierarchyToFtrack(pyblish.api.ContextPlugin): except Exception: tp, value, tb = sys.exc_info() self.session.rollback() + self.session._configure_locations() six.reraise(tp, value, tb) # Incoming links. @@ -165,6 +167,7 @@ class IntegrateHierarchyToFtrack(pyblish.api.ContextPlugin): except Exception: tp, value, tb = sys.exc_info() self.session.rollback() + self.session._configure_locations() six.reraise(tp, value, tb) # Create notes. @@ -185,6 +188,7 @@ class IntegrateHierarchyToFtrack(pyblish.api.ContextPlugin): except Exception: tp, value, tb = sys.exc_info() self.session.rollback() + self.session._configure_locations() six.reraise(tp, value, tb) # Import children. @@ -201,6 +205,7 @@ class IntegrateHierarchyToFtrack(pyblish.api.ContextPlugin): except Exception: tp, value, tb = sys.exc_info() self.session.rollback() + self.session._configure_locations() six.reraise(tp, value, tb) # Create new links. @@ -242,6 +247,7 @@ class IntegrateHierarchyToFtrack(pyblish.api.ContextPlugin): except Exception: tp, value, tb = sys.exc_info() self.session.rollback() + self.session._configure_locations() six.reraise(tp, value, tb) return task @@ -256,6 +262,7 @@ class IntegrateHierarchyToFtrack(pyblish.api.ContextPlugin): except Exception: tp, value, tb = sys.exc_info() self.session.rollback() + self.session._configure_locations() six.reraise(tp, value, tb) return entity @@ -270,7 +277,8 @@ class IntegrateHierarchyToFtrack(pyblish.api.ContextPlugin): except Exception: tp, value, tb = sys.exc_info() self.session.rollback() - raise + self.session._configure_locations() + six.reraise(tp, value, tb) def auto_sync_on(self, project): @@ -283,4 +291,5 @@ class IntegrateHierarchyToFtrack(pyblish.api.ContextPlugin): except Exception: tp, value, tb = sys.exc_info() self.session.rollback() - raise + self.session._configure_locations() + six.reraise(tp, value, tb) From b4080ebbe23855a943f3b61ba9ae24deabe307bc Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Tue, 22 Sep 2020 16:12:25 +0200 Subject: [PATCH 08/92] docstrings and abstract method --- pype/modules/__init__.py | 1 + pype/modules/base.py | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/pype/modules/__init__.py b/pype/modules/__init__.py index 32b7426ba5..aacd541e18 100644 --- a/pype/modules/__init__.py +++ b/pype/modules/__init__.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- from .base import PypeModule __all__ = ( diff --git a/pype/modules/base.py b/pype/modules/base.py index ede2f41bd5..ee90aa4cbb 100644 --- a/pype/modules/base.py +++ b/pype/modules/base.py @@ -1,10 +1,19 @@ +# -*- coding: utf-8 -*- +"""Base class for Pype Modules.""" from uuid import uuid4 -from abc import ABC +from abc import ABC, abstractmethod from pype.api import Logger class PypeModule(ABC): - """Base class of pype module.""" + """Base class of pype module. + + Attributes: + id (UUID): Module id. + enabled (bool): Is module enabled. + name (str): Module name. + """ + enabled = False name = None _id = None @@ -23,5 +32,7 @@ class PypeModule(ABC): def id(self): return self._id + @abstractmethod def startup_environments(self): + """Get startup environments for module.""" return {} From c2d8868ac043bf1a041510d15d6696bb9848074f Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 22 Sep 2020 15:25:46 +0100 Subject: [PATCH 09/92] FIx missing update of frame range. --- pype/plugins/maya/load/load_image_plane.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/pype/plugins/maya/load/load_image_plane.py b/pype/plugins/maya/load/load_image_plane.py index 7d8ae27f89..e17382f7ed 100644 --- a/pype/plugins/maya/load/load_image_plane.py +++ b/pype/plugins/maya/load/load_image_plane.py @@ -1,7 +1,7 @@ import pymel.core as pc import maya.cmds as cmds -from avalon import api +from avalon import api, io from avalon.maya.pipeline import containerise from avalon.maya import lib from Qt import QtWidgets @@ -147,6 +147,17 @@ class ImagePlaneLoader(api.Loader): type="string" ) + # Set frame range. + version = io.find_one({"_id": representation["parent"]}) + subset = io.find_one({"_id": version["parent"]}) + asset = io.find_one({"_id": subset["parent"]}) + start_frame = asset["data"]["frameStart"] + end_frame = asset["data"]["frameEnd"] + image_plane_shape.frameOffset.set(1 - start_frame) + image_plane_shape.frameIn.set(start_frame) + image_plane_shape.frameOut.set(end_frame) + image_plane_shape.frameCache.set(end_frame) + def switch(self, container, representation): self.update(container, representation) From 40c8b3fd7975cd8972e7aa6f66c8473406a12072 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 22 Sep 2020 15:26:16 +0100 Subject: [PATCH 10/92] Fix missing frame range update. --- pype/plugins/maya/load/load_audio.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pype/plugins/maya/load/load_audio.py b/pype/plugins/maya/load/load_audio.py index ca38082ed0..81bcca48e1 100644 --- a/pype/plugins/maya/load/load_audio.py +++ b/pype/plugins/maya/load/load_audio.py @@ -1,7 +1,7 @@ from maya import cmds, mel import pymel.core as pc -from avalon import api +from avalon import api, io from avalon.maya.pipeline import containerise from avalon.maya import lib @@ -58,6 +58,13 @@ class AudioLoader(api.Loader): type="string" ) + # Set frame range. + version = io.find_one({"_id": representation["parent"]}) + subset = io.find_one({"_id": version["parent"]}) + asset = io.find_one({"_id": subset["parent"]}) + audio_node.sourceStart.set(1 - asset["data"]["frameStart"]) + audio_node.sourceEnd.set(asset["data"]["frameEnd"]) + def switch(self, container, representation): self.update(container, representation) From 2e8f5ee55cf460e118d16647af34918f43123751 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 24 Sep 2020 12:47:08 +0200 Subject: [PATCH 11/92] collect anatomy instance data plugin converted to context plugin --- pype/plugins/global/publish/collect_anatomy_instance_data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/plugins/global/publish/collect_anatomy_instance_data.py b/pype/plugins/global/publish/collect_anatomy_instance_data.py index 44a4d43946..455b44c4f1 100644 --- a/pype/plugins/global/publish/collect_anatomy_instance_data.py +++ b/pype/plugins/global/publish/collect_anatomy_instance_data.py @@ -28,7 +28,7 @@ from avalon import io import pyblish.api -class CollectAnatomyInstanceData(pyblish.api.InstancePlugin): +class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): """Collect Instance specific Anatomy data.""" order = pyblish.api.CollectorOrder + 0.49 From 2831f3f4b4cde563fcb0da7690b528b0f0c221f6 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 24 Sep 2020 12:48:18 +0200 Subject: [PATCH 12/92] collecting was separated to 3 parts with only 3 calls to mongo database --- .../publish/collect_anatomy_instance_data.py | 315 +++++++++++++----- 1 file changed, 224 insertions(+), 91 deletions(-) diff --git a/pype/plugins/global/publish/collect_anatomy_instance_data.py b/pype/plugins/global/publish/collect_anatomy_instance_data.py index 455b44c4f1..446f671b86 100644 --- a/pype/plugins/global/publish/collect_anatomy_instance_data.py +++ b/pype/plugins/global/publish/collect_anatomy_instance_data.py @@ -23,123 +23,256 @@ Provides: import copy import json +import collections from avalon import io import pyblish.api class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): - """Collect Instance specific Anatomy data.""" + """Collect Instance specific Anatomy data. + + Plugin is running for all instances on context even not active instances. + """ order = pyblish.api.CollectorOrder + 0.49 label = "Collect Anatomy Instance data" - def process(self, instance): - # get all the stuff from the database - anatomy_data = copy.deepcopy(instance.context.data["anatomyData"]) - project_entity = instance.context.data["projectEntity"] - context_asset_entity = instance.context.data["assetEntity"] - instance_asset_entity = instance.data.get("assetEntity") + def process(self, context): + self.log.info("Collecting anatomy data for all instances.") - asset_name = instance.data["asset"] + self.fill_missing_asset_docs(context) + self.fill_latest_versions(context) + self.fill_anatomy_data(context) - # There is possibility that assetEntity on instance is already set - # which can happen in standalone publisher - if ( - instance_asset_entity - and instance_asset_entity["name"] == asset_name - ): - asset_entity = instance_asset_entity + self.log.info("Anatomy Data collection finished.") - # Check if asset name is the same as what is in context - # - they may be different, e.g. in NukeStudio - elif context_asset_entity["name"] == asset_name: - asset_entity = context_asset_entity + def fill_missing_asset_docs(self, context): + self.log.debug("Qeurying asset documents for instances.") - else: - asset_entity = io.find_one({ - "type": "asset", - "name": asset_name, - "parent": project_entity["_id"] - }) + context_asset_doc = context.data["assetEntity"] - subset_name = instance.data["subset"] - version_number = instance.data.get("version") - latest_version = None + instances_with_missing_asset_doc = collections.defaultdict(list) + for instance in context: + instance_asset_doc = instance.data.get("assetEntity") + _asset_name = instance.data["asset"] - if asset_entity: - subset_entity = io.find_one({ - "type": "subset", - "name": subset_name, - "parent": asset_entity["_id"] - }) + # There is possibility that assetEntity on instance is already set + # which can happen in standalone publisher + if ( + instance_asset_doc + and instance_asset_doc["name"] == _asset_name + ): + continue + + # Check if asset name is the same as what is in context + # - they may be different, e.g. in NukeStudio + if context_asset_doc["name"] == _asset_name: + instance.data["assetEntity"] = context_asset_doc - if subset_entity is None: - self.log.debug("Subset entity does not exist yet.") else: - version_entity = io.find_one( - { - "type": "version", - "parent": subset_entity["_id"] - }, - sort=[("name", -1)] - ) - if version_entity: - latest_version = version_entity["name"] + instances_with_missing_asset_doc[_asset_name].append(instance) - # If version is not specified for instance or context - if version_number is None: - # TODO we should be able to change default version by studio - # preferences (like start with version number `0`) - version_number = 1 - # use latest version (+1) if already any exist - if latest_version is not None: - version_number += int(latest_version) + if not instances_with_missing_asset_doc: + self.log.debug("All instances already had right asset document.") + return - anatomy_updates = { - "asset": asset_name, - "family": instance.data["family"], - "subset": subset_name, - "version": version_number + asset_names = list(instances_with_missing_asset_doc.keys()) + self.log.debug("Querying asset documents with names: {}".format( + ", ".join(["\"{}\"".format(name) for name in asset_names]) + )) + asset_docs = io.find({ + "type": "asset", + "name": {"$in": asset_names} + }) + asset_docs_by_name = { + asset_doc["name"]: asset_doc + for asset_doc in asset_docs } - if ( - asset_entity - and asset_entity["_id"] != context_asset_entity["_id"] - ): - parents = asset_entity["data"].get("parents") or list() - anatomy_updates["hierarchy"] = "/".join(parents) - task_name = instance.data.get("task") - if task_name: - anatomy_updates["task"] = task_name + not_found_asset_names = [] + for asset_name, instances in instances_with_missing_asset_doc.items(): + asset_doc = asset_docs_by_name.get(asset_name) + if not asset_doc: + not_found_asset_names.append(asset_name) + continue - # Version should not be collected since may be instance - anatomy_data.update(anatomy_updates) + for _instance in instances: + _instance.data["assetEntity"] = asset_doc - resolution_width = instance.data.get("resolutionWidth") - if resolution_width: - anatomy_data["resolution_width"] = resolution_width + if not_found_asset_names: + joined_asset_names = ", ".join( + ["\"{}\"".format(name) for name in not_found_asset_names] + ) + self.log.warning(( + "Not found asset documents with names \"{}\"." + ).format(joined_asset_names)) - resolution_height = instance.data.get("resolutionHeight") - if resolution_height: - anatomy_data["resolution_height"] = resolution_height + def fill_latest_versions(self, context): + """Try to find latest version for each instance's subset. - pixel_aspect = instance.data.get("pixelAspect") - if pixel_aspect: - anatomy_data["pixel_aspect"] = float("{:0.2f}".format( - float(pixel_aspect))) + Key "latestVersion" is always set to latest version or `None`. - fps = instance.data.get("fps") - if fps: - anatomy_data["fps"] = float("{:0.2f}".format( - float(fps))) + Args: + context (pyblish.Context) - instance.data["projectEntity"] = project_entity - instance.data["assetEntity"] = asset_entity - instance.data["anatomyData"] = anatomy_data - instance.data["latestVersion"] = latest_version - # TODO should be version number set here? - instance.data["version"] = version_number + Returns: + None - self.log.info("Instance anatomy Data collected") - self.log.debug(json.dumps(anatomy_data, indent=4)) + """ + self.log.debug("Qeurying latest versions for instances.") + + hierarchy = {} + subset_names = set() + asset_ids = set() + for instance in context: + # Make sure `"latestVersion"` key is set + latest_version = instance.data.get("latestVersion") + instance.data["latestVersion"] = latest_version + + # Skip instances withou "assetEntity" + asset_doc = instance.data.get("assetEntity") + if not asset_doc: + continue + + # Store asset ids and subset names for queries + asset_id = asset_doc["_id"] + subset_name = instance.data["subset"] + asset_ids.add(asset_id) + subset_names.add(subset_name) + + # Prepare instance hiearchy for faster filling latest versions + if asset_id not in hierarchy: + hierarchy[asset_id] = {} + if subset_name not in hierarchy[asset_id]: + hierarchy[asset_id][subset_name] = [] + hierarchy[asset_id][subset_name].append(instance) + + subset_docs = list(io.find({ + "type": "subset", + "parent": {"$in": list(asset_ids)}, + "name": {"$in": list(subset_names)} + })) + + subset_ids = [ + subset_doc["_id"] + for subset_doc in subset_docs + ] + + last_version_by_subset_id = self._query_last_versions(subset_ids) + for subset_doc in subset_docs: + subset_id = subset_doc["_id"] + last_version = last_version_by_subset_id.get(subset_id) + if last_version is None: + continue + + asset_id = subset_doc["parent"] + subset_name = subset_doc["name"] + _instances = hierarchy[asset_id][subset_name] + for _instance in _instances: + _instance.data["latestVersion"] = last_version + + def _query_last_versions(self, subset_ids): + """Retrieve all latest versions for entered subset_ids. + + Args: + subset_ids (list): List of subset ids with type `ObjectId`. + + Returns: + dict: Key is subset id and value is last version name. + """ + _pipeline = [ + # Find all versions of those subsets + {"$match": { + "type": "version", + "parent": {"$in": subset_ids} + }}, + # Sorting versions all together + {"$sort": {"name": 1}}, + # Group them by "parent", but only take the last + {"$group": { + "_id": "$parent", + "_version_id": {"$last": "$_id"}, + "name": {"$last": "$name"} + }} + ] + + last_version_by_subset_id = {} + for doc in io.aggregate(_pipeline): + subset_id = doc["_id"] + last_version_by_subset_id[subset_id] = doc["name"] + + return last_version_by_subset_id + + def fill_anatomy_data(self, context): + self.log.debug("Storing anatomy data to instance data.") + + project_doc = context.data["projectEntity"] + context_asset_doc = context.data["assetEntity"] + + for instance in context: + version_number = instance.data.get("version") + # If version is not specified for instance or context + if version_number is None: + # TODO we should be able to change default version by studio + # preferences (like start with version number `0`) + version_number = 1 + # use latest version (+1) if already any exist + latest_version = instance.data["latestVersion"] + if latest_version is not None: + version_number += int(latest_version) + + anatomy_updates = { + "asset": instance.data["asset"], + "family": instance.data["family"], + "subset": instance.data["subset"], + "version": version_number + } + + # Hiearchy + asset_doc = instance.data.get("assetEntity") + if asset_doc and asset_doc["_id"] != context_asset_doc["_id"]: + parents = asset_doc["data"].get("parents") or list() + anatomy_updates["hierarchy"] = "/".join(parents) + + # Task + task_name = instance.data.get("task") + if task_name: + anatomy_updates["task"] = task_name + + # Additional data + resolution_width = instance.data.get("resolutionWidth") + if resolution_width: + anatomy_updates["resolution_width"] = resolution_width + + resolution_height = instance.data.get("resolutionHeight") + if resolution_height: + anatomy_updates["resolution_height"] = resolution_height + + pixel_aspect = instance.data.get("pixelAspect") + if pixel_aspect: + anatomy_updates["pixel_aspect"] = float( + "{:0.2f}".format(float(pixel_aspect)) + ) + + fps = instance.data.get("fps") + if fps: + anatomy_updates["fps"] = float("{:0.2f}".format(float(fps))) + + anatomy_data = copy.deepcopy(context.data["anatomyData"]) + anatomy_data.update(anatomy_updates) + + # Store anatomy data + instance.data["projectEntity"] = project_doc + instance.data["anatomyData"] = anatomy_data + instance.data["version"] = version_number + + # Log collected data + instance_name = instance.data["name"] + instance_label = instance.data.get("label") + if instance_label: + instance_name += "({})".format(instance_label) + self.log.debug("Anatomy data for instance {}: {}".format( + instance_name, + json.dumps(anatomy_data, indent=4) + )) From 3830e347d4722ec7850dff01341acad5606baead Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 24 Sep 2020 16:07:49 +0200 Subject: [PATCH 13/92] remove all widgets in content_layout on refresh --- pype/tools/settings/settings/widgets/base.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pype/tools/settings/settings/widgets/base.py b/pype/tools/settings/settings/widgets/base.py index 404d7b700b..243a8448e5 100644 --- a/pype/tools/settings/settings/widgets/base.py +++ b/pype/tools/settings/settings/widgets/base.py @@ -133,11 +133,11 @@ class SystemWidget(QtWidgets.QWidget): def reset(self): reset_default_settings() - if self.content_layout.count() != 0: - for widget in self.input_fields: - self.content_layout.removeWidget(widget) - widget.deleteLater() - self.input_fields.clear() + self.input_fields.clear() + while self.content_layout.count() != 0: + widget = self.content_layout.itemAt(0).widget() + self.content_layout.removeWidget(widget) + widget.deleteLater() self.schema = lib.gui_schema("system_schema", "0_system_gui_schema") self.keys = self.schema.get("keys", []) From 7104ac3fbce20b3bb2391e1db781d60b074d85e1 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 24 Sep 2020 17:30:08 +0200 Subject: [PATCH 14/92] rest api module is not using QThread for running but python's Thread --- pype/modules/rest_api/rest_api.py | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/pype/modules/rest_api/rest_api.py b/pype/modules/rest_api/rest_api.py index cc98b56a3b..3e0c646560 100644 --- a/pype/modules/rest_api/rest_api.py +++ b/pype/modules/rest_api/rest_api.py @@ -1,6 +1,6 @@ import os import socket -from Qt import QtCore +import threading from socketserver import ThreadingMixIn from http.server import HTTPServer @@ -155,14 +155,15 @@ class RestApiServer: def is_running(self): return self.rest_api_thread.is_running + def tray_exit(self): + self.stop() + def stop(self): - self.rest_api_thread.is_running = False - - def thread_stopped(self): - self._is_running = False + self.rest_api_thread.stop() + self.rest_api_thread.join() -class RestApiThread(QtCore.QThread): +class RestApiThread(threading.Thread): """ Listener for REST requests. It is possible to register callbacks for url paths. @@ -174,6 +175,12 @@ class RestApiThread(QtCore.QThread): self.is_running = False self.module = module self.port = port + self.httpd = None + + def stop(self): + self.is_running = False + if self.httpd: + self.httpd.server_close() def run(self): self.is_running = True @@ -185,12 +192,14 @@ class RestApiThread(QtCore.QThread): ) with ThreadingSimpleServer(("", self.port), Handler) as httpd: + self.httpd = httpd while self.is_running: httpd.handle_request() + except Exception: log.warning( "Rest Api Server service has failed", exc_info=True ) + self.httpd = None self.is_running = False - self.module.thread_stopped() From 71a5a036ddc1658df5444004a4a09c92321e8fab Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Fri, 25 Sep 2020 10:15:10 +0200 Subject: [PATCH 15/92] validate intent --- .../plugins/global/publish/validate_intent.py | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 pype/plugins/global/publish/validate_intent.py diff --git a/pype/plugins/global/publish/validate_intent.py b/pype/plugins/global/publish/validate_intent.py new file mode 100644 index 0000000000..80bcb0e164 --- /dev/null +++ b/pype/plugins/global/publish/validate_intent.py @@ -0,0 +1,31 @@ +import pyblish.api +import os + + +class ValidateIntent(pyblish.api.ContextPlugin): + """Validate intent of the publish. + + It is required to fill the intent of this publish. Chech the log + for more details + """ + + order = pyblish.api.ValidatorOrder + + label = "Validate Intent" + # TODO: this should be off by default and only activated viac config + tasks = ["animation"] + hosts = ["harmony"] + if os.environ.get("AVALON_TASK") not in tasks: + active = False + + def process(self, context): + msg = ( + "Please make sure that you select the intent of this publish." + ) + + intent = context.data.get("intent") + self.log.debug(intent) + assert intent, msg + + intent_value = intent.get("value") + assert intent is not "", msg From 76a241afe9001f7fc816f741d56c377abc95f8ce Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Fri, 25 Sep 2020 13:24:30 +0200 Subject: [PATCH 16/92] bump version --- pype/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/version.py b/pype/version.py index 96fc614cb2..0f90260218 100644 --- a/pype/version.py +++ b/pype/version.py @@ -1 +1 @@ -__version__ = "2.12.1" +__version__ = "2.12.2" From 2416d4d33f616dcf8f6c854e4cbad0e97961a753 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 25 Sep 2020 14:46:04 +0200 Subject: [PATCH 17/92] set port variable to None so the variable exists --- pype/modules/websocket_server/websocket_server.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pype/modules/websocket_server/websocket_server.py b/pype/modules/websocket_server/websocket_server.py index 1152c65e00..ec6785625a 100644 --- a/pype/modules/websocket_server/websocket_server.py +++ b/pype/modules/websocket_server/websocket_server.py @@ -31,6 +31,7 @@ class WebSocketServer(): self.client = None self.handlers = {} + port = None websocket_url = os.getenv("WEBSOCKET_URL") if websocket_url: parsed = urllib.parse.urlparse(websocket_url) From 64052aa44e8c159a2429d842e88d2fa3220fc124 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 25 Sep 2020 16:08:34 +0200 Subject: [PATCH 18/92] logging module without qt in globals --- pype/modules/logging/{ => tray}/gui/__init__.py | 0 pype/modules/logging/{ => tray}/gui/app.py | 0 pype/modules/logging/{ => tray}/gui/models.py | 0 pype/modules/logging/{ => tray}/gui/widgets.py | 0 pype/modules/logging/tray/logging_module.py | 8 +++++--- 5 files changed, 5 insertions(+), 3 deletions(-) rename pype/modules/logging/{ => tray}/gui/__init__.py (100%) rename pype/modules/logging/{ => tray}/gui/app.py (100%) rename pype/modules/logging/{ => tray}/gui/models.py (100%) rename pype/modules/logging/{ => tray}/gui/widgets.py (100%) diff --git a/pype/modules/logging/gui/__init__.py b/pype/modules/logging/tray/gui/__init__.py similarity index 100% rename from pype/modules/logging/gui/__init__.py rename to pype/modules/logging/tray/gui/__init__.py diff --git a/pype/modules/logging/gui/app.py b/pype/modules/logging/tray/gui/app.py similarity index 100% rename from pype/modules/logging/gui/app.py rename to pype/modules/logging/tray/gui/app.py diff --git a/pype/modules/logging/gui/models.py b/pype/modules/logging/tray/gui/models.py similarity index 100% rename from pype/modules/logging/gui/models.py rename to pype/modules/logging/tray/gui/models.py diff --git a/pype/modules/logging/gui/widgets.py b/pype/modules/logging/tray/gui/widgets.py similarity index 100% rename from pype/modules/logging/gui/widgets.py rename to pype/modules/logging/tray/gui/widgets.py diff --git a/pype/modules/logging/tray/logging_module.py b/pype/modules/logging/tray/logging_module.py index 9b26d5d9bf..a40ce90ea9 100644 --- a/pype/modules/logging/tray/logging_module.py +++ b/pype/modules/logging/tray/logging_module.py @@ -1,6 +1,4 @@ -from Qt import QtWidgets from pype.api import Logger -from ..gui.app import LogsWindow class LoggingModule: @@ -8,7 +6,11 @@ class LoggingModule: self.parent = parent self.log = Logger().get_logger(self.__class__.__name__, "logging") + self.tray_init(main_parent, parent) + + def tray_init(self, main_parent, parent): try: + from .gui.app import LogsWindow self.window = LogsWindow() self.tray_menu = self._tray_menu except Exception: @@ -18,9 +20,9 @@ class LoggingModule: # Definition of Tray menu def _tray_menu(self, parent_menu): + from Qt import QtWidgets # Menu for Tray App menu = QtWidgets.QMenu('Logging', parent_menu) - # menu.setProperty('submenu', 'on') show_action = QtWidgets.QAction("Show Logs", menu) show_action.triggered.connect(self.on_show_logs) From d67a8a5f833845f6fa077cea607eff51253bce94 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 25 Sep 2020 16:09:03 +0200 Subject: [PATCH 19/92] TimersManager is not singleton --- pype/modules/timers_manager/timers_manager.py | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/pype/modules/timers_manager/timers_manager.py b/pype/modules/timers_manager/timers_manager.py index 82ba1013f0..aeefddc75a 100644 --- a/pype/modules/timers_manager/timers_manager.py +++ b/pype/modules/timers_manager/timers_manager.py @@ -2,20 +2,7 @@ from .widget_user_idle import WidgetUserIdle, SignalHandler from pype.api import Logger, config -class Singleton(type): - """ Signleton implementation - """ - _instances = {} - - def __call__(cls, *args, **kwargs): - if cls not in cls._instances: - cls._instances[cls] = super( - Singleton, cls - ).__call__(*args, **kwargs) - return cls._instances[cls] - - -class TimersManager(metaclass=Singleton): +class TimersManager: """ Handles about Timers. Should be able to start/stop all timers at once. From 480acb9238eac4f183ae9f0a7022cb4250f27ed5 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 25 Sep 2020 16:09:30 +0200 Subject: [PATCH 20/92] timers manager has tray_init where all qt imports are done --- pype/modules/timers_manager/__init__.py | 1 - pype/modules/timers_manager/timers_manager.py | 10 +++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/pype/modules/timers_manager/__init__.py b/pype/modules/timers_manager/__init__.py index a8a478d7ae..9de205f088 100644 --- a/pype/modules/timers_manager/__init__.py +++ b/pype/modules/timers_manager/__init__.py @@ -1,5 +1,4 @@ from .timers_manager import TimersManager -from .widget_user_idle import WidgetUserIdle CLASS_DEFINIION = TimersManager diff --git a/pype/modules/timers_manager/timers_manager.py b/pype/modules/timers_manager/timers_manager.py index aeefddc75a..62767c24f1 100644 --- a/pype/modules/timers_manager/timers_manager.py +++ b/pype/modules/timers_manager/timers_manager.py @@ -1,5 +1,4 @@ -from .widget_user_idle import WidgetUserIdle, SignalHandler -from pype.api import Logger, config +from pype.api import Logger class TimersManager: @@ -28,7 +27,13 @@ class TimersManager: self.idle_man = None self.signal_handler = None + + self.trat_init(tray_widget, main_widget) + + def trat_init(self, tray_widget, main_widget): + from .widget_user_idle import WidgetUserIdle, SignalHandler self.widget_user_idle = WidgetUserIdle(self, tray_widget) + self.signal_handler = SignalHandler(self) def set_signal_times(self): try: @@ -106,7 +111,6 @@ class TimersManager: """ if 'IdleManager' in modules: - self.signal_handler = SignalHandler(self) if self.set_signal_times() is True: self.register_to_idle_manager(modules['IdleManager']) From 903713c7497a36b699dbbc7e77dec0469a5013cb Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 25 Sep 2020 16:11:48 +0200 Subject: [PATCH 21/92] user module has also `tray_init` method for all Qt imports --- pype/modules/user/user_module.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/pype/modules/user/user_module.py b/pype/modules/user/user_module.py index f2de9dc2fb..dc57fe4a63 100644 --- a/pype/modules/user/user_module.py +++ b/pype/modules/user/user_module.py @@ -3,8 +3,6 @@ import json import getpass import appdirs -from Qt import QtWidgets -from .widget_user import UserWidget from pype.api import Logger @@ -24,6 +22,12 @@ class UserModule: self.cred_path = os.path.normpath(os.path.join( self.cred_folder_path, self.cred_filename )) + self.widget_login = None + + self.tray_init(main_parent, parent) + + def tray_init(self, main_parent=None, parent=None): + from .widget_user import UserWidget self.widget_login = UserWidget(self) self.load_credentials() @@ -66,6 +70,7 @@ class UserModule: # Definition of Tray menu def tray_menu(self, parent_menu): + from Qt import QtWidgets """Add menu or action to Tray(or parent)'s menu""" action = QtWidgets.QAction("Username", parent_menu) action.triggered.connect(self.show_widget) @@ -121,7 +126,8 @@ class UserModule: self.cred = {"username": username} os.environ[self.env_name] = username - self.widget_login.set_user(username) + if self.widget_login: + self.widget_login.set_user(username) try: file = open(self.cred_path, "w") file.write(json.dumps(self.cred)) From 06b1501cacc27dd21a828851c66236ffeffd01cc Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 25 Sep 2020 16:11:58 +0200 Subject: [PATCH 22/92] small modification in logging module --- pype/modules/logging/tray/logging_module.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pype/modules/logging/tray/logging_module.py b/pype/modules/logging/tray/logging_module.py index a40ce90ea9..84b40f68e1 100644 --- a/pype/modules/logging/tray/logging_module.py +++ b/pype/modules/logging/tray/logging_module.py @@ -6,6 +6,8 @@ class LoggingModule: self.parent = parent self.log = Logger().get_logger(self.__class__.__name__, "logging") + self.window = None + self.tray_init(main_parent, parent) def tray_init(self, main_parent, parent): @@ -25,7 +27,7 @@ class LoggingModule: menu = QtWidgets.QMenu('Logging', parent_menu) show_action = QtWidgets.QAction("Show Logs", menu) - show_action.triggered.connect(self.on_show_logs) + show_action.triggered.connect(self._show_logs_gui) menu.addAction(show_action) parent_menu.addMenu(menu) @@ -36,5 +38,6 @@ class LoggingModule: def process_modules(self, modules): return - def on_show_logs(self): - self.window.show() + def _show_logs_gui(self): + if self.window: + self.window.show() From 0c92ca3808dce4adf017c72d329b9a4fb2c0cce7 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 25 Sep 2020 16:12:18 +0200 Subject: [PATCH 23/92] moved gui imports in standalone publisher from globals --- pype/modules/standalonepublish/standalonepublish_module.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/modules/standalonepublish/standalonepublish_module.py b/pype/modules/standalonepublish/standalonepublish_module.py index ed997bfd9f..f8bc0c6f24 100644 --- a/pype/modules/standalonepublish/standalonepublish_module.py +++ b/pype/modules/standalonepublish/standalonepublish_module.py @@ -2,7 +2,6 @@ import os import sys import subprocess import pype -from pype import tools class StandAlonePublishModule: @@ -30,6 +29,7 @@ class StandAlonePublishModule: )) def show(self): + from pype import tools standalone_publisher_tool_path = os.path.join( os.path.dirname(tools.__file__), "standalonepublish" From 6d3cd04e459b253732932dfd90ee4bebf066b327 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Fri, 25 Sep 2020 16:35:17 +0200 Subject: [PATCH 24/92] fix wrong exception catch --- pype/plugins/maya/publish/extract_camera_mayaScene.py | 2 +- pype/plugins/maya/publish/extract_maya_scene_raw.py | 2 +- pype/plugins/maya/publish/extract_model.py | 2 +- pype/plugins/maya/publish/extract_yeti_rig.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pype/plugins/maya/publish/extract_camera_mayaScene.py b/pype/plugins/maya/publish/extract_camera_mayaScene.py index 03dde031e9..1a0f4694d1 100644 --- a/pype/plugins/maya/publish/extract_camera_mayaScene.py +++ b/pype/plugins/maya/publish/extract_camera_mayaScene.py @@ -101,7 +101,7 @@ class ExtractCameraMayaScene(pype.api.Extractor): self.log.info( "Using {} as scene type".format(self.scene_type)) break - except AttributeError: + except KeyError: # no preset found pass diff --git a/pype/plugins/maya/publish/extract_maya_scene_raw.py b/pype/plugins/maya/publish/extract_maya_scene_raw.py index 2971572552..d273646af8 100644 --- a/pype/plugins/maya/publish/extract_maya_scene_raw.py +++ b/pype/plugins/maya/publish/extract_maya_scene_raw.py @@ -33,7 +33,7 @@ class ExtractMayaSceneRaw(pype.api.Extractor): self.log.info( "Using {} as scene type".format(self.scene_type)) break - except AttributeError: + except KeyError: # no preset found pass # Define extract output file path diff --git a/pype/plugins/maya/publish/extract_model.py b/pype/plugins/maya/publish/extract_model.py index 330e471e53..d77e65f989 100644 --- a/pype/plugins/maya/publish/extract_model.py +++ b/pype/plugins/maya/publish/extract_model.py @@ -41,7 +41,7 @@ class ExtractModel(pype.api.Extractor): self.log.info( "Using {} as scene type".format(self.scene_type)) break - except AttributeError: + except KeyError: # no preset found pass # Define extract output file path diff --git a/pype/plugins/maya/publish/extract_yeti_rig.py b/pype/plugins/maya/publish/extract_yeti_rig.py index 2f66d3e026..d48a956b88 100644 --- a/pype/plugins/maya/publish/extract_yeti_rig.py +++ b/pype/plugins/maya/publish/extract_yeti_rig.py @@ -111,7 +111,7 @@ class ExtractYetiRig(pype.api.Extractor): self.log.info( "Using {} as scene type".format(self.scene_type)) break - except AttributeError: + except KeyError: # no preset found pass yeti_nodes = cmds.ls(instance, type="pgYetiMaya") From bfb906f37ad1efcfc3d72768a12fb2841a9b583b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 30 Sep 2020 11:06:06 +0200 Subject: [PATCH 25/92] moved custom db connector to folder where is used --- .../custom_db_connector.py | 102 ++++++++---------- 1 file changed, 43 insertions(+), 59 deletions(-) rename pype/modules/ftrack/{lib => ftrack_server}/custom_db_connector.py (71%) diff --git a/pype/modules/ftrack/lib/custom_db_connector.py b/pype/modules/ftrack/ftrack_server/custom_db_connector.py similarity index 71% rename from pype/modules/ftrack/lib/custom_db_connector.py rename to pype/modules/ftrack/ftrack_server/custom_db_connector.py index d498d041dc..232481e6f4 100644 --- a/pype/modules/ftrack/lib/custom_db_connector.py +++ b/pype/modules/ftrack/ftrack_server/custom_db_connector.py @@ -16,9 +16,9 @@ import pymongo from pype.api import decompose_url -class NotActiveTable(Exception): +class NotActiveCollection(Exception): def __init__(self, *args, **kwargs): - msg = "Active table is not set. (This is bug)" + msg = "Active collection is not set. (This is bug)" if not (args or kwargs): args = [msg] super().__init__(*args, **kwargs) @@ -40,12 +40,12 @@ def auto_reconnect(func): return decorated -def check_active_table(func): +def check_active_collection(func): """Check if CustomDbConnector has active collection.""" @functools.wraps(func) def decorated(obj, *args, **kwargs): - if not obj.active_table: - raise NotActiveTable() + if not obj.active_collection: + raise NotActiveCollection() return func(obj, *args, **kwargs) return decorated @@ -55,7 +55,7 @@ class CustomDbConnector: timeout = int(os.environ["AVALON_TIMEOUT"]) def __init__( - self, uri, database_name, port=None, table_name=None + self, uri, database_name, port=None, collection_name=None ): self._mongo_client = None self._sentry_client = None @@ -76,10 +76,10 @@ class CustomDbConnector: self._port = port self._database_name = database_name - self.active_table = table_name + self.active_collection = collection_name def __getitem__(self, key): - # gives direct access to collection withou setting `active_table` + # gives direct access to collection withou setting `active_collection` return self._database[key] def __getattribute__(self, attr): @@ -88,9 +88,11 @@ class CustomDbConnector: try: return super(CustomDbConnector, self).__getattribute__(attr) except AttributeError: - if self.active_table is None: - raise NotActiveTable() - return self._database[self.active_table].__getattribute__(attr) + if self.active_collection is None: + raise NotActiveCollection() + return self._database[self.active_collection].__getattribute__( + attr + ) def install(self): """Establish a persistent connection to the database""" @@ -146,46 +148,28 @@ class CustomDbConnector: self._is_installed = False atexit.unregister(self.uninstall) - def create_table(self, name, **options): - if self.exist_table(name): + def collection_exists(self, collection_name): + return collection_name in self.collections() + + def create_collection(self, name, **options): + if self.collection_exists(name): return return self._database.create_collection(name, **options) - def exist_table(self, table_name): - return table_name in self.tables() - - def create_table(self, name, **options): - if self.exist_table(name): - return - - return self._database.create_collection(name, **options) - - def exist_table(self, table_name): - return table_name in self.tables() - - def tables(self): - """List available tables - Returns: - list of table names - """ - collection_names = self.collections() - for table_name in collection_names: - if table_name in ("system.indexes",): - continue - yield table_name - @auto_reconnect def collections(self): - return self._database.collection_names() + for col_name in self._database.collection_names(): + if col_name not in ("system.indexes",): + yield col_name - @check_active_table + @check_active_collection @auto_reconnect def insert_one(self, item, **options): assert isinstance(item, dict), "item must be of type " - return self._database[self.active_table].insert_one(item, **options) + return self._database[self.active_collection].insert_one(item, **options) - @check_active_table + @check_active_collection @auto_reconnect def insert_many(self, items, ordered=True, **options): # check if all items are valid @@ -194,72 +178,72 @@ class CustomDbConnector: assert isinstance(item, dict), "`item` must be of type " options["ordered"] = ordered - return self._database[self.active_table].insert_many(items, **options) + return self._database[self.active_collection].insert_many(items, **options) - @check_active_table + @check_active_collection @auto_reconnect def find(self, filter, projection=None, sort=None, **options): options["sort"] = sort - return self._database[self.active_table].find( + return self._database[self.active_collection].find( filter, projection, **options ) - @check_active_table + @check_active_collection @auto_reconnect def find_one(self, filter, projection=None, sort=None, **options): assert isinstance(filter, dict), "filter must be " options["sort"] = sort - return self._database[self.active_table].find_one( + return self._database[self.active_collection].find_one( filter, projection, **options ) - @check_active_table + @check_active_collection @auto_reconnect def replace_one(self, filter, replacement, **options): - return self._database[self.active_table].replace_one( + return self._database[self.active_collection].replace_one( filter, replacement, **options ) - @check_active_table + @check_active_collection @auto_reconnect def update_one(self, filter, update, **options): - return self._database[self.active_table].update_one( + return self._database[self.active_collection].update_one( filter, update, **options ) - @check_active_table + @check_active_collection @auto_reconnect def update_many(self, filter, update, **options): - return self._database[self.active_table].update_many( + return self._database[self.active_collection].update_many( filter, update, **options ) - @check_active_table + @check_active_collection @auto_reconnect def distinct(self, **options): - return self._database[self.active_table].distinct(**options) + return self._database[self.active_collection].distinct(**options) - @check_active_table + @check_active_collection @auto_reconnect def drop_collection(self, name_or_collection, **options): - return self._database[self.active_table].drop( + return self._database[self.active_collection].drop( name_or_collection, **options ) - @check_active_table + @check_active_collection @auto_reconnect def delete_one(self, filter, collation=None, **options): options["collation"] = collation - return self._database[self.active_table].delete_one( + return self._database[self.active_collection].delete_one( filter, **options ) - @check_active_table + @check_active_collection @auto_reconnect def delete_many(self, filter, collation=None, **options): options["collation"] = collation - return self._database[self.active_table].delete_many( + return self._database[self.active_collection].delete_many( filter, **options ) From b41f9ac8bc63c54f380c24780fe7ac10889afb80 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 30 Sep 2020 11:07:35 +0200 Subject: [PATCH 26/92] variables with table changed to collection --- pype/modules/ftrack/ftrack_server/lib.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pype/modules/ftrack/ftrack_server/lib.py b/pype/modules/ftrack/ftrack_server/lib.py index ee6b1216dc..3a1c742ae8 100644 --- a/pype/modules/ftrack/ftrack_server/lib.py +++ b/pype/modules/ftrack/ftrack_server/lib.py @@ -153,9 +153,9 @@ class StorerEventHub(SocketBaseEventHub): class ProcessEventHub(SocketBaseEventHub): hearbeat_msg = b"processor" - uri, port, database, table_name = get_ftrack_event_mongo_info() + uri, port, database, collection_name = get_ftrack_event_mongo_info() - is_table_created = False + is_collection_created = False pypelog = Logger().get_logger("Session Processor") def __init__(self, *args, **kwargs): @@ -163,7 +163,7 @@ class ProcessEventHub(SocketBaseEventHub): self.uri, self.database, self.port, - self.table_name + self.collection_name ) super(ProcessEventHub, self).__init__(*args, **kwargs) @@ -184,7 +184,7 @@ class ProcessEventHub(SocketBaseEventHub): "Error with Mongo access, probably permissions." "Check if exist database with name \"{}\"" " and collection \"{}\" inside." - ).format(self.database, self.table_name)) + ).format(self.database, self.collection_name)) self.sock.sendall(b"MongoError") sys.exit(0) From 15172d32e3295b2f404c18cdabb32eeac800e85c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 30 Sep 2020 11:12:23 +0200 Subject: [PATCH 27/92] modified imports inside ftrack module to be explicit --- pype/modules/ftrack/__init__.py | 12 +++++++++++- pype/modules/ftrack/ftrack_server/__init__.py | 6 ++++++ pype/modules/ftrack/ftrack_server/lib.py | 2 +- .../modules/ftrack/ftrack_server/sub_event_storer.py | 10 ++++++---- pype/modules/ftrack/lib/ftrack_base_handler.py | 6 +++--- pype/modules/ftrack/tray/login_dialog.py | 2 +- 6 files changed, 28 insertions(+), 10 deletions(-) diff --git a/pype/modules/ftrack/__init__.py b/pype/modules/ftrack/__init__.py index aa8f04bffb..fad771f084 100644 --- a/pype/modules/ftrack/__init__.py +++ b/pype/modules/ftrack/__init__.py @@ -1,2 +1,12 @@ -from .lib import * +from . import ftrack_server from .ftrack_server import FtrackServer, check_ftrack_url +from .lib import BaseHandler, BaseEvent, BaseAction + +__all__ = ( + "ftrack_server", + "FtrackServer", + "check_ftrack_url", + "BaseHandler", + "BaseEvent", + "BaseAction" +) diff --git a/pype/modules/ftrack/ftrack_server/__init__.py b/pype/modules/ftrack/ftrack_server/__init__.py index fcae4e0690..9e3920b500 100644 --- a/pype/modules/ftrack/ftrack_server/__init__.py +++ b/pype/modules/ftrack/ftrack_server/__init__.py @@ -1,2 +1,8 @@ from .ftrack_server import FtrackServer from .lib import check_ftrack_url + + +__all__ = ( + "FtrackServer", + "check_ftrack_url" +) diff --git a/pype/modules/ftrack/ftrack_server/lib.py b/pype/modules/ftrack/ftrack_server/lib.py index 3a1c742ae8..79b708b17a 100644 --- a/pype/modules/ftrack/ftrack_server/lib.py +++ b/pype/modules/ftrack/ftrack_server/lib.py @@ -26,7 +26,7 @@ from pype.api import ( compose_url ) -from pype.modules.ftrack.lib.custom_db_connector import CustomDbConnector +from .custom_db_connector import CustomDbConnector TOPIC_STATUS_SERVER = "pype.event.server.status" diff --git a/pype/modules/ftrack/ftrack_server/sub_event_storer.py b/pype/modules/ftrack/ftrack_server/sub_event_storer.py index 1635f6cea3..2f4395c8db 100644 --- a/pype/modules/ftrack/ftrack_server/sub_event_storer.py +++ b/pype/modules/ftrack/ftrack_server/sub_event_storer.py @@ -12,7 +12,9 @@ from pype.modules.ftrack.ftrack_server.lib import ( get_ftrack_event_mongo_info, TOPIC_STATUS_SERVER, TOPIC_STATUS_SERVER_RESULT ) -from pype.modules.ftrack.lib.custom_db_connector import CustomDbConnector +from pype.modules.ftrack.ftrack_server.custom_db_connector import ( + CustomDbConnector +) from pype.api import Logger log = Logger().get_logger("Event storer") @@ -23,8 +25,8 @@ class SessionFactory: session = None -uri, port, database, table_name = get_ftrack_event_mongo_info() -dbcon = CustomDbConnector(uri, database, port, table_name) +uri, port, database, collection_name = get_ftrack_event_mongo_info() +dbcon = CustomDbConnector(uri, database, port, collection_name) # ignore_topics = ["ftrack.meta.connected"] ignore_topics = [] @@ -200,7 +202,7 @@ def main(args): "Error with Mongo access, probably permissions." "Check if exist database with name \"{}\"" " and collection \"{}\" inside." - ).format(database, table_name)) + ).format(database, collection_name)) sock.sendall(b"MongoError") finally: diff --git a/pype/modules/ftrack/lib/ftrack_base_handler.py b/pype/modules/ftrack/lib/ftrack_base_handler.py index ce6607d6bf..d322fbaf23 100644 --- a/pype/modules/ftrack/lib/ftrack_base_handler.py +++ b/pype/modules/ftrack/lib/ftrack_base_handler.py @@ -2,7 +2,7 @@ import functools import time from pype.api import Logger import ftrack_api -from pype.modules.ftrack.ftrack_server.lib import SocketSession +from pype.modules.ftrack import ftrack_server class MissingPermision(Exception): @@ -41,7 +41,7 @@ class BaseHandler(object): self.log = Logger().get_logger(self.__class__.__name__) if not( isinstance(session, ftrack_api.session.Session) or - isinstance(session, SocketSession) + isinstance(session, ftrack_server.lib.SocketSession) ): raise Exception(( "Session object entered with args is instance of \"{}\"" @@ -49,7 +49,7 @@ class BaseHandler(object): ).format( str(type(session)), str(ftrack_api.session.Session), - str(SocketSession) + str(ftrack_server.lib.SocketSession) )) self._session = session diff --git a/pype/modules/ftrack/tray/login_dialog.py b/pype/modules/ftrack/tray/login_dialog.py index 7730ee1609..aeed82671f 100644 --- a/pype/modules/ftrack/tray/login_dialog.py +++ b/pype/modules/ftrack/tray/login_dialog.py @@ -1,7 +1,7 @@ import os import requests from avalon import style -from pype.modules.ftrack import credentials +from pype.modules.ftrack.lib import credentials from . import login_tools from pype.api import resources from Qt import QtCore, QtGui, QtWidgets From 689c483631a42dd39079bf02851e6e1f9d05225c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 30 Sep 2020 11:12:31 +0200 Subject: [PATCH 28/92] formatting changes --- pype/modules/ftrack/lib/avalon_sync.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/modules/ftrack/lib/avalon_sync.py b/pype/modules/ftrack/lib/avalon_sync.py index 03124ab10d..292ce752cf 100644 --- a/pype/modules/ftrack/lib/avalon_sync.py +++ b/pype/modules/ftrack/lib/avalon_sync.py @@ -1022,7 +1022,7 @@ class SyncEntitiesFactory: continue ent_path_items = [ent["name"] for ent in entity["link"]] - parents = ent_path_items[1:len(ent_path_items)-1:] + parents = ent_path_items[1:len(ent_path_items) - 1:] hierarchy = "" if len(parents) > 0: hierarchy = os.path.sep.join(parents) @@ -1141,7 +1141,7 @@ class SyncEntitiesFactory: if not is_right and not else_match_better: entity = entity_dict["entity"] ent_path_items = [ent["name"] for ent in entity["link"]] - parents = ent_path_items[1:len(ent_path_items)-1:] + parents = ent_path_items[1:len(ent_path_items) - 1:] av_parents = av_ent_by_mongo_id["data"]["parents"] if av_parents == parents: is_right = True From bb77c72b98d204b39d7d4bd953df289eeab5b18e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 30 Sep 2020 11:30:21 +0200 Subject: [PATCH 29/92] fix thread stopping in ftrack login --- pype/modules/ftrack/tray/login_dialog.py | 2 ++ pype/modules/ftrack/tray/login_tools.py | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/pype/modules/ftrack/tray/login_dialog.py b/pype/modules/ftrack/tray/login_dialog.py index aeed82671f..94ad29e478 100644 --- a/pype/modules/ftrack/tray/login_dialog.py +++ b/pype/modules/ftrack/tray/login_dialog.py @@ -238,6 +238,8 @@ class CredentialsDialog(QtWidgets.QDialog): # If there is an existing server thread running we need to stop it. if self._login_server_thread: + if self._login_server_thread.isAlive(): + self._login_server_thread.stop() self._login_server_thread.join() self._login_server_thread = None diff --git a/pype/modules/ftrack/tray/login_tools.py b/pype/modules/ftrack/tray/login_tools.py index e7d22fbc19..d3297eaa76 100644 --- a/pype/modules/ftrack/tray/login_tools.py +++ b/pype/modules/ftrack/tray/login_tools.py @@ -61,12 +61,17 @@ class LoginServerThread(threading.Thread): def __init__(self, url, callback): self.url = url self.callback = callback + self._server = None super(LoginServerThread, self).__init__() def _handle_login(self, api_user, api_key): '''Login to server with *api_user* and *api_key*.''' self.callback(api_user, api_key) + def stop(self): + if self._server: + self._server.server_close() + def run(self): '''Listen for events.''' self._server = HTTPServer( From c9f381e60b6b1fc4956c1bfb718d49f8180c2478 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 30 Sep 2020 11:46:23 +0200 Subject: [PATCH 30/92] hound fixes --- pype/modules/ftrack/ftrack_server/custom_db_connector.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pype/modules/ftrack/ftrack_server/custom_db_connector.py b/pype/modules/ftrack/ftrack_server/custom_db_connector.py index 232481e6f4..8a8ba4ccbb 100644 --- a/pype/modules/ftrack/ftrack_server/custom_db_connector.py +++ b/pype/modules/ftrack/ftrack_server/custom_db_connector.py @@ -167,7 +167,9 @@ class CustomDbConnector: @auto_reconnect def insert_one(self, item, **options): assert isinstance(item, dict), "item must be of type " - return self._database[self.active_collection].insert_one(item, **options) + return self._database[self.active_collection].insert_one( + item, **options + ) @check_active_collection @auto_reconnect @@ -178,7 +180,9 @@ class CustomDbConnector: assert isinstance(item, dict), "`item` must be of type " options["ordered"] = ordered - return self._database[self.active_collection].insert_many(items, **options) + return self._database[self.active_collection].insert_many( + items, **options + ) @check_active_collection @auto_reconnect From f16d601a8a1c00d48404909662f90ed1010dd378 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 30 Sep 2020 12:22:17 +0200 Subject: [PATCH 31/92] changed default port to 8098 --- pype/modules/websocket_server/websocket_server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/modules/websocket_server/websocket_server.py b/pype/modules/websocket_server/websocket_server.py index ec6785625a..daf4b03103 100644 --- a/pype/modules/websocket_server/websocket_server.py +++ b/pype/modules/websocket_server/websocket_server.py @@ -37,7 +37,7 @@ class WebSocketServer(): parsed = urllib.parse.urlparse(websocket_url) port = parsed.port if not port: - port = 8099 # fallback + port = 8098 # fallback self.app = web.Application() From b7211626b05dc3fba8cf213b9cc235d76d2bc874 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 30 Sep 2020 12:40:13 +0200 Subject: [PATCH 32/92] fix(sp): adding "clip" family to better filter editorial instances --- .../standalonepublisher/publish/collect_clip_instances.py | 4 ++-- .../standalonepublisher/publish/extract_shot_data.py | 2 +- .../publish/validate_editorial_resources.py | 8 +++++--- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/pype/plugins/standalonepublisher/publish/collect_clip_instances.py b/pype/plugins/standalonepublisher/publish/collect_clip_instances.py index a7af8df143..def0c13a78 100644 --- a/pype/plugins/standalonepublisher/publish/collect_clip_instances.py +++ b/pype/plugins/standalonepublisher/publish/collect_clip_instances.py @@ -17,13 +17,13 @@ class CollectClipInstances(pyblish.api.InstancePlugin): subsets = { "referenceMain": { "family": "review", - "families": ["review", "ftrack"], + "families": ["clip", "ftrack"], # "ftrackFamily": "review", "extension": ".mp4" }, "audioMain": { "family": "audio", - "families": ["ftrack"], + "families": ["clip", "ftrack"], # "ftrackFamily": "audio", "extension": ".wav", # "version": 1 diff --git a/pype/plugins/standalonepublisher/publish/extract_shot_data.py b/pype/plugins/standalonepublisher/publish/extract_shot_data.py index c39247d6d6..d5af7638ee 100644 --- a/pype/plugins/standalonepublisher/publish/extract_shot_data.py +++ b/pype/plugins/standalonepublisher/publish/extract_shot_data.py @@ -10,7 +10,7 @@ class ExtractShotData(pype.api.Extractor): label = "Extract Shot Data" hosts = ["standalonepublisher"] - families = ["review", "audio"] + families = ["clip"] # presets diff --git a/pype/plugins/standalonepublisher/publish/validate_editorial_resources.py b/pype/plugins/standalonepublisher/publish/validate_editorial_resources.py index ebc449c4ec..7e1694fbd1 100644 --- a/pype/plugins/standalonepublisher/publish/validate_editorial_resources.py +++ b/pype/plugins/standalonepublisher/publish/validate_editorial_resources.py @@ -1,5 +1,3 @@ -import os - import pyblish.api import pype.api @@ -9,10 +7,14 @@ class ValidateEditorialResources(pyblish.api.InstancePlugin): label = "Validate Editorial Resources" hosts = ["standalonepublisher"] - families = ["audio", "review"] + families = ["clip"] + order = pype.api.ValidateContentsOrder def process(self, instance): + self.log.debug( + f"Instance: {instance}, Families: " + f"{[instance.data['family']] + instance.data['families']}") check_file = instance.data["editorialVideoPath"] msg = f"Missing \"{check_file}\"." assert check_file, msg From d384fb9a45afa0c0b065ed977eced85a6269002a Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 30 Sep 2020 13:21:24 +0200 Subject: [PATCH 33/92] Qt is not used in avalon_apps module if tray_init is not triggered --- pype/modules/avalon_apps/avalon_app.py | 34 ++++++++++++++++++-------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/pype/modules/avalon_apps/avalon_app.py b/pype/modules/avalon_apps/avalon_app.py index 7ed651f82b..de10268304 100644 --- a/pype/modules/avalon_apps/avalon_app.py +++ b/pype/modules/avalon_apps/avalon_app.py @@ -1,16 +1,27 @@ -from Qt import QtWidgets -from avalon.tools import libraryloader from pype.api import Logger -from pype.tools.launcher import LauncherWindow, actions class AvalonApps: def __init__(self, main_parent=None, parent=None): self.log = Logger().get_logger(__name__) - self.main_parent = main_parent + + self.tray_init(main_parent, parent) + + def tray_init(self, main_parent, parent): + from avalon.tools.libraryloader import app + from avalon import style + from pype.tools.launcher import LauncherWindow, actions + self.parent = parent + self.main_parent = main_parent self.app_launcher = LauncherWindow() + self.libraryloader = app.Window( + icon=self.parent.icon, + show_projects=True, + show_libraries=True + ) + self.libraryloader.setStyleSheet(style.load_stylesheet()) # actions.register_default_actions() actions.register_config_actions() @@ -23,6 +34,7 @@ class AvalonApps: # Definition of Tray menu def tray_menu(self, parent_menu=None): + from Qt import QtWidgets # Actions if parent_menu is None: if self.parent is None: @@ -52,9 +64,11 @@ class AvalonApps: self.app_launcher.activateWindow() def show_library_loader(self): - libraryloader.show( - parent=self.main_parent, - icon=self.parent.icon, - show_projects=True, - show_libraries=True - ) + self.libraryloader.show() + + # Raise and activate the window + # for MacOS + self.libraryloader.raise_() + # for Windows + self.libraryloader.activateWindow() + self.libraryloader.refresh() From 7b93174d920d3f7f4dc582bdae75b155b19c64a7 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 30 Sep 2020 14:39:30 +0200 Subject: [PATCH 34/92] moved environments folder one level higher --- .../{system_settings => }/environments/avalon.json | 0 .../{system_settings => }/environments/blender.json | 3 ++- .../{system_settings => }/environments/celaction.json | 0 .../{system_settings => }/environments/deadline.json | 0 .../{system_settings => }/environments/ftrack.json | 0 .../{system_settings => }/environments/global.json | 6 +++--- .../{system_settings => }/environments/harmony.json | 0 .../{system_settings => }/environments/houdini.json | 0 .../defaults/{system_settings => }/environments/maya.json | 0 .../{system_settings => }/environments/maya_2018.json | 0 .../{system_settings => }/environments/maya_2020.json | 0 .../{system_settings => }/environments/mayabatch.json | 0 .../{system_settings => }/environments/mayabatch_2019.json | 0 .../{system_settings => }/environments/mtoa_3.1.1.json | 0 .../{system_settings => }/environments/muster.json | 0 .../defaults/{system_settings => }/environments/nuke.json | 0 .../{system_settings => }/environments/nukestudio.json | 0 .../environments/nukestudio_10.0.json | 0 .../defaults/{system_settings => }/environments/nukex.json | 0 .../{system_settings => }/environments/nukex_10.0.json | 0 pype/settings/defaults/environments/photoshop.json | 7 +++++++ .../{system_settings => }/environments/premiere.json | 0 .../{system_settings => }/environments/resolve.json | 0 .../{system_settings => }/environments/storyboardpro.json | 0 .../{system_settings => }/environments/unreal_4.24.json | 0 .../{system_settings => }/environments/vray_4300.json | 0 .../defaults/system_settings/environments/photoshop.json | 4 ---- 27 files changed, 12 insertions(+), 8 deletions(-) rename pype/settings/defaults/{system_settings => }/environments/avalon.json (100%) rename pype/settings/defaults/{system_settings => }/environments/blender.json (82%) rename pype/settings/defaults/{system_settings => }/environments/celaction.json (100%) rename pype/settings/defaults/{system_settings => }/environments/deadline.json (100%) rename pype/settings/defaults/{system_settings => }/environments/ftrack.json (100%) rename pype/settings/defaults/{system_settings => }/environments/global.json (91%) rename pype/settings/defaults/{system_settings => }/environments/harmony.json (100%) rename pype/settings/defaults/{system_settings => }/environments/houdini.json (100%) rename pype/settings/defaults/{system_settings => }/environments/maya.json (100%) rename pype/settings/defaults/{system_settings => }/environments/maya_2018.json (100%) rename pype/settings/defaults/{system_settings => }/environments/maya_2020.json (100%) rename pype/settings/defaults/{system_settings => }/environments/mayabatch.json (100%) rename pype/settings/defaults/{system_settings => }/environments/mayabatch_2019.json (100%) rename pype/settings/defaults/{system_settings => }/environments/mtoa_3.1.1.json (100%) rename pype/settings/defaults/{system_settings => }/environments/muster.json (100%) rename pype/settings/defaults/{system_settings => }/environments/nuke.json (100%) rename pype/settings/defaults/{system_settings => }/environments/nukestudio.json (100%) rename pype/settings/defaults/{system_settings => }/environments/nukestudio_10.0.json (100%) rename pype/settings/defaults/{system_settings => }/environments/nukex.json (100%) rename pype/settings/defaults/{system_settings => }/environments/nukex_10.0.json (100%) create mode 100644 pype/settings/defaults/environments/photoshop.json rename pype/settings/defaults/{system_settings => }/environments/premiere.json (100%) rename pype/settings/defaults/{system_settings => }/environments/resolve.json (100%) rename pype/settings/defaults/{system_settings => }/environments/storyboardpro.json (100%) rename pype/settings/defaults/{system_settings => }/environments/unreal_4.24.json (100%) rename pype/settings/defaults/{system_settings => }/environments/vray_4300.json (100%) delete mode 100644 pype/settings/defaults/system_settings/environments/photoshop.json diff --git a/pype/settings/defaults/system_settings/environments/avalon.json b/pype/settings/defaults/environments/avalon.json similarity index 100% rename from pype/settings/defaults/system_settings/environments/avalon.json rename to pype/settings/defaults/environments/avalon.json diff --git a/pype/settings/defaults/system_settings/environments/blender.json b/pype/settings/defaults/environments/blender.json similarity index 82% rename from pype/settings/defaults/system_settings/environments/blender.json rename to pype/settings/defaults/environments/blender.json index 6f4f6a012d..00a4070b8e 100644 --- a/pype/settings/defaults/system_settings/environments/blender.json +++ b/pype/settings/defaults/environments/blender.json @@ -3,5 +3,6 @@ "PYTHONPATH": [ "{PYPE_SETUP_PATH}/repos/avalon-core/setup/blender", "{PYTHONPATH}" - ] + ], + "CREATE_NEW_CONSOLE": "yes" } diff --git a/pype/settings/defaults/system_settings/environments/celaction.json b/pype/settings/defaults/environments/celaction.json similarity index 100% rename from pype/settings/defaults/system_settings/environments/celaction.json rename to pype/settings/defaults/environments/celaction.json diff --git a/pype/settings/defaults/system_settings/environments/deadline.json b/pype/settings/defaults/environments/deadline.json similarity index 100% rename from pype/settings/defaults/system_settings/environments/deadline.json rename to pype/settings/defaults/environments/deadline.json diff --git a/pype/settings/defaults/system_settings/environments/ftrack.json b/pype/settings/defaults/environments/ftrack.json similarity index 100% rename from pype/settings/defaults/system_settings/environments/ftrack.json rename to pype/settings/defaults/environments/ftrack.json diff --git a/pype/settings/defaults/system_settings/environments/global.json b/pype/settings/defaults/environments/global.json similarity index 91% rename from pype/settings/defaults/system_settings/environments/global.json rename to pype/settings/defaults/environments/global.json index ef528e6857..ba467d2f5d 100644 --- a/pype/settings/defaults/system_settings/environments/global.json +++ b/pype/settings/defaults/environments/global.json @@ -6,9 +6,9 @@ "PYPE_PROJECT_PLUGINS": "", "STUDIO_SOFT": "{PYP_SETUP_ROOT}/soft", "FFMPEG_PATH": { - "windows": "{VIRTUAL_ENV}/localized/ffmpeg_exec/windows/bin;{PYPE_SETUP_PATH}/vendor/ffmpeg_exec/windows/bin", - "darwin": "{VIRTUAL_ENV}/localized/ffmpeg_exec/darwin/bin:{PYPE_SETUP_PATH}/vendor/ffmpeg_exec/darwin/bin", - "linux": "{VIRTUAL_ENV}/localized/ffmpeg_exec/linux:{PYPE_SETUP_PATH}/vendor/ffmpeg_exec/linux" + "windows": "{VIRTUAL_ENV}/localized/ffmpeg_exec/windows/bin;{PYPE_SETUP_PATH}/vendor/bin/ffmpeg_exec/windows/bin", + "darwin": "{VIRTUAL_ENV}/localized/ffmpeg_exec/darwin/bin:{PYPE_SETUP_PATH}/vendor/bin/ffmpeg_exec/darwin/bin", + "linux": "{VIRTUAL_ENV}/localized/ffmpeg_exec/linux:{PYPE_SETUP_PATH}/vendor/bin/ffmpeg_exec/linux" }, "DJV_PATH": { "windows": [ diff --git a/pype/settings/defaults/system_settings/environments/harmony.json b/pype/settings/defaults/environments/harmony.json similarity index 100% rename from pype/settings/defaults/system_settings/environments/harmony.json rename to pype/settings/defaults/environments/harmony.json diff --git a/pype/settings/defaults/system_settings/environments/houdini.json b/pype/settings/defaults/environments/houdini.json similarity index 100% rename from pype/settings/defaults/system_settings/environments/houdini.json rename to pype/settings/defaults/environments/houdini.json diff --git a/pype/settings/defaults/system_settings/environments/maya.json b/pype/settings/defaults/environments/maya.json similarity index 100% rename from pype/settings/defaults/system_settings/environments/maya.json rename to pype/settings/defaults/environments/maya.json diff --git a/pype/settings/defaults/system_settings/environments/maya_2018.json b/pype/settings/defaults/environments/maya_2018.json similarity index 100% rename from pype/settings/defaults/system_settings/environments/maya_2018.json rename to pype/settings/defaults/environments/maya_2018.json diff --git a/pype/settings/defaults/system_settings/environments/maya_2020.json b/pype/settings/defaults/environments/maya_2020.json similarity index 100% rename from pype/settings/defaults/system_settings/environments/maya_2020.json rename to pype/settings/defaults/environments/maya_2020.json diff --git a/pype/settings/defaults/system_settings/environments/mayabatch.json b/pype/settings/defaults/environments/mayabatch.json similarity index 100% rename from pype/settings/defaults/system_settings/environments/mayabatch.json rename to pype/settings/defaults/environments/mayabatch.json diff --git a/pype/settings/defaults/system_settings/environments/mayabatch_2019.json b/pype/settings/defaults/environments/mayabatch_2019.json similarity index 100% rename from pype/settings/defaults/system_settings/environments/mayabatch_2019.json rename to pype/settings/defaults/environments/mayabatch_2019.json diff --git a/pype/settings/defaults/system_settings/environments/mtoa_3.1.1.json b/pype/settings/defaults/environments/mtoa_3.1.1.json similarity index 100% rename from pype/settings/defaults/system_settings/environments/mtoa_3.1.1.json rename to pype/settings/defaults/environments/mtoa_3.1.1.json diff --git a/pype/settings/defaults/system_settings/environments/muster.json b/pype/settings/defaults/environments/muster.json similarity index 100% rename from pype/settings/defaults/system_settings/environments/muster.json rename to pype/settings/defaults/environments/muster.json diff --git a/pype/settings/defaults/system_settings/environments/nuke.json b/pype/settings/defaults/environments/nuke.json similarity index 100% rename from pype/settings/defaults/system_settings/environments/nuke.json rename to pype/settings/defaults/environments/nuke.json diff --git a/pype/settings/defaults/system_settings/environments/nukestudio.json b/pype/settings/defaults/environments/nukestudio.json similarity index 100% rename from pype/settings/defaults/system_settings/environments/nukestudio.json rename to pype/settings/defaults/environments/nukestudio.json diff --git a/pype/settings/defaults/system_settings/environments/nukestudio_10.0.json b/pype/settings/defaults/environments/nukestudio_10.0.json similarity index 100% rename from pype/settings/defaults/system_settings/environments/nukestudio_10.0.json rename to pype/settings/defaults/environments/nukestudio_10.0.json diff --git a/pype/settings/defaults/system_settings/environments/nukex.json b/pype/settings/defaults/environments/nukex.json similarity index 100% rename from pype/settings/defaults/system_settings/environments/nukex.json rename to pype/settings/defaults/environments/nukex.json diff --git a/pype/settings/defaults/system_settings/environments/nukex_10.0.json b/pype/settings/defaults/environments/nukex_10.0.json similarity index 100% rename from pype/settings/defaults/system_settings/environments/nukex_10.0.json rename to pype/settings/defaults/environments/nukex_10.0.json diff --git a/pype/settings/defaults/environments/photoshop.json b/pype/settings/defaults/environments/photoshop.json new file mode 100644 index 0000000000..d39634ce20 --- /dev/null +++ b/pype/settings/defaults/environments/photoshop.json @@ -0,0 +1,7 @@ +{ + "AVALON_PHOTOSHOP_WORKFILES_ON_LAUNCH": "1", + "PYTHONPATH": "{PYTHONPATH}", + "PYPE_LOG_NO_COLORS": "Yes", + "WEBSOCKET_URL": "ws://localhost:8099/ws/", + "WORKFILES_SAVE_AS": "Yes" +} diff --git a/pype/settings/defaults/system_settings/environments/premiere.json b/pype/settings/defaults/environments/premiere.json similarity index 100% rename from pype/settings/defaults/system_settings/environments/premiere.json rename to pype/settings/defaults/environments/premiere.json diff --git a/pype/settings/defaults/system_settings/environments/resolve.json b/pype/settings/defaults/environments/resolve.json similarity index 100% rename from pype/settings/defaults/system_settings/environments/resolve.json rename to pype/settings/defaults/environments/resolve.json diff --git a/pype/settings/defaults/system_settings/environments/storyboardpro.json b/pype/settings/defaults/environments/storyboardpro.json similarity index 100% rename from pype/settings/defaults/system_settings/environments/storyboardpro.json rename to pype/settings/defaults/environments/storyboardpro.json diff --git a/pype/settings/defaults/system_settings/environments/unreal_4.24.json b/pype/settings/defaults/environments/unreal_4.24.json similarity index 100% rename from pype/settings/defaults/system_settings/environments/unreal_4.24.json rename to pype/settings/defaults/environments/unreal_4.24.json diff --git a/pype/settings/defaults/system_settings/environments/vray_4300.json b/pype/settings/defaults/environments/vray_4300.json similarity index 100% rename from pype/settings/defaults/system_settings/environments/vray_4300.json rename to pype/settings/defaults/environments/vray_4300.json diff --git a/pype/settings/defaults/system_settings/environments/photoshop.json b/pype/settings/defaults/system_settings/environments/photoshop.json deleted file mode 100644 index 2208a88665..0000000000 --- a/pype/settings/defaults/system_settings/environments/photoshop.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "AVALON_PHOTOSHOP_WORKFILES_ON_LAUNCH": "1", - "PYTHONPATH": "{PYTHONPATH}" -} From 68c10ab1ee47e9ddc408d7006c2562941f282963 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 30 Sep 2020 14:39:44 +0200 Subject: [PATCH 35/92] added launchers to pype --- .../defaults/launchers/blender_2.80.toml | 8 +++++ .../defaults/launchers/blender_2.81.toml | 9 ++++++ .../defaults/launchers/blender_2.82.toml | 9 ++++++ .../defaults/launchers/blender_2.83.toml | 9 ++++++ .../defaults/launchers/celaction_local.toml | 9 ++++++ .../defaults/launchers/celaction_publish.toml | 8 +++++ .../defaults/launchers/darwin/blender_2.82 | 2 ++ .../defaults/launchers/darwin/harmony_17 | 9 ++++++ .../launchers/darwin/harmony_17_launch | 5 ++++ .../defaults/launchers/darwin/python3 | 2 ++ .../defaults/launchers/harmony_17.toml | 9 ++++++ .../defaults/launchers/houdini_16.toml | 8 +++++ .../defaults/launchers/houdini_17.toml | 8 +++++ .../defaults/launchers/houdini_18.toml | 8 +++++ .../defaults/launchers/linux/maya2016 | 8 +++++ .../defaults/launchers/linux/maya2017 | 8 +++++ .../defaults/launchers/linux/maya2018 | 8 +++++ .../defaults/launchers/linux/maya2019 | 8 +++++ .../defaults/launchers/linux/maya2020 | 8 +++++ .../defaults/launchers/linux/nuke11.3 | 2 ++ .../defaults/launchers/linux/nuke12.0 | 2 ++ .../defaults/launchers/linux/nukestudio11.3 | 2 ++ .../defaults/launchers/linux/nukestudio12.0 | 2 ++ .../defaults/launchers/linux/nukex11.3 | 2 ++ .../defaults/launchers/linux/nukex12.0 | 2 ++ .../defaults/launchers/maya_2016.toml | 27 +++++++++++++++++ .../defaults/launchers/maya_2017.toml | 29 +++++++++++++++++++ .../defaults/launchers/maya_2018.toml | 15 ++++++++++ .../defaults/launchers/maya_2019.toml | 15 ++++++++++ .../defaults/launchers/maya_2020.toml | 15 ++++++++++ .../defaults/launchers/mayabatch_2019.toml | 17 +++++++++++ .../defaults/launchers/mayabatch_2020.toml | 17 +++++++++++ .../defaults/launchers/mayapy2016.toml | 17 +++++++++++ .../defaults/launchers/mayapy2017.toml | 17 +++++++++++ .../defaults/launchers/mayapy2018.toml | 17 +++++++++++ .../defaults/launchers/mayapy2019.toml | 17 +++++++++++ .../defaults/launchers/mayapy2020.toml | 17 +++++++++++ pype/settings/defaults/launchers/myapp.toml | 5 ++++ .../defaults/launchers/nuke_10.0.toml | 8 +++++ .../defaults/launchers/nuke_11.0.toml | 8 +++++ .../defaults/launchers/nuke_11.2.toml | 8 +++++ .../defaults/launchers/nuke_11.3.toml | 8 +++++ .../defaults/launchers/nuke_12.0.toml | 8 +++++ .../defaults/launchers/nukestudio_10.0.toml | 8 +++++ .../defaults/launchers/nukestudio_11.0.toml | 8 +++++ .../defaults/launchers/nukestudio_11.2.toml | 8 +++++ .../defaults/launchers/nukestudio_11.3.toml | 8 +++++ .../defaults/launchers/nukestudio_12.0.toml | 8 +++++ .../defaults/launchers/nukex_10.0.toml | 8 +++++ .../defaults/launchers/nukex_11.0.toml | 8 +++++ .../defaults/launchers/nukex_11.2.toml | 8 +++++ .../defaults/launchers/nukex_11.3.toml | 8 +++++ .../defaults/launchers/nukex_12.0.toml | 8 +++++ .../defaults/launchers/photoshop_2020.toml | 9 ++++++ .../defaults/launchers/premiere_2019.toml | 9 ++++++ .../defaults/launchers/premiere_2020.toml | 10 +++++++ .../settings/defaults/launchers/python_2.toml | 12 ++++++++ .../settings/defaults/launchers/python_3.toml | 12 ++++++++ .../defaults/launchers/resolve_16.toml | 10 +++++++ pype/settings/defaults/launchers/shell.toml | 7 +++++ .../defaults/launchers/storyboardpro_7.toml | 9 ++++++ .../defaults/launchers/unreal_4.24.toml | 10 +++++++ .../launchers/windows/blender_2.80.bat | 11 +++++++ .../launchers/windows/blender_2.81.bat | 11 +++++++ .../launchers/windows/blender_2.82.bat | 11 +++++++ .../launchers/windows/blender_2.83.bat | 11 +++++++ .../launchers/windows/celaction_local.bat | 19 ++++++++++++ .../launchers/windows/celaction_publish.bat | 3 ++ .../defaults/launchers/windows/harmony_17.bat | 13 +++++++++ .../defaults/launchers/windows/houdini_16.bat | 13 +++++++++ .../defaults/launchers/windows/houdini_17.bat | 13 +++++++++ .../defaults/launchers/windows/houdini_18.bat | 13 +++++++++ .../defaults/launchers/windows/maya2016.bat | 17 +++++++++++ .../defaults/launchers/windows/maya2017.bat | 17 +++++++++++ .../defaults/launchers/windows/maya2018.bat | 17 +++++++++++ .../defaults/launchers/windows/maya2019.bat | 17 +++++++++++ .../defaults/launchers/windows/maya2020.bat | 17 +++++++++++ .../launchers/windows/mayabatch2019.bat | 14 +++++++++ .../launchers/windows/mayabatch2020.bat | 14 +++++++++ .../defaults/launchers/windows/mayapy2016.bat | 13 +++++++++ .../defaults/launchers/windows/mayapy2017.bat | 13 +++++++++ .../defaults/launchers/windows/mayapy2018.bat | 13 +++++++++ .../defaults/launchers/windows/mayapy2019.bat | 13 +++++++++ .../defaults/launchers/windows/mayapy2020.bat | 13 +++++++++ .../defaults/launchers/windows/nuke10.0.bat | 13 +++++++++ .../defaults/launchers/windows/nuke11.0.bat | 13 +++++++++ .../defaults/launchers/windows/nuke11.2.bat | 13 +++++++++ .../defaults/launchers/windows/nuke11.3.bat | 13 +++++++++ .../defaults/launchers/windows/nuke12.0.bat | 13 +++++++++ .../launchers/windows/nukestudio10.0.bat | 13 +++++++++ .../launchers/windows/nukestudio11.0.bat | 13 +++++++++ .../launchers/windows/nukestudio11.2.bat | 13 +++++++++ .../launchers/windows/nukestudio11.3.bat | 13 +++++++++ .../launchers/windows/nukestudio12.0.bat | 13 +++++++++ .../defaults/launchers/windows/nukex10.0.bat | 13 +++++++++ .../defaults/launchers/windows/nukex11.0.bat | 13 +++++++++ .../defaults/launchers/windows/nukex11.2.bat | 13 +++++++++ .../defaults/launchers/windows/nukex11.3.bat | 13 +++++++++ .../defaults/launchers/windows/nukex12.0.bat | 13 +++++++++ .../launchers/windows/photoshop_2020.bat | 15 ++++++++++ .../launchers/windows/premiere_pro_2019.bat | 14 +++++++++ .../launchers/windows/premiere_pro_2020.bat | 13 +++++++++ .../defaults/launchers/windows/python3.bat | 13 +++++++++ .../defaults/launchers/windows/resolve_16.bat | 17 +++++++++++ .../defaults/launchers/windows/shell.bat | 2 ++ .../launchers/windows/storyboardpro_7.bat | 13 +++++++++ .../defaults/launchers/windows/unreal.bat | 11 +++++++ 107 files changed, 1177 insertions(+) create mode 100644 pype/settings/defaults/launchers/blender_2.80.toml create mode 100644 pype/settings/defaults/launchers/blender_2.81.toml create mode 100644 pype/settings/defaults/launchers/blender_2.82.toml create mode 100644 pype/settings/defaults/launchers/blender_2.83.toml create mode 100644 pype/settings/defaults/launchers/celaction_local.toml create mode 100644 pype/settings/defaults/launchers/celaction_publish.toml create mode 100644 pype/settings/defaults/launchers/darwin/blender_2.82 create mode 100644 pype/settings/defaults/launchers/darwin/harmony_17 create mode 100644 pype/settings/defaults/launchers/darwin/harmony_17_launch create mode 100644 pype/settings/defaults/launchers/darwin/python3 create mode 100644 pype/settings/defaults/launchers/harmony_17.toml create mode 100644 pype/settings/defaults/launchers/houdini_16.toml create mode 100644 pype/settings/defaults/launchers/houdini_17.toml create mode 100644 pype/settings/defaults/launchers/houdini_18.toml create mode 100644 pype/settings/defaults/launchers/linux/maya2016 create mode 100644 pype/settings/defaults/launchers/linux/maya2017 create mode 100644 pype/settings/defaults/launchers/linux/maya2018 create mode 100644 pype/settings/defaults/launchers/linux/maya2019 create mode 100644 pype/settings/defaults/launchers/linux/maya2020 create mode 100644 pype/settings/defaults/launchers/linux/nuke11.3 create mode 100644 pype/settings/defaults/launchers/linux/nuke12.0 create mode 100644 pype/settings/defaults/launchers/linux/nukestudio11.3 create mode 100644 pype/settings/defaults/launchers/linux/nukestudio12.0 create mode 100644 pype/settings/defaults/launchers/linux/nukex11.3 create mode 100644 pype/settings/defaults/launchers/linux/nukex12.0 create mode 100644 pype/settings/defaults/launchers/maya_2016.toml create mode 100644 pype/settings/defaults/launchers/maya_2017.toml create mode 100644 pype/settings/defaults/launchers/maya_2018.toml create mode 100644 pype/settings/defaults/launchers/maya_2019.toml create mode 100644 pype/settings/defaults/launchers/maya_2020.toml create mode 100644 pype/settings/defaults/launchers/mayabatch_2019.toml create mode 100644 pype/settings/defaults/launchers/mayabatch_2020.toml create mode 100644 pype/settings/defaults/launchers/mayapy2016.toml create mode 100644 pype/settings/defaults/launchers/mayapy2017.toml create mode 100644 pype/settings/defaults/launchers/mayapy2018.toml create mode 100644 pype/settings/defaults/launchers/mayapy2019.toml create mode 100644 pype/settings/defaults/launchers/mayapy2020.toml create mode 100644 pype/settings/defaults/launchers/myapp.toml create mode 100644 pype/settings/defaults/launchers/nuke_10.0.toml create mode 100644 pype/settings/defaults/launchers/nuke_11.0.toml create mode 100644 pype/settings/defaults/launchers/nuke_11.2.toml create mode 100644 pype/settings/defaults/launchers/nuke_11.3.toml create mode 100644 pype/settings/defaults/launchers/nuke_12.0.toml create mode 100644 pype/settings/defaults/launchers/nukestudio_10.0.toml create mode 100644 pype/settings/defaults/launchers/nukestudio_11.0.toml create mode 100644 pype/settings/defaults/launchers/nukestudio_11.2.toml create mode 100644 pype/settings/defaults/launchers/nukestudio_11.3.toml create mode 100644 pype/settings/defaults/launchers/nukestudio_12.0.toml create mode 100644 pype/settings/defaults/launchers/nukex_10.0.toml create mode 100644 pype/settings/defaults/launchers/nukex_11.0.toml create mode 100644 pype/settings/defaults/launchers/nukex_11.2.toml create mode 100644 pype/settings/defaults/launchers/nukex_11.3.toml create mode 100644 pype/settings/defaults/launchers/nukex_12.0.toml create mode 100644 pype/settings/defaults/launchers/photoshop_2020.toml create mode 100644 pype/settings/defaults/launchers/premiere_2019.toml create mode 100644 pype/settings/defaults/launchers/premiere_2020.toml create mode 100644 pype/settings/defaults/launchers/python_2.toml create mode 100644 pype/settings/defaults/launchers/python_3.toml create mode 100644 pype/settings/defaults/launchers/resolve_16.toml create mode 100644 pype/settings/defaults/launchers/shell.toml create mode 100644 pype/settings/defaults/launchers/storyboardpro_7.toml create mode 100644 pype/settings/defaults/launchers/unreal_4.24.toml create mode 100644 pype/settings/defaults/launchers/windows/blender_2.80.bat create mode 100644 pype/settings/defaults/launchers/windows/blender_2.81.bat create mode 100644 pype/settings/defaults/launchers/windows/blender_2.82.bat create mode 100644 pype/settings/defaults/launchers/windows/blender_2.83.bat create mode 100644 pype/settings/defaults/launchers/windows/celaction_local.bat create mode 100644 pype/settings/defaults/launchers/windows/celaction_publish.bat create mode 100644 pype/settings/defaults/launchers/windows/harmony_17.bat create mode 100644 pype/settings/defaults/launchers/windows/houdini_16.bat create mode 100644 pype/settings/defaults/launchers/windows/houdini_17.bat create mode 100644 pype/settings/defaults/launchers/windows/houdini_18.bat create mode 100644 pype/settings/defaults/launchers/windows/maya2016.bat create mode 100644 pype/settings/defaults/launchers/windows/maya2017.bat create mode 100644 pype/settings/defaults/launchers/windows/maya2018.bat create mode 100644 pype/settings/defaults/launchers/windows/maya2019.bat create mode 100644 pype/settings/defaults/launchers/windows/maya2020.bat create mode 100644 pype/settings/defaults/launchers/windows/mayabatch2019.bat create mode 100644 pype/settings/defaults/launchers/windows/mayabatch2020.bat create mode 100644 pype/settings/defaults/launchers/windows/mayapy2016.bat create mode 100644 pype/settings/defaults/launchers/windows/mayapy2017.bat create mode 100644 pype/settings/defaults/launchers/windows/mayapy2018.bat create mode 100644 pype/settings/defaults/launchers/windows/mayapy2019.bat create mode 100644 pype/settings/defaults/launchers/windows/mayapy2020.bat create mode 100644 pype/settings/defaults/launchers/windows/nuke10.0.bat create mode 100644 pype/settings/defaults/launchers/windows/nuke11.0.bat create mode 100644 pype/settings/defaults/launchers/windows/nuke11.2.bat create mode 100644 pype/settings/defaults/launchers/windows/nuke11.3.bat create mode 100644 pype/settings/defaults/launchers/windows/nuke12.0.bat create mode 100644 pype/settings/defaults/launchers/windows/nukestudio10.0.bat create mode 100644 pype/settings/defaults/launchers/windows/nukestudio11.0.bat create mode 100644 pype/settings/defaults/launchers/windows/nukestudio11.2.bat create mode 100644 pype/settings/defaults/launchers/windows/nukestudio11.3.bat create mode 100644 pype/settings/defaults/launchers/windows/nukestudio12.0.bat create mode 100644 pype/settings/defaults/launchers/windows/nukex10.0.bat create mode 100644 pype/settings/defaults/launchers/windows/nukex11.0.bat create mode 100644 pype/settings/defaults/launchers/windows/nukex11.2.bat create mode 100644 pype/settings/defaults/launchers/windows/nukex11.3.bat create mode 100644 pype/settings/defaults/launchers/windows/nukex12.0.bat create mode 100644 pype/settings/defaults/launchers/windows/photoshop_2020.bat create mode 100644 pype/settings/defaults/launchers/windows/premiere_pro_2019.bat create mode 100644 pype/settings/defaults/launchers/windows/premiere_pro_2020.bat create mode 100644 pype/settings/defaults/launchers/windows/python3.bat create mode 100644 pype/settings/defaults/launchers/windows/resolve_16.bat create mode 100644 pype/settings/defaults/launchers/windows/shell.bat create mode 100644 pype/settings/defaults/launchers/windows/storyboardpro_7.bat create mode 100644 pype/settings/defaults/launchers/windows/unreal.bat diff --git a/pype/settings/defaults/launchers/blender_2.80.toml b/pype/settings/defaults/launchers/blender_2.80.toml new file mode 100644 index 0000000000..88b5ea0c11 --- /dev/null +++ b/pype/settings/defaults/launchers/blender_2.80.toml @@ -0,0 +1,8 @@ +application_dir = "blender" +executable = "blender_2.80" +schema = "avalon-core:application-1.0" +label = "Blender" +label_variant = "2.80" +ftrack_label = "Blender" +icon = "app_icons/blender.png" +ftrack_icon = "{}/app_icons/blender.png" diff --git a/pype/settings/defaults/launchers/blender_2.81.toml b/pype/settings/defaults/launchers/blender_2.81.toml new file mode 100644 index 0000000000..072eaa8141 --- /dev/null +++ b/pype/settings/defaults/launchers/blender_2.81.toml @@ -0,0 +1,9 @@ +application_dir = "blender" +executable = "blender_2.81" +schema = "avalon-core:application-1.0" +label = "Blender" +label_variant = "2.81" +icon = "app_icons/blender.png" + +ftrack_label = "Blender" +ftrack_icon = '{}/app_icons/blender.png' diff --git a/pype/settings/defaults/launchers/blender_2.82.toml b/pype/settings/defaults/launchers/blender_2.82.toml new file mode 100644 index 0000000000..a485f790f1 --- /dev/null +++ b/pype/settings/defaults/launchers/blender_2.82.toml @@ -0,0 +1,9 @@ +application_dir = "blender" +executable = "blender_2.82" +schema = "avalon-core:application-1.0" +label = "Blender" +label_variant = "2.82" +icon = "app_icons/blender.png" + +ftrack_label = "Blender" +ftrack_icon = '{}/app_icons/blender.png' diff --git a/pype/settings/defaults/launchers/blender_2.83.toml b/pype/settings/defaults/launchers/blender_2.83.toml new file mode 100644 index 0000000000..0f98151d01 --- /dev/null +++ b/pype/settings/defaults/launchers/blender_2.83.toml @@ -0,0 +1,9 @@ +application_dir = "blender" +executable = "blender_2.83" +schema = "avalon-core:application-1.0" +label = "Blender" +label_variant = "2.83" +icon = "app_icons/blender.png" + +ftrack_label = "Blender" +ftrack_icon = '{}/app_icons/blender.png' diff --git a/pype/settings/defaults/launchers/celaction_local.toml b/pype/settings/defaults/launchers/celaction_local.toml new file mode 100644 index 0000000000..6cc5d4fa0e --- /dev/null +++ b/pype/settings/defaults/launchers/celaction_local.toml @@ -0,0 +1,9 @@ +executable = "celaction_local" +schema = "avalon-core:application-1.0" +application_dir = "celaction" +label = "CelAction2D" +icon = "app_icons/celaction_local.png" +launch_hook = "pype/hooks/celaction/prelaunch.py/CelactionPrelaunchHook" + +ftrack_label = "CelAction2D" +ftrack_icon = '{}/app_icons/celaction_local.png' diff --git a/pype/settings/defaults/launchers/celaction_publish.toml b/pype/settings/defaults/launchers/celaction_publish.toml new file mode 100644 index 0000000000..dc7ac82673 --- /dev/null +++ b/pype/settings/defaults/launchers/celaction_publish.toml @@ -0,0 +1,8 @@ +schema = "avalon-core:application-1.0" +application_dir = "shell" +executable = "celaction_publish" +label = "Celaction Shell" +icon = "app_icons/celaction.png" + +[environment] +CREATE_NEW_CONSOLE = "Yes" diff --git a/pype/settings/defaults/launchers/darwin/blender_2.82 b/pype/settings/defaults/launchers/darwin/blender_2.82 new file mode 100644 index 0000000000..8254411ea2 --- /dev/null +++ b/pype/settings/defaults/launchers/darwin/blender_2.82 @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +open -a blender $@ diff --git a/pype/settings/defaults/launchers/darwin/harmony_17 b/pype/settings/defaults/launchers/darwin/harmony_17 new file mode 100644 index 0000000000..b7eba2c2d0 --- /dev/null +++ b/pype/settings/defaults/launchers/darwin/harmony_17 @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +DIRNAME="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +set >~/environment.tmp +if [ $? -ne -0 ] ; then + echo "ERROR: cannot write to '~/environment.tmp'!" + read -n 1 -s -r -p "Press any key to exit" + return +fi +open -a Terminal.app "$DIRNAME/harmony_17_launch" diff --git a/pype/settings/defaults/launchers/darwin/harmony_17_launch b/pype/settings/defaults/launchers/darwin/harmony_17_launch new file mode 100644 index 0000000000..5dcf5db57e --- /dev/null +++ b/pype/settings/defaults/launchers/darwin/harmony_17_launch @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +source ~/environment.tmp +export $(cut -d= -f1 ~/environment.tmp) +exe="/Applications/Toon Boom Harmony 17 Premium/Harmony Premium.app/Contents/MacOS/Harmony Premium" +$PYPE_PYTHON_EXE -c "import avalon.harmony;avalon.harmony.launch('$exe')" diff --git a/pype/settings/defaults/launchers/darwin/python3 b/pype/settings/defaults/launchers/darwin/python3 new file mode 100644 index 0000000000..c2b82c7638 --- /dev/null +++ b/pype/settings/defaults/launchers/darwin/python3 @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +open /usr/bin/python3 --args $@ diff --git a/pype/settings/defaults/launchers/harmony_17.toml b/pype/settings/defaults/launchers/harmony_17.toml new file mode 100644 index 0000000000..dd1c929b1b --- /dev/null +++ b/pype/settings/defaults/launchers/harmony_17.toml @@ -0,0 +1,9 @@ +application_dir = "harmony" +label = "Harmony" +label_variant = "17" +ftrack_label = "Harmony" +schema = "avalon-core:application-1.0" +executable = "harmony_17" +description = "" +icon = "app_icons/harmony.png" +ftrack_icon = '{}/app_icons/harmony.png' diff --git a/pype/settings/defaults/launchers/houdini_16.toml b/pype/settings/defaults/launchers/houdini_16.toml new file mode 100644 index 0000000000..0a0876a264 --- /dev/null +++ b/pype/settings/defaults/launchers/houdini_16.toml @@ -0,0 +1,8 @@ +executable = "houdini_16" +schema = "avalon-core:application-1.0" +application_dir = "houdini" +label = "Houdini" +label_variant = "16" +ftrack_label = "Houdini" +icon = "app_icons/houdini.png" +ftrack_icon = '{}/app_icons/houdini.png' diff --git a/pype/settings/defaults/launchers/houdini_17.toml b/pype/settings/defaults/launchers/houdini_17.toml new file mode 100644 index 0000000000..203f5cdb9b --- /dev/null +++ b/pype/settings/defaults/launchers/houdini_17.toml @@ -0,0 +1,8 @@ +executable = "houdini_17" +schema = "avalon-core:application-1.0" +application_dir = "houdini" +label = "Houdini" +label_variant = "17" +ftrack_label = "Houdini" +icon = "app_icons/houdini.png" +ftrack_icon = '{}/app_icons/houdini.png' diff --git a/pype/settings/defaults/launchers/houdini_18.toml b/pype/settings/defaults/launchers/houdini_18.toml new file mode 100644 index 0000000000..40f530c291 --- /dev/null +++ b/pype/settings/defaults/launchers/houdini_18.toml @@ -0,0 +1,8 @@ +executable = "houdini_18" +schema = "avalon-core:application-1.0" +application_dir = "houdini" +label = "Houdini" +label_variant = "18" +ftrack_label = "Houdini" +icon = "app_icons/houdini.png" +ftrack_icon = '{}/app_icons/houdini.png' diff --git a/pype/settings/defaults/launchers/linux/maya2016 b/pype/settings/defaults/launchers/linux/maya2016 new file mode 100644 index 0000000000..98424304b1 --- /dev/null +++ b/pype/settings/defaults/launchers/linux/maya2016 @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +maya_path = "/usr/autodesk/maya2016/bin/maya" + +if [[ -z $PYPE_LOG_NO_COLORS ]]; then + $maya_path -file "$AVALON_LAST_WORKFILE" $@ +else + $maya_path $@ diff --git a/pype/settings/defaults/launchers/linux/maya2017 b/pype/settings/defaults/launchers/linux/maya2017 new file mode 100644 index 0000000000..7a2662a55e --- /dev/null +++ b/pype/settings/defaults/launchers/linux/maya2017 @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +maya_path = "/usr/autodesk/maya2017/bin/maya" + +if [[ -z $AVALON_LAST_WORKFILE ]]; then + $maya_path -file "$AVALON_LAST_WORKFILE" $@ +else + $maya_path $@ diff --git a/pype/settings/defaults/launchers/linux/maya2018 b/pype/settings/defaults/launchers/linux/maya2018 new file mode 100644 index 0000000000..db832b3fe7 --- /dev/null +++ b/pype/settings/defaults/launchers/linux/maya2018 @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +maya_path = "/usr/autodesk/maya2018/bin/maya" + +if [[ -z $AVALON_LAST_WORKFILE ]]; then + $maya_path -file "$AVALON_LAST_WORKFILE" $@ +else + $maya_path $@ diff --git a/pype/settings/defaults/launchers/linux/maya2019 b/pype/settings/defaults/launchers/linux/maya2019 new file mode 100644 index 0000000000..8398734ab9 --- /dev/null +++ b/pype/settings/defaults/launchers/linux/maya2019 @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +maya_path = "/usr/autodesk/maya2019/bin/maya" + +if [[ -z $AVALON_LAST_WORKFILE ]]; then + $maya_path -file "$AVALON_LAST_WORKFILE" $@ +else + $maya_path $@ diff --git a/pype/settings/defaults/launchers/linux/maya2020 b/pype/settings/defaults/launchers/linux/maya2020 new file mode 100644 index 0000000000..18a1edd598 --- /dev/null +++ b/pype/settings/defaults/launchers/linux/maya2020 @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +maya_path = "/usr/autodesk/maya2020/bin/maya" + +if [[ -z $AVALON_LAST_WORKFILE ]]; then + $maya_path -file "$AVALON_LAST_WORKFILE" $@ +else + $maya_path $@ diff --git a/pype/settings/defaults/launchers/linux/nuke11.3 b/pype/settings/defaults/launchers/linux/nuke11.3 new file mode 100644 index 0000000000..b1c9a90d74 --- /dev/null +++ b/pype/settings/defaults/launchers/linux/nuke11.3 @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +gnome-terminal -e '/usr/local/Nuke11.3v5/Nuke11.3' diff --git a/pype/settings/defaults/launchers/linux/nuke12.0 b/pype/settings/defaults/launchers/linux/nuke12.0 new file mode 100644 index 0000000000..99ea1a6b0c --- /dev/null +++ b/pype/settings/defaults/launchers/linux/nuke12.0 @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +gnome-terminal -e '/usr/local/Nuke12.0v1/Nuke12.0' diff --git a/pype/settings/defaults/launchers/linux/nukestudio11.3 b/pype/settings/defaults/launchers/linux/nukestudio11.3 new file mode 100644 index 0000000000..750d54a7d5 --- /dev/null +++ b/pype/settings/defaults/launchers/linux/nukestudio11.3 @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +gnome-terminal -e '/usr/local/Nuke11.3v5/Nuke11.3 --studio' diff --git a/pype/settings/defaults/launchers/linux/nukestudio12.0 b/pype/settings/defaults/launchers/linux/nukestudio12.0 new file mode 100644 index 0000000000..ba5cf654a8 --- /dev/null +++ b/pype/settings/defaults/launchers/linux/nukestudio12.0 @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +gnome-terminal -e '/usr/local/Nuke12.0v1/Nuke12.0 --studio' diff --git a/pype/settings/defaults/launchers/linux/nukex11.3 b/pype/settings/defaults/launchers/linux/nukex11.3 new file mode 100644 index 0000000000..d913e4b961 --- /dev/null +++ b/pype/settings/defaults/launchers/linux/nukex11.3 @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +gnome-terminal -e '/usr/local/Nuke11.3v5/Nuke11.3 -nukex' diff --git a/pype/settings/defaults/launchers/linux/nukex12.0 b/pype/settings/defaults/launchers/linux/nukex12.0 new file mode 100644 index 0000000000..da2721c48b --- /dev/null +++ b/pype/settings/defaults/launchers/linux/nukex12.0 @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +gnome-terminal -e '/usr/local/Nuke12.0v1/Nuke12.0 -nukex' diff --git a/pype/settings/defaults/launchers/maya_2016.toml b/pype/settings/defaults/launchers/maya_2016.toml new file mode 100644 index 0000000000..24a463d9c6 --- /dev/null +++ b/pype/settings/defaults/launchers/maya_2016.toml @@ -0,0 +1,27 @@ +application_dir = "maya" +default_dirs = [ + "scenes", + "data", + "renderData/shaders", + "images" +] +label = "Autodesk Maya" +label_variant = "2016" +ftrack_label = "Maya" +schema = "avalon-core:application-1.0" +executable = "maya2016" +description = "" +icon = "app_icons/maya.png" +ftrack_icon = '{}/app_icons/maya.png' + +[copy] +"{PYPE_MODULE_ROOT}/pype/resources/maya/workspace.mel" = "workspace.mel" + +[environment] +MAYA_DISABLE_CLIC_IPM = "Yes" # Disable the AdSSO process +MAYA_DISABLE_CIP = "Yes" # Shorten time to boot +MAYA_DISABLE_CER = "Yes" +PYTHONPATH = [ + "{AVALON_CORE}/setup/maya", + "{PYTHONPATH}" +] diff --git a/pype/settings/defaults/launchers/maya_2017.toml b/pype/settings/defaults/launchers/maya_2017.toml new file mode 100644 index 0000000000..5295862e87 --- /dev/null +++ b/pype/settings/defaults/launchers/maya_2017.toml @@ -0,0 +1,29 @@ +application_dir = "maya" +default_dirs = [ + "scenes", + "data", + "renderData/shaders", + "images" +] +label = "Autodesk Maya" +label_variant = "2017" +ftrack_label = "Maya" +schema = "avalon-core:application-1.0" +executable = "maya2017" +description = "" +icon = "app_icons/maya.png" +ftrack_icon = '{}/app_icons/maya.png' + +[copy] +"{PYPE_MODULE_ROOT}/pype/resources/maya/workspace.mel" = "workspace.mel" + +[environment] +MAYA_DISABLE_CLIC_IPM = "Yes" # Disable the AdSSO process +MAYA_DISABLE_CIP = "Yes" # Shorten time to boot +MAYA_DISABLE_CER = "Yes" +PYMEL_SKIP_MEL_INIT = "Yes" +LC_ALL= "C" # Mute color management warnings +PYTHONPATH = [ + "{AVALON_CORE}/setup/maya", + "{PYTHONPATH}" +] diff --git a/pype/settings/defaults/launchers/maya_2018.toml b/pype/settings/defaults/launchers/maya_2018.toml new file mode 100644 index 0000000000..2bdff2094d --- /dev/null +++ b/pype/settings/defaults/launchers/maya_2018.toml @@ -0,0 +1,15 @@ +application_dir = "maya" +default_dirs = [ + "renders" +] +label = "Autodesk Maya" +label_variant = "2018" +ftrack_label = "Maya" +schema = "avalon-core:application-1.0" +executable = "maya2018" +description = "" +icon = "app_icons/maya.png" +ftrack_icon = '{}/app_icons/maya.png' + +[copy] +"{PYPE_MODULE_ROOT}/pype/resources/maya/workspace.mel" = "workspace.mel" diff --git a/pype/settings/defaults/launchers/maya_2019.toml b/pype/settings/defaults/launchers/maya_2019.toml new file mode 100644 index 0000000000..8eb88179f9 --- /dev/null +++ b/pype/settings/defaults/launchers/maya_2019.toml @@ -0,0 +1,15 @@ +application_dir = "maya" +default_dirs = [ + "renders" +] +label = "Autodesk Maya" +label_variant = "2019" +ftrack_label = "Maya" +schema = "avalon-core:application-1.0" +executable = "maya2019" +description = "" +icon = "app_icons/maya.png" +ftrack_icon = '{}/app_icons/maya.png' + +[copy] +"{PYPE_MODULE_ROOT}/pype/resources/maya/workspace.mel" = "workspace.mel" diff --git a/pype/settings/defaults/launchers/maya_2020.toml b/pype/settings/defaults/launchers/maya_2020.toml new file mode 100644 index 0000000000..693de0cf9e --- /dev/null +++ b/pype/settings/defaults/launchers/maya_2020.toml @@ -0,0 +1,15 @@ +application_dir = "maya" +default_dirs = [ + "renders" +] +label = "Autodesk Maya" +label_variant = "2020" +ftrack_label = "Maya" +schema = "avalon-core:application-1.0" +executable = "maya2020" +description = "" +icon = "app_icons/maya.png" +ftrack_icon = '{}/app_icons/maya.png' + +[copy] +"{PYPE_MODULE_ROOT}/pype/resources/maya/workspace.mel" = "workspace.mel" diff --git a/pype/settings/defaults/launchers/mayabatch_2019.toml b/pype/settings/defaults/launchers/mayabatch_2019.toml new file mode 100644 index 0000000000..a928618d2b --- /dev/null +++ b/pype/settings/defaults/launchers/mayabatch_2019.toml @@ -0,0 +1,17 @@ +application_dir = "maya" +default_dirs = [ + "scenes", + "data", + "renderData/shaders", + "images" +] +label = "Autodesk Maya 2019x64" +schema = "avalon-core:application-1.0" +executable = "mayabatch2019" +description = "" + +[environment] +PYTHONPATH = [ + "{AVALON_CORE}/setup/maya", + "{PYTHONPATH}" +] diff --git a/pype/settings/defaults/launchers/mayabatch_2020.toml b/pype/settings/defaults/launchers/mayabatch_2020.toml new file mode 100644 index 0000000000..cd1e1e4474 --- /dev/null +++ b/pype/settings/defaults/launchers/mayabatch_2020.toml @@ -0,0 +1,17 @@ +application_dir = "maya" +default_dirs = [ + "scenes", + "data", + "renderData/shaders", + "images" +] +label = "Autodesk Maya 2020x64" +schema = "avalon-core:application-1.0" +executable = "mayabatch2020" +description = "" + +[environment] +PYTHONPATH = [ + "{AVALON_CORE}/setup/maya", + "{PYTHONPATH}" +] diff --git a/pype/settings/defaults/launchers/mayapy2016.toml b/pype/settings/defaults/launchers/mayapy2016.toml new file mode 100644 index 0000000000..ad1e3dee86 --- /dev/null +++ b/pype/settings/defaults/launchers/mayapy2016.toml @@ -0,0 +1,17 @@ +application_dir = "maya" +default_dirs = [ + "scenes", + "data", + "renderData/shaders", + "images" +] +label = "Autodesk Maya 2016x64" +schema = "avalon-core:application-1.0" +executable = "mayapy2016" +description = "" + +[environment] +PYTHONPATH = [ + "{AVALON_CORE}/setup/maya", + "{PYTHONPATH}" +] diff --git a/pype/settings/defaults/launchers/mayapy2017.toml b/pype/settings/defaults/launchers/mayapy2017.toml new file mode 100644 index 0000000000..8d2095ff47 --- /dev/null +++ b/pype/settings/defaults/launchers/mayapy2017.toml @@ -0,0 +1,17 @@ +application_dir = "maya" +default_dirs = [ + "scenes", + "data", + "renderData/shaders", + "images" +] +label = "Autodesk Maya 2017x64" +schema = "avalon-core:application-1.0" +executable = "mayapy2017" +description = "" + +[environment] +PYTHONPATH = [ + "{AVALON_CORE}/setup/maya", + "{PYTHONPATH}" +] diff --git a/pype/settings/defaults/launchers/mayapy2018.toml b/pype/settings/defaults/launchers/mayapy2018.toml new file mode 100644 index 0000000000..597744fd85 --- /dev/null +++ b/pype/settings/defaults/launchers/mayapy2018.toml @@ -0,0 +1,17 @@ +application_dir = "maya" +default_dirs = [ + "scenes", + "data", + "renderData/shaders", + "images" +] +label = "Autodesk Maya 2018x64" +schema = "avalon-core:application-1.0" +executable = "mayapy2017" +description = "" + +[environment] +PYTHONPATH = [ + "{AVALON_CORE}/setup/maya", + "{PYTHONPATH}" +] diff --git a/pype/settings/defaults/launchers/mayapy2019.toml b/pype/settings/defaults/launchers/mayapy2019.toml new file mode 100644 index 0000000000..3c8a9860f9 --- /dev/null +++ b/pype/settings/defaults/launchers/mayapy2019.toml @@ -0,0 +1,17 @@ +application_dir = "maya" +default_dirs = [ + "scenes", + "data", + "renderData/shaders", + "images" +] +label = "Autodesk Maya 2019x64" +schema = "avalon-core:application-1.0" +executable = "mayapy2019" +description = "" + +[environment] +PYTHONPATH = [ + "{AVALON_CORE}/setup/maya", + "{PYTHONPATH}" +] diff --git a/pype/settings/defaults/launchers/mayapy2020.toml b/pype/settings/defaults/launchers/mayapy2020.toml new file mode 100644 index 0000000000..8f2d2e4a67 --- /dev/null +++ b/pype/settings/defaults/launchers/mayapy2020.toml @@ -0,0 +1,17 @@ +application_dir = "maya" +default_dirs = [ + "scenes", + "data", + "renderData/shaders", + "images" +] +label = "Autodesk Maya 2020x64" +schema = "avalon-core:application-1.0" +executable = "mayapy2020" +description = "" + +[environment] +PYTHONPATH = [ + "{AVALON_CORE}/setup/maya", + "{PYTHONPATH}" +] diff --git a/pype/settings/defaults/launchers/myapp.toml b/pype/settings/defaults/launchers/myapp.toml new file mode 100644 index 0000000000..21da0d52b2 --- /dev/null +++ b/pype/settings/defaults/launchers/myapp.toml @@ -0,0 +1,5 @@ +executable = "python" +schema = "avalon-core:application-1.0" +application_dir = "myapp" +label = "My App" +arguments = [ "-c", "import sys; from Qt import QtWidgets; if __name__ == '__main__':;\n app = QtWidgets.QApplication(sys.argv);\n window = QtWidgets.QWidget();\n window.setWindowTitle(\"My App\");\n window.resize(400, 300);\n window.show();\n app.exec_();\n",] \ No newline at end of file diff --git a/pype/settings/defaults/launchers/nuke_10.0.toml b/pype/settings/defaults/launchers/nuke_10.0.toml new file mode 100644 index 0000000000..d4dd028942 --- /dev/null +++ b/pype/settings/defaults/launchers/nuke_10.0.toml @@ -0,0 +1,8 @@ +executable = "nuke10.0" +schema = "avalon-core:application-1.0" +application_dir = "nuke" +label = "Nuke" +label_variant = "10.0v4" +ftrack_label = "Nuke" +icon = "app_icons/nuke.png" +ftrack_icon = '{}/app_icons/nuke.png' diff --git a/pype/settings/defaults/launchers/nuke_11.0.toml b/pype/settings/defaults/launchers/nuke_11.0.toml new file mode 100644 index 0000000000..10ff6aca37 --- /dev/null +++ b/pype/settings/defaults/launchers/nuke_11.0.toml @@ -0,0 +1,8 @@ +executable = "nuke11.0" +schema = "avalon-core:application-1.0" +application_dir = "nuke" +label = "Nuke" +label_variant = "11.0" +ftrack_label = "Nuke" +icon = "app_icons/nuke.png" +ftrack_icon = '{}/app_icons/nuke.png' diff --git a/pype/settings/defaults/launchers/nuke_11.2.toml b/pype/settings/defaults/launchers/nuke_11.2.toml new file mode 100644 index 0000000000..530c7f610e --- /dev/null +++ b/pype/settings/defaults/launchers/nuke_11.2.toml @@ -0,0 +1,8 @@ +executable = "nuke11.2" +schema = "avalon-core:application-1.0" +application_dir = "nuke" +label = "Nuke" +label_variant = "11.2" +ftrack_label = "Nuke" +icon = "app_icons/nuke.png" +ftrack_icon = '{}/app_icons/nuke.png' diff --git a/pype/settings/defaults/launchers/nuke_11.3.toml b/pype/settings/defaults/launchers/nuke_11.3.toml new file mode 100644 index 0000000000..c9ff005feb --- /dev/null +++ b/pype/settings/defaults/launchers/nuke_11.3.toml @@ -0,0 +1,8 @@ +executable = "nuke11.3" +schema = "avalon-core:application-1.0" +application_dir = "nuke" +label = "Nuke" +label_variant = "11.3" +ftrack_label = "Nuke" +icon = "app_icons/nuke.png" +ftrack_icon = '{}/app_icons/nuke.png' diff --git a/pype/settings/defaults/launchers/nuke_12.0.toml b/pype/settings/defaults/launchers/nuke_12.0.toml new file mode 100644 index 0000000000..9ac1084fbf --- /dev/null +++ b/pype/settings/defaults/launchers/nuke_12.0.toml @@ -0,0 +1,8 @@ +executable = "nuke12.0" +schema = "avalon-core:application-1.0" +application_dir = "nuke" +label = "Nuke" +label_variant = "12.0" +ftrack_label = "Nuke" +icon = "app_icons/nuke.png" +ftrack_icon = '{}/app_icons/nuke.png' diff --git a/pype/settings/defaults/launchers/nukestudio_10.0.toml b/pype/settings/defaults/launchers/nukestudio_10.0.toml new file mode 100644 index 0000000000..6c554aff62 --- /dev/null +++ b/pype/settings/defaults/launchers/nukestudio_10.0.toml @@ -0,0 +1,8 @@ +executable = "nukestudio10.0" +schema = "avalon-core:application-1.0" +application_dir = "nukestudio" +label = "NukeStudio" +label_variant = "10.0" +ftrack_label = "NukeStudio" +icon = "app_icons/nuke.png" +ftrack_icon = '{}/app_icons/nuke.png' diff --git a/pype/settings/defaults/launchers/nukestudio_11.0.toml b/pype/settings/defaults/launchers/nukestudio_11.0.toml new file mode 100644 index 0000000000..482aa6587e --- /dev/null +++ b/pype/settings/defaults/launchers/nukestudio_11.0.toml @@ -0,0 +1,8 @@ +executable = "nukestudio11.0" +schema = "avalon-core:application-1.0" +application_dir = "nukestudio" +label = "NukeStudio" +label_variant = "11.0" +ftrack_label = "NukeStudio" +icon = "app_icons/nuke.png" +ftrack_icon = '{}/app_icons/nuke.png' diff --git a/pype/settings/defaults/launchers/nukestudio_11.2.toml b/pype/settings/defaults/launchers/nukestudio_11.2.toml new file mode 100644 index 0000000000..78d1de3d8b --- /dev/null +++ b/pype/settings/defaults/launchers/nukestudio_11.2.toml @@ -0,0 +1,8 @@ +executable = "nukestudio11.2" +schema = "avalon-core:application-1.0" +application_dir = "nukestudio" +label = "NukeStudio" +label_variant = "11.2" +ftrack_label = "NukeStudio" +icon = "app_icons/nuke.png" +ftrack_icon = '{}/app_icons/nuke.png' diff --git a/pype/settings/defaults/launchers/nukestudio_11.3.toml b/pype/settings/defaults/launchers/nukestudio_11.3.toml new file mode 100644 index 0000000000..35c6a08b2f --- /dev/null +++ b/pype/settings/defaults/launchers/nukestudio_11.3.toml @@ -0,0 +1,8 @@ +executable = "nukestudio11.3" +schema = "avalon-core:application-1.0" +application_dir = "nukestudio" +label = "NukeStudio" +label_variant = "11.3" +ftrack_label = "NukeStudio" +icon = "app_icons/nuke.png" +ftrack_icon = '{}/app_icons/nuke.png' diff --git a/pype/settings/defaults/launchers/nukestudio_12.0.toml b/pype/settings/defaults/launchers/nukestudio_12.0.toml new file mode 100644 index 0000000000..2754116aef --- /dev/null +++ b/pype/settings/defaults/launchers/nukestudio_12.0.toml @@ -0,0 +1,8 @@ +executable = "nukestudio12.0" +schema = "avalon-core:application-1.0" +application_dir = "nukestudio" +label = "NukeStudio" +label_variant = "12.0" +ftrack_label = "NukeStudio" +icon = "app_icons/nuke.png" +ftrack_icon = '{}/app_icons/nuke.png' diff --git a/pype/settings/defaults/launchers/nukex_10.0.toml b/pype/settings/defaults/launchers/nukex_10.0.toml new file mode 100644 index 0000000000..48da30fe16 --- /dev/null +++ b/pype/settings/defaults/launchers/nukex_10.0.toml @@ -0,0 +1,8 @@ +executable = "nukex10.0" +schema = "avalon-core:application-1.0" +application_dir = "nuke" +label = "NukeX" +label_variant = "10.0" +ftrack_label = "NukeX" +icon = "app_icons/nuke.png" +ftrack_icon = '{}/app_icons/nukex.png' diff --git a/pype/settings/defaults/launchers/nukex_11.0.toml b/pype/settings/defaults/launchers/nukex_11.0.toml new file mode 100644 index 0000000000..8f353e9e00 --- /dev/null +++ b/pype/settings/defaults/launchers/nukex_11.0.toml @@ -0,0 +1,8 @@ +executable = "nukex11.0" +schema = "avalon-core:application-1.0" +application_dir = "nuke" +label = "NukeX" +label_variant = "11.0" +ftrack_label = "NukeX" +icon = "app_icons/nuke.png" +ftrack_icon = '{}/app_icons/nukex.png' diff --git a/pype/settings/defaults/launchers/nukex_11.2.toml b/pype/settings/defaults/launchers/nukex_11.2.toml new file mode 100644 index 0000000000..38e37fa4c9 --- /dev/null +++ b/pype/settings/defaults/launchers/nukex_11.2.toml @@ -0,0 +1,8 @@ +executable = "nukex11.2" +schema = "avalon-core:application-1.0" +application_dir = "nuke" +label = "NukeX" +label_variant = "11.2" +ftrack_label = "NukeX" +icon = "app_icons/nuke.png" +ftrack_icon = '{}/app_icons/nukex.png' diff --git a/pype/settings/defaults/launchers/nukex_11.3.toml b/pype/settings/defaults/launchers/nukex_11.3.toml new file mode 100644 index 0000000000..42969c5e69 --- /dev/null +++ b/pype/settings/defaults/launchers/nukex_11.3.toml @@ -0,0 +1,8 @@ +executable = "nukex11.3" +schema = "avalon-core:application-1.0" +application_dir = "nuke" +label = "NukeX" +label_variant = "11.3" +ftrack_label = "NukeX" +icon = "app_icons/nuke.png" +ftrack_icon = '{}/app_icons/nukex.png' diff --git a/pype/settings/defaults/launchers/nukex_12.0.toml b/pype/settings/defaults/launchers/nukex_12.0.toml new file mode 100644 index 0000000000..19d27a12d7 --- /dev/null +++ b/pype/settings/defaults/launchers/nukex_12.0.toml @@ -0,0 +1,8 @@ +executable = "nukex12.0" +schema = "avalon-core:application-1.0" +application_dir = "nuke" +label = "NukeX" +label_variant = "12.0" +ftrack_label = "NukeX" +icon = "app_icons/nuke.png" +ftrack_icon = '{}/app_icons/nukex.png' diff --git a/pype/settings/defaults/launchers/photoshop_2020.toml b/pype/settings/defaults/launchers/photoshop_2020.toml new file mode 100644 index 0000000000..8164af929f --- /dev/null +++ b/pype/settings/defaults/launchers/photoshop_2020.toml @@ -0,0 +1,9 @@ +executable = "photoshop_2020" +schema = "avalon-core:application-1.0" +application_dir = "photoshop" +label = "Adobe Photoshop" +label_variant = "2020" +icon = "app_icons/photoshop.png" +ftrack_label = "Photoshop" +ftrack_icon = '{}/app_icons/photoshop.png' +launch_hook = "pype/hooks/photoshop/prelaunch.py/PhotoshopPrelaunch" diff --git a/pype/settings/defaults/launchers/premiere_2019.toml b/pype/settings/defaults/launchers/premiere_2019.toml new file mode 100644 index 0000000000..d03395e022 --- /dev/null +++ b/pype/settings/defaults/launchers/premiere_2019.toml @@ -0,0 +1,9 @@ +executable = "premiere_pro_2019" +schema = "avalon-core:application-1.0" +application_dir = "premiere" +label = "Adobe Premiere Pro CC" +label_variant = "2019" +icon = "app_icons/premiere.png" + +ftrack_label = "Premiere" +ftrack_icon = '{}/app_icons/premiere.png' diff --git a/pype/settings/defaults/launchers/premiere_2020.toml b/pype/settings/defaults/launchers/premiere_2020.toml new file mode 100644 index 0000000000..01c7b5b745 --- /dev/null +++ b/pype/settings/defaults/launchers/premiere_2020.toml @@ -0,0 +1,10 @@ +executable = "premiere_pro_2020" +schema = "avalon-core:application-1.0" +application_dir = "premiere" +label = "Adobe Premiere Pro CC" +label_variant = "2020" +launch_hook = "pype/hooks/premiere/prelaunch.py/PremierePrelaunch" +icon = "app_icons/premiere.png" + +ftrack_label = "Premiere" +ftrack_icon = '{}/app_icons/premiere.png' diff --git a/pype/settings/defaults/launchers/python_2.toml b/pype/settings/defaults/launchers/python_2.toml new file mode 100644 index 0000000000..f1c1ca7e68 --- /dev/null +++ b/pype/settings/defaults/launchers/python_2.toml @@ -0,0 +1,12 @@ +schema = "avalon-core:application-1.0" +application_dir = "python" +executable = "python" +label = "Python" +label_variant = "2" +icon = "app_icons/python.png" + +ftrack_label = "Python" +ftrack_icon = '{}/app_icons/python.png' + +[environment] +CREATE_NEW_CONSOLE = "Yes" diff --git a/pype/settings/defaults/launchers/python_3.toml b/pype/settings/defaults/launchers/python_3.toml new file mode 100644 index 0000000000..90fb10eaeb --- /dev/null +++ b/pype/settings/defaults/launchers/python_3.toml @@ -0,0 +1,12 @@ +schema = "avalon-core:application-1.0" +application_dir = "python" +executable = "python3" +label = "Python" +label_variant = "3" +icon = "app_icons/python.png" + +ftrack_label = "Python" +ftrack_icon = '{}/app_icons/python.png' + +[environment] +CREATE_NEW_CONSOLE = "Yes" diff --git a/pype/settings/defaults/launchers/resolve_16.toml b/pype/settings/defaults/launchers/resolve_16.toml new file mode 100644 index 0000000000..47918a22a6 --- /dev/null +++ b/pype/settings/defaults/launchers/resolve_16.toml @@ -0,0 +1,10 @@ +executable = "resolve_16" +schema = "avalon-core:application-1.0" +application_dir = "resolve" +label = "BM DaVinci Resolve" +label_variant = "16" +launch_hook = "pype/hooks/resolve/prelaunch.py/ResolvePrelaunch" +icon = "app_icons/resolve.png" + +ftrack_label = "BM DaVinci Resolve" +ftrack_icon = '{}/app_icons/resolve.png' diff --git a/pype/settings/defaults/launchers/shell.toml b/pype/settings/defaults/launchers/shell.toml new file mode 100644 index 0000000000..959ad392ea --- /dev/null +++ b/pype/settings/defaults/launchers/shell.toml @@ -0,0 +1,7 @@ +schema = "avalon-core:application-1.0" +application_dir = "shell" +executable = "shell" +label = "Shell" + +[environment] +CREATE_NEW_CONSOLE = "Yes" \ No newline at end of file diff --git a/pype/settings/defaults/launchers/storyboardpro_7.toml b/pype/settings/defaults/launchers/storyboardpro_7.toml new file mode 100644 index 0000000000..067f10a23a --- /dev/null +++ b/pype/settings/defaults/launchers/storyboardpro_7.toml @@ -0,0 +1,9 @@ +application_dir = "storyboardpro" +label = "Storyboard Pro" +label_variant = "7" +ftrack_label = "Storyboard Pro" +schema = "avalon-core:application-1.0" +executable = "storyboardpro_7" +description = "" +icon = "app_icons/storyboardpro.png" +ftrack_icon = '{}/app_icons/storyboardpro.png' diff --git a/pype/settings/defaults/launchers/unreal_4.24.toml b/pype/settings/defaults/launchers/unreal_4.24.toml new file mode 100644 index 0000000000..10b14e7f59 --- /dev/null +++ b/pype/settings/defaults/launchers/unreal_4.24.toml @@ -0,0 +1,10 @@ +executable = "unreal" +schema = "avalon-core:application-1.0" +application_dir = "unreal" +label = "Unreal Editor" +label_variant = "4.24" +icon = "app_icons/ue4.png" +launch_hook = "pype/hooks/unreal/unreal_prelaunch.py/UnrealPrelaunch" + +ftrack_label = "UnrealEditor" +ftrack_icon = '{}/app_icons/ue4.png' diff --git a/pype/settings/defaults/launchers/windows/blender_2.80.bat b/pype/settings/defaults/launchers/windows/blender_2.80.bat new file mode 100644 index 0000000000..5b8a37356b --- /dev/null +++ b/pype/settings/defaults/launchers/windows/blender_2.80.bat @@ -0,0 +1,11 @@ +set __app__="Blender" +set __exe__="C:\Program Files\Blender Foundation\Blender 2.80\blender.exe" +if not exist %__exe__% goto :missing_app + +start %__app__% %__exe__% %* + +goto :eof + +:missing_app + echo ERROR: %__app__% not found in %__exe__% + exit /B 1 diff --git a/pype/settings/defaults/launchers/windows/blender_2.81.bat b/pype/settings/defaults/launchers/windows/blender_2.81.bat new file mode 100644 index 0000000000..a900b18eda --- /dev/null +++ b/pype/settings/defaults/launchers/windows/blender_2.81.bat @@ -0,0 +1,11 @@ +set __app__="Blender" +set __exe__="C:\Program Files\Blender Foundation\Blender 2.81\blender.exe" +if not exist %__exe__% goto :missing_app + +start %__app__% %__exe__% %* + +goto :eof + +:missing_app + echo ERROR: %__app__% not found in %__exe__% + exit /B 1 diff --git a/pype/settings/defaults/launchers/windows/blender_2.82.bat b/pype/settings/defaults/launchers/windows/blender_2.82.bat new file mode 100644 index 0000000000..7105c1efe1 --- /dev/null +++ b/pype/settings/defaults/launchers/windows/blender_2.82.bat @@ -0,0 +1,11 @@ +set __app__="Blender" +set __exe__="C:\Program Files\Blender Foundation\Blender 2.82\blender.exe" --python-use-system-env +if not exist %__exe__% goto :missing_app + +start %__app__% %__exe__% %* + +goto :eof + +:missing_app + echo ERROR: %__app__% not found in %__exe__% + exit /B 1 diff --git a/pype/settings/defaults/launchers/windows/blender_2.83.bat b/pype/settings/defaults/launchers/windows/blender_2.83.bat new file mode 100644 index 0000000000..671952f0d7 --- /dev/null +++ b/pype/settings/defaults/launchers/windows/blender_2.83.bat @@ -0,0 +1,11 @@ +set __app__="Blender" +set __exe__="C:\Program Files\Blender Foundation\Blender 2.83\blender.exe" --python-use-system-env +if not exist %__exe__% goto :missing_app + +start %__app__% %__exe__% %* + +goto :eof + +:missing_app + echo ERROR: %__app__% not found in %__exe__% + exit /B 1 diff --git a/pype/settings/defaults/launchers/windows/celaction_local.bat b/pype/settings/defaults/launchers/windows/celaction_local.bat new file mode 100644 index 0000000000..8f2171617e --- /dev/null +++ b/pype/settings/defaults/launchers/windows/celaction_local.bat @@ -0,0 +1,19 @@ +set __app__="CelAction2D" +set __app_dir__="C:\Program Files (x86)\CelAction\" +set __exe__="C:\Program Files (x86)\CelAction\CelAction2D.exe" + +if not exist %__exe__% goto :missing_app + +pushd %__app_dir__% + +if "%PYPE_CELACTION_PROJECT_FILE%"=="" ( + start %__app__% %__exe__% %* +) else ( + start %__app__% %__exe__% "%PYPE_CELACTION_PROJECT_FILE%" %* +) + +goto :eof + +:missing_app + echo ERROR: %__app__% not found in %__exe__% + exit /B 1 diff --git a/pype/settings/defaults/launchers/windows/celaction_publish.bat b/pype/settings/defaults/launchers/windows/celaction_publish.bat new file mode 100644 index 0000000000..77ec2ac24e --- /dev/null +++ b/pype/settings/defaults/launchers/windows/celaction_publish.bat @@ -0,0 +1,3 @@ +echo %* + +%PYPE_PYTHON_EXE% "%PYPE_MODULE_ROOT%\pype\hosts\celaction\cli.py" %* diff --git a/pype/settings/defaults/launchers/windows/harmony_17.bat b/pype/settings/defaults/launchers/windows/harmony_17.bat new file mode 100644 index 0000000000..0822650875 --- /dev/null +++ b/pype/settings/defaults/launchers/windows/harmony_17.bat @@ -0,0 +1,13 @@ +@echo off + +set __app__="Harmony 17" +set __exe__="C:/Program Files (x86)/Toon Boom Animation/Toon Boom Harmony 17 Premium/win64/bin/HarmonyPremium.exe" +if not exist %__exe__% goto :missing_app + +start %__app__% cmd.exe /k "python -c ^"import avalon.harmony;avalon.harmony.launch("%__exe__%")^"" + +goto :eof + +:missing_app + echo ERROR: %__app__% not found in %__exe__% + exit /B 1 diff --git a/pype/settings/defaults/launchers/windows/houdini_16.bat b/pype/settings/defaults/launchers/windows/houdini_16.bat new file mode 100644 index 0000000000..018ba08b4c --- /dev/null +++ b/pype/settings/defaults/launchers/windows/houdini_16.bat @@ -0,0 +1,13 @@ +@echo off + +set __app__="Houdini 16.0" +set __exe__="C:\Program Files\Side Effects Software\Houdini 16.0.621\bin\houdini.exe" +if not exist %__exe__% goto :missing_app + +start %__app__% %__exe__% %* + +goto :eof + +:missing_app + echo ERROR: %__app__% not found in %__exe__% + exit /B 1 diff --git a/pype/settings/defaults/launchers/windows/houdini_17.bat b/pype/settings/defaults/launchers/windows/houdini_17.bat new file mode 100644 index 0000000000..950a599623 --- /dev/null +++ b/pype/settings/defaults/launchers/windows/houdini_17.bat @@ -0,0 +1,13 @@ +@echo off + +set __app__="Houdini 17.0" +set __exe__="C:\Program Files\Side Effects Software\Houdini 17.0.459\bin\houdini.exe" +if not exist %__exe__% goto :missing_app + +start %__app__% %__exe__% %* + +goto :eof + +:missing_app + echo ERROR: %__app__% not found in %__exe__% + exit /B 1 diff --git a/pype/settings/defaults/launchers/windows/houdini_18.bat b/pype/settings/defaults/launchers/windows/houdini_18.bat new file mode 100644 index 0000000000..3d6b1ae258 --- /dev/null +++ b/pype/settings/defaults/launchers/windows/houdini_18.bat @@ -0,0 +1,13 @@ +@echo off + +set __app__="Houdini 18.0" +set __exe__="C:\Program Files\Side Effects Software\Houdini 18.0.287\bin\houdini.exe" +if not exist %__exe__% goto :missing_app + +start %__app__% %__exe__% %* + +goto :eof + +:missing_app + echo ERROR: %__app__% not found in %__exe__% + exit /B 1 diff --git a/pype/settings/defaults/launchers/windows/maya2016.bat b/pype/settings/defaults/launchers/windows/maya2016.bat new file mode 100644 index 0000000000..54f15cf269 --- /dev/null +++ b/pype/settings/defaults/launchers/windows/maya2016.bat @@ -0,0 +1,17 @@ +@echo off + +set __app__="Maya 2016" +set __exe__="C:\Program Files\Autodesk\Maya2016\bin\maya.exe" +if not exist %__exe__% goto :missing_app + +if "%AVALON_LAST_WORKFILE%"=="" ( + start %__app__% %__exe__% %* +) else ( + start %__app__% %__exe__% -file "%AVALON_LAST_WORKFILE%" %* +) + +goto :eof + +:missing_app + echo ERROR: %__app__% not found in %__exe__% + exit /B 1 diff --git a/pype/settings/defaults/launchers/windows/maya2017.bat b/pype/settings/defaults/launchers/windows/maya2017.bat new file mode 100644 index 0000000000..5c2aeb495c --- /dev/null +++ b/pype/settings/defaults/launchers/windows/maya2017.bat @@ -0,0 +1,17 @@ +@echo off + +set __app__="Maya 2017" +set __exe__="C:\Program Files\Autodesk\Maya2017\bin\maya.exe" +if not exist %__exe__% goto :missing_app + +if "%AVALON_LAST_WORKFILE%"=="" ( + start %__app__% %__exe__% %* +) else ( + start %__app__% %__exe__% -file "%AVALON_LAST_WORKFILE%" %* +) + +goto :eof + +:missing_app + echo ERROR: %__app__% not found in %__exe__% + exit /B 1 diff --git a/pype/settings/defaults/launchers/windows/maya2018.bat b/pype/settings/defaults/launchers/windows/maya2018.bat new file mode 100644 index 0000000000..28cf776c77 --- /dev/null +++ b/pype/settings/defaults/launchers/windows/maya2018.bat @@ -0,0 +1,17 @@ +@echo off + +set __app__="Maya 2018" +set __exe__="C:\Program Files\Autodesk\Maya2018\bin\maya.exe" +if not exist %__exe__% goto :missing_app + +if "%AVALON_LAST_WORKFILE%"=="" ( + start %__app__% %__exe__% %* +) else ( + start %__app__% %__exe__% -file "%AVALON_LAST_WORKFILE%" %* +) + +goto :eof + +:missing_app + echo ERROR: %__app__% not found in %__exe__% + exit /B 1 diff --git a/pype/settings/defaults/launchers/windows/maya2019.bat b/pype/settings/defaults/launchers/windows/maya2019.bat new file mode 100644 index 0000000000..7e80dd2557 --- /dev/null +++ b/pype/settings/defaults/launchers/windows/maya2019.bat @@ -0,0 +1,17 @@ +@echo off + +set __app__="Maya 2019" +set __exe__="C:\Program Files\Autodesk\Maya2019\bin\maya.exe" +if not exist %__exe__% goto :missing_app + +if "%AVALON_LAST_WORKFILE%"=="" ( + start %__app__% %__exe__% %* +) else ( + start %__app__% %__exe__% -file "%AVALON_LAST_WORKFILE%" %* +) + +goto :eof + +:missing_app + echo ERROR: %__app__% not found in %__exe__% + exit /B 1 diff --git a/pype/settings/defaults/launchers/windows/maya2020.bat b/pype/settings/defaults/launchers/windows/maya2020.bat new file mode 100644 index 0000000000..b2acb5df5a --- /dev/null +++ b/pype/settings/defaults/launchers/windows/maya2020.bat @@ -0,0 +1,17 @@ +@echo off + +set __app__="Maya 2020" +set __exe__="C:\Program Files\Autodesk\maya2020\bin\maya.exe" +if not exist %__exe__% goto :missing_app + +if "%AVALON_LAST_WORKFILE%"=="" ( + start %__app__% %__exe__% %* +) else ( + start %__app__% %__exe__% -file "%AVALON_LAST_WORKFILE%" %* +) + +goto :eof + +:missing_app + echo ERROR: %__app__% not found in %__exe__% + exit /B 1 diff --git a/pype/settings/defaults/launchers/windows/mayabatch2019.bat b/pype/settings/defaults/launchers/windows/mayabatch2019.bat new file mode 100644 index 0000000000..ddd9b9b956 --- /dev/null +++ b/pype/settings/defaults/launchers/windows/mayabatch2019.bat @@ -0,0 +1,14 @@ +@echo off + +set __app__="Maya Batch 2019" +set __exe__="C:\Program Files\Autodesk\Maya2019\bin\mayabatch.exe" +if not exist %__exe__% goto :missing_app + +echo "running maya : %*" +%__exe__% %* +echo "done." +goto :eof + +:missing_app + echo ERROR: %__app__% not found in %__exe__% + exit /B 1 diff --git a/pype/settings/defaults/launchers/windows/mayabatch2020.bat b/pype/settings/defaults/launchers/windows/mayabatch2020.bat new file mode 100644 index 0000000000..b1cbc6dbb6 --- /dev/null +++ b/pype/settings/defaults/launchers/windows/mayabatch2020.bat @@ -0,0 +1,14 @@ +@echo off + +set __app__="Maya Batch 2020" +set __exe__="C:\Program Files\Autodesk\Maya2020\bin\mayabatch.exe" +if not exist %__exe__% goto :missing_app + +echo "running maya : %*" +%__exe__% %* +echo "done." +goto :eof + +:missing_app + echo ERROR: %__app__% not found in %__exe__% + exit /B 1 diff --git a/pype/settings/defaults/launchers/windows/mayapy2016.bat b/pype/settings/defaults/launchers/windows/mayapy2016.bat new file mode 100644 index 0000000000..205991fd3d --- /dev/null +++ b/pype/settings/defaults/launchers/windows/mayapy2016.bat @@ -0,0 +1,13 @@ +@echo off + +set __app__="Mayapy 2016" +set __exe__="C:\Program Files\Autodesk\Maya2016\bin\mayapy.exe" +if not exist %__exe__% goto :missing_app + +call %__exe__% %* + +goto :eof + +:missing_app + echo ERROR: %__app__% not found at %__exe__% + exit /B 1 diff --git a/pype/settings/defaults/launchers/windows/mayapy2017.bat b/pype/settings/defaults/launchers/windows/mayapy2017.bat new file mode 100644 index 0000000000..14aacc5a7f --- /dev/null +++ b/pype/settings/defaults/launchers/windows/mayapy2017.bat @@ -0,0 +1,13 @@ +@echo off + +set __app__="Mayapy 2017" +set __exe__="C:\Program Files\Autodesk\Maya2017\bin\mayapy.exe" +if not exist %__exe__% goto :missing_app + +call %__exe__% %* + +goto :eof + +:missing_app + echo ERROR: %__app__% not found at %__exe__% + exit /B 1 diff --git a/pype/settings/defaults/launchers/windows/mayapy2018.bat b/pype/settings/defaults/launchers/windows/mayapy2018.bat new file mode 100644 index 0000000000..c47c472f46 --- /dev/null +++ b/pype/settings/defaults/launchers/windows/mayapy2018.bat @@ -0,0 +1,13 @@ +@echo off + +set __app__="Mayapy 2018" +set __exe__="C:\Program Files\Autodesk\Maya2018\bin\mayapy.exe" +if not exist %__exe__% goto :missing_app + +call %__exe__% %* + +goto :eof + +:missing_app + echo ERROR: %__app__% not found at %__exe__% + exit /B 1 diff --git a/pype/settings/defaults/launchers/windows/mayapy2019.bat b/pype/settings/defaults/launchers/windows/mayapy2019.bat new file mode 100644 index 0000000000..73ca5b2d40 --- /dev/null +++ b/pype/settings/defaults/launchers/windows/mayapy2019.bat @@ -0,0 +1,13 @@ +@echo off + +set __app__="Mayapy 2019" +set __exe__="C:\Program Files\Autodesk\Maya2019\bin\mayapy.exe" +if not exist %__exe__% goto :missing_app + +call %__exe__% %* + +goto :eof + +:missing_app + echo ERROR: %__app__% not found at %__exe__% + exit /B 1 diff --git a/pype/settings/defaults/launchers/windows/mayapy2020.bat b/pype/settings/defaults/launchers/windows/mayapy2020.bat new file mode 100644 index 0000000000..770a03dcf5 --- /dev/null +++ b/pype/settings/defaults/launchers/windows/mayapy2020.bat @@ -0,0 +1,13 @@ +@echo off + +set __app__="Mayapy 2020" +set __exe__="C:\Program Files\Autodesk\Maya2020\bin\mayapy.exe" +if not exist %__exe__% goto :missing_app + +call %__exe__% %* + +goto :eofS + +:missing_app + echo ERROR: %__app__% not found at %__exe__% + exit /B 1 diff --git a/pype/settings/defaults/launchers/windows/nuke10.0.bat b/pype/settings/defaults/launchers/windows/nuke10.0.bat new file mode 100644 index 0000000000..a47cbdfb20 --- /dev/null +++ b/pype/settings/defaults/launchers/windows/nuke10.0.bat @@ -0,0 +1,13 @@ +@echo off + +set __app__="Nuke10.0v4" +set __exe__="C:\Program Files\Nuke10.0v4\Nuke10.0.exe" +if not exist %__exe__% goto :missing_app + +start %__app__% %__exe__% %* + +goto :eof + +:missing_app + echo ERROR: %__app__% not found in %__exe__% + exit /B 1 diff --git a/pype/settings/defaults/launchers/windows/nuke11.0.bat b/pype/settings/defaults/launchers/windows/nuke11.0.bat new file mode 100644 index 0000000000..a374c5cf5b --- /dev/null +++ b/pype/settings/defaults/launchers/windows/nuke11.0.bat @@ -0,0 +1,13 @@ +@echo off + +set __app__="Nuke11.0v4" +set __exe__="C:\Program Files\Nuke11.0v4\Nuke11.0.exe" +if not exist %__exe__% goto :missing_app + +start %__app__% %__exe__% %* + +goto :eof + +:missing_app + echo ERROR: %__app__% not found in %__exe__% + exit /B 1 diff --git a/pype/settings/defaults/launchers/windows/nuke11.2.bat b/pype/settings/defaults/launchers/windows/nuke11.2.bat new file mode 100644 index 0000000000..4c777ac28c --- /dev/null +++ b/pype/settings/defaults/launchers/windows/nuke11.2.bat @@ -0,0 +1,13 @@ +@echo off + +set __app__="Nuke11.2v3" +set __exe__="C:\Program Files\Nuke11.2v3\Nuke11.2.exe" +if not exist %__exe__% goto :missing_app + +start %__app__% %__exe__% %* + +goto :eof + +:missing_app + echo ERROR: %__app__% not found in %__exe__% + exit /B 1 diff --git a/pype/settings/defaults/launchers/windows/nuke11.3.bat b/pype/settings/defaults/launchers/windows/nuke11.3.bat new file mode 100644 index 0000000000..a023f5f46f --- /dev/null +++ b/pype/settings/defaults/launchers/windows/nuke11.3.bat @@ -0,0 +1,13 @@ +@echo off + +set __app__="Nuke11.3v1" +set __exe__="C:\Program Files\Nuke11.3v1\Nuke11.3.exe" +if not exist %__exe__% goto :missing_app + +start %__app__% %__exe__% %* + +goto :eof + +:missing_app + echo ERROR: %__app__% not found in %__exe__% + exit /B 1 diff --git a/pype/settings/defaults/launchers/windows/nuke12.0.bat b/pype/settings/defaults/launchers/windows/nuke12.0.bat new file mode 100644 index 0000000000..d8fb5772bb --- /dev/null +++ b/pype/settings/defaults/launchers/windows/nuke12.0.bat @@ -0,0 +1,13 @@ +@echo off + +set __app__="Nuke12.0v1" +set __exe__="C:\Program Files\Nuke12.0v1\Nuke12.0.exe" +if not exist %__exe__% goto :missing_app + +start %__app__% %__exe__% %* + +goto :eof + +:missing_app + echo ERROR: %__app__% not found in %__exe__% + exit /B 1 diff --git a/pype/settings/defaults/launchers/windows/nukestudio10.0.bat b/pype/settings/defaults/launchers/windows/nukestudio10.0.bat new file mode 100644 index 0000000000..82f833667c --- /dev/null +++ b/pype/settings/defaults/launchers/windows/nukestudio10.0.bat @@ -0,0 +1,13 @@ +@echo off + +set __app__="NukeStudio10.0v4" +set __exe__="C:\Program Files\Nuke10.0v4\Nuke10.0.exe" --studio +if not exist %__exe__% goto :missing_app + +start %__app__% %__exe__% %* + +goto :eof + +:missing_app + echo ERROR: %__app__% not found in %__exe__% + exit /B 1 diff --git a/pype/settings/defaults/launchers/windows/nukestudio11.0.bat b/pype/settings/defaults/launchers/windows/nukestudio11.0.bat new file mode 100644 index 0000000000..b66797727e --- /dev/null +++ b/pype/settings/defaults/launchers/windows/nukestudio11.0.bat @@ -0,0 +1,13 @@ +@echo off + +set __app__="NukeStudio11.0v4" +set __exe__="C:\Program Files\Nuke11.0v4\Nuke11.0.exe" -studio +if not exist %__exe__% goto :missing_app + +start %__app__% %__exe__% %* + +goto :eof + +:missing_app + echo ERROR: %__app__% not found in %__exe__% + exit /B 1 diff --git a/pype/settings/defaults/launchers/windows/nukestudio11.2.bat b/pype/settings/defaults/launchers/windows/nukestudio11.2.bat new file mode 100644 index 0000000000..a653d816b4 --- /dev/null +++ b/pype/settings/defaults/launchers/windows/nukestudio11.2.bat @@ -0,0 +1,13 @@ +@echo off + +set __app__="NukeStudio11.2v3" +set __exe__="C:\Program Files\Nuke11.2v3\Nuke11.2.exe" -studio +if not exist %__exe__% goto :missing_app + +start %__app__% %__exe__% %* + +goto :eof + +:missing_app + echo ERROR: %__app__% not found in %__exe__% + exit /B 1 diff --git a/pype/settings/defaults/launchers/windows/nukestudio11.3.bat b/pype/settings/defaults/launchers/windows/nukestudio11.3.bat new file mode 100644 index 0000000000..62c8718873 --- /dev/null +++ b/pype/settings/defaults/launchers/windows/nukestudio11.3.bat @@ -0,0 +1,13 @@ +@echo off + +set __app__="NukeStudio11.3v1" +set __exe__="C:\Program Files\Nuke11.3v1\Nuke11.3.exe" --studio +if not exist %__exe__% goto :missing_app + +start %__app__% %__exe__% %* + +goto :eof + +:missing_app + echo ERROR: %__app__% not found in %__exe__% + exit /B 1 diff --git a/pype/settings/defaults/launchers/windows/nukestudio12.0.bat b/pype/settings/defaults/launchers/windows/nukestudio12.0.bat new file mode 100644 index 0000000000..488232bcbf --- /dev/null +++ b/pype/settings/defaults/launchers/windows/nukestudio12.0.bat @@ -0,0 +1,13 @@ +@echo off + +set __app__="NukeStudio12.0v1" +set __exe__="C:\Program Files\Nuke12.0v1\Nuke12.0.exe" --studio +if not exist %__exe__% goto :missing_app + +start %__app__% %__exe__% %* + +goto :eof + +:missing_app + echo ERROR: %__app__% not found in %__exe__% + exit /B 1 diff --git a/pype/settings/defaults/launchers/windows/nukex10.0.bat b/pype/settings/defaults/launchers/windows/nukex10.0.bat new file mode 100644 index 0000000000..1759706a7b --- /dev/null +++ b/pype/settings/defaults/launchers/windows/nukex10.0.bat @@ -0,0 +1,13 @@ +@echo off + +set __app__="NukeX10.0v4" +set __exe__="C:\Program Files\Nuke10.0v4\Nuke10.0.exe" -nukex +if not exist %__exe__% goto :missing_app + +start %__app__% %__exe__% %* + +goto :eof + +:missing_app + echo ERROR: %__app__% not found in %__exe__% + exit /B 1 diff --git a/pype/settings/defaults/launchers/windows/nukex11.0.bat b/pype/settings/defaults/launchers/windows/nukex11.0.bat new file mode 100644 index 0000000000..b554a7b6fa --- /dev/null +++ b/pype/settings/defaults/launchers/windows/nukex11.0.bat @@ -0,0 +1,13 @@ +@echo off + +set __app__="NukeX11.0v4" +set __exe__="C:\Program Files\Nuke11.0v4\Nuke11.0.exe" --nukex +if not exist %__exe__% goto :missing_app + +start %__app__% %__exe__% %* + +goto :eof + +:missing_app + echo ERROR: %__app__% not found in %__exe__% + exit /B 1 diff --git a/pype/settings/defaults/launchers/windows/nukex11.2.bat b/pype/settings/defaults/launchers/windows/nukex11.2.bat new file mode 100644 index 0000000000..a4cb5dec5c --- /dev/null +++ b/pype/settings/defaults/launchers/windows/nukex11.2.bat @@ -0,0 +1,13 @@ +@echo off + +set __app__="NukeX11.2v3" +set __exe__="C:\Program Files\Nuke11.2v3\Nuke11.2.exe" --nukex +if not exist %__exe__% goto :missing_app + +start %__app__% %__exe__% %* + +goto :eof + +:missing_app + echo ERROR: %__app__% not found in %__exe__% + exit /B 1 diff --git a/pype/settings/defaults/launchers/windows/nukex11.3.bat b/pype/settings/defaults/launchers/windows/nukex11.3.bat new file mode 100644 index 0000000000..490b55cf4c --- /dev/null +++ b/pype/settings/defaults/launchers/windows/nukex11.3.bat @@ -0,0 +1,13 @@ +@echo off + +set __app__="NukeX11.3v1" +set __exe__="C:\Program Files\Nuke11.3v1\Nuke11.3.exe" --nukex +if not exist %__exe__% goto :missing_app + +start %__app__% %__exe__% %* + +goto :eof + +:missing_app + echo ERROR: %__app__% not found in %__exe__% + exit /B 1 diff --git a/pype/settings/defaults/launchers/windows/nukex12.0.bat b/pype/settings/defaults/launchers/windows/nukex12.0.bat new file mode 100644 index 0000000000..26adf0d3f1 --- /dev/null +++ b/pype/settings/defaults/launchers/windows/nukex12.0.bat @@ -0,0 +1,13 @@ +@echo off + +set __app__="NukeX12.0v1" +set __exe__="C:\Program Files\Nuke12.0v1\Nuke12.0.exe" --nukex +if not exist %__exe__% goto :missing_app + +start %__app__% %__exe__% %* + +goto :eof + +:missing_app + echo ERROR: %__app__% not found in %__exe__% + exit /B 1 diff --git a/pype/settings/defaults/launchers/windows/photoshop_2020.bat b/pype/settings/defaults/launchers/windows/photoshop_2020.bat new file mode 100644 index 0000000000..6b90922ef6 --- /dev/null +++ b/pype/settings/defaults/launchers/windows/photoshop_2020.bat @@ -0,0 +1,15 @@ +@echo off + +set __app__="Photoshop 2020" +set __exe__="C:\Program Files\Adobe\Adobe Photoshop 2020\Photoshop.exe" +if not exist %__exe__% goto :missing_app + +start %__app__% cmd.exe /k "%PYPE_PYTHON_EXE% -c ^"import avalon.photoshop;avalon.photoshop.launch("%__exe__%")^"" + +goto :eof + +pause + +:missing_app + echo ERROR: %__app__% not found in %__exe__% + exit /B 1 diff --git a/pype/settings/defaults/launchers/windows/premiere_pro_2019.bat b/pype/settings/defaults/launchers/windows/premiere_pro_2019.bat new file mode 100644 index 0000000000..4886737d2f --- /dev/null +++ b/pype/settings/defaults/launchers/windows/premiere_pro_2019.bat @@ -0,0 +1,14 @@ +@echo off + +set __app__="Adobe Premiere Pro" +set __exe__="C:\Program Files\Adobe\Adobe Premiere Pro CC 2019\Adobe Premiere Pro.exe" +if not exist %__exe__% goto :missing_app + +python -u %PREMIERA_PATH%\init.py +start %__app__% %__exe__% %* + +goto :eof + +:missing_app + echo ERROR: %__app__% not found in %__exe__% + exit /B 1 diff --git a/pype/settings/defaults/launchers/windows/premiere_pro_2020.bat b/pype/settings/defaults/launchers/windows/premiere_pro_2020.bat new file mode 100644 index 0000000000..14662d3be3 --- /dev/null +++ b/pype/settings/defaults/launchers/windows/premiere_pro_2020.bat @@ -0,0 +1,13 @@ +@echo off + +set __app__="Adobe Premiere Pro" +set __exe__="C:\Program Files\Adobe\Adobe Premiere Pro 2020\Adobe Premiere Pro.exe" +if not exist %__exe__% goto :missing_app + +start %__app__% %__exe__% %* + +goto :eof + +:missing_app + echo ERROR: %__app__% not found in %__exe__% + exit /B 1 diff --git a/pype/settings/defaults/launchers/windows/python3.bat b/pype/settings/defaults/launchers/windows/python3.bat new file mode 100644 index 0000000000..c7c116fe72 --- /dev/null +++ b/pype/settings/defaults/launchers/windows/python3.bat @@ -0,0 +1,13 @@ +@echo off + +set __app__="Python36" +set __exe__="C:\Python36\python.exe" +if not exist %__exe__% goto :missing_app + +start %__app__% %__exe__% %* + +goto :eof + +:missing_app + echo ERROR: %__app__% not found in %__exe__% + exit /B 1 diff --git a/pype/settings/defaults/launchers/windows/resolve_16.bat b/pype/settings/defaults/launchers/windows/resolve_16.bat new file mode 100644 index 0000000000..1a5d964e6b --- /dev/null +++ b/pype/settings/defaults/launchers/windows/resolve_16.bat @@ -0,0 +1,17 @@ +@echo off + +set __app__="Resolve" +set __appy__="Resolve Python Console" +set __exe__="C:/Program Files/Blackmagic Design/DaVinci Resolve/Resolve.exe" +set __py__="%PYTHON36_RESOLVE%/python.exe" + +if not exist %__exe__% goto :missing_app + +start %__app__% %__exe__% %* +IF "%RESOLVE_DEV%"=="True" (start %__appy__% %__py__% -i %PRE_PYTHON_SCRIPT%) + +goto :eof + +:missing_app + echo ERROR: %__app__% not found in %__exe__% + exit /B 1 diff --git a/pype/settings/defaults/launchers/windows/shell.bat b/pype/settings/defaults/launchers/windows/shell.bat new file mode 100644 index 0000000000..eb0895364f --- /dev/null +++ b/pype/settings/defaults/launchers/windows/shell.bat @@ -0,0 +1,2 @@ +@echo off +start cmd diff --git a/pype/settings/defaults/launchers/windows/storyboardpro_7.bat b/pype/settings/defaults/launchers/windows/storyboardpro_7.bat new file mode 100644 index 0000000000..122edac572 --- /dev/null +++ b/pype/settings/defaults/launchers/windows/storyboardpro_7.bat @@ -0,0 +1,13 @@ +@echo off + +set __app__="Storyboard Pro 7" +set __exe__="C:/Program Files (x86)/Toon Boom Animation/Toon Boom Storyboard Pro 7/win64/bin/StoryboardPro.exe" +if not exist %__exe__% goto :missing_app + +start %__app__% cmd.exe /k "python -c ^"import avalon.storyboardpro;avalon.storyboardpro.launch("%__exe__%")^"" + +goto :eof + +:missing_app + echo ERROR: %__app__% not found in %__exe__% + exit /B 1 diff --git a/pype/settings/defaults/launchers/windows/unreal.bat b/pype/settings/defaults/launchers/windows/unreal.bat new file mode 100644 index 0000000000..7771aaa5a5 --- /dev/null +++ b/pype/settings/defaults/launchers/windows/unreal.bat @@ -0,0 +1,11 @@ +set __app__="Unreal Editor" +set __exe__="%AVALON_CURRENT_UNREAL_ENGINE%\Engine\Binaries\Win64\UE4Editor.exe" +if not exist %__exe__% goto :missing_app + +start %__app__% %__exe__% %PYPE_UNREAL_PROJECT_FILE% %* + +goto :eof + +:missing_app + echo ERROR: %__app__% not found in %__exe__% + exit /B 1 From 9e40f3c9fd719bc39ce2a2efa26c2c4979272199 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 30 Sep 2020 14:50:33 +0200 Subject: [PATCH 36/92] implemented function which returns environments --- pype/settings/lib.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/pype/settings/lib.py b/pype/settings/lib.py index 388557ca9b..848bdeea92 100644 --- a/pype/settings/lib.py +++ b/pype/settings/lib.py @@ -19,6 +19,12 @@ SYSTEM_SETTINGS_PATH = os.path.join( STUDIO_OVERRIDES_PATH, SYSTEM_SETTINGS_KEY + ".json" ) +# File where studio's environment overrides are stored +ENVIRONMENTS_KEY = "environments" +ENVIRONMENTS_PATH = os.path.join( + STUDIO_OVERRIDES_PATH, ENVIRONMENTS_KEY + ".json" +) + # File where studio's default project overrides are stored PROJECT_SETTINGS_KEY = "project_settings" PROJECT_SETTINGS_FILENAME = PROJECT_SETTINGS_KEY + ".json" @@ -162,6 +168,12 @@ def studio_system_settings(): return {} +def studio_environments(): + if os.path.exists(ENVIRONMENTS_PATH): + return load_json(ENVIRONMENTS_PATH) + return {} + + def studio_project_settings(): if os.path.exists(PROJECT_SETTINGS_PATH): return load_json(PROJECT_SETTINGS_PATH) @@ -256,3 +268,9 @@ def project_settings(project_name): project_overrides = project_settings_overrides(project_name) return apply_overrides(studio_overrides, project_overrides) + + +def environments(): + default_values = default_settings()[ENVIRONMENTS_KEY] + studio_values = studio_system_settings() + return apply_overrides(default_values, studio_values) From def129501420aa644d8304f6a6c719ac12a97c55 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 30 Sep 2020 14:50:48 +0200 Subject: [PATCH 37/92] added environemnts function to pype.api --- pype/api.py | 4 +++- pype/settings/__init__.py | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/pype/api.py b/pype/api.py index 021080b4d5..c1bf84b4ef 100644 --- a/pype/api.py +++ b/pype/api.py @@ -1,6 +1,7 @@ from .settings import ( system_settings, - project_settings + project_settings, + environments ) from pypeapp import ( Logger, @@ -55,6 +56,7 @@ from .lib import _subprocess as subprocess __all__ = [ "system_settings", "project_settings", + "environments", "Logger", "Anatomy", diff --git a/pype/settings/__init__.py b/pype/settings/__init__.py index 7e73d541a4..7a99ba0b2f 100644 --- a/pype/settings/__init__.py +++ b/pype/settings/__init__.py @@ -1,9 +1,11 @@ from .lib import ( system_settings, - project_settings + project_settings, + environments ) __all__ = ( "system_settings", - "project_settings" + "project_settings", + "environments" ) From 6951e046de4a531a635a80ca8909f601cac076c9 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 30 Sep 2020 15:13:19 +0200 Subject: [PATCH 38/92] clockify modules does not use Qt unless tray_init is triggered --- pype/modules/clockify/clockify.py | 75 +++++++++++++++++-------------- 1 file changed, 41 insertions(+), 34 deletions(-) diff --git a/pype/modules/clockify/clockify.py b/pype/modules/clockify/clockify.py index fea15a1bea..24f1b0b39d 100644 --- a/pype/modules/clockify/clockify.py +++ b/pype/modules/clockify/clockify.py @@ -1,9 +1,8 @@ import os import threading +import time + from pype.api import Logger -from avalon import style -from Qt import QtWidgets -from .widgets import ClockifySettings, MessageWidget from .clockify_api import ClockifyAPI from .constants import CLOCKIFY_FTRACK_USER_PATH @@ -17,11 +16,21 @@ class ClockifyModule: os.environ["CLOCKIFY_WORKSPACE"] = self.workspace_name + self.timer_manager = None + self.MessageWidgetClass = None + + self.clockapi = ClockifyAPI(master_parent=self) + self.log = Logger().get_logger(self.__class__.__name__, "PypeTray") + self.tray_init(main_parent, parent) + + def tray_init(self, main_parent, parent): + from .widgets import ClockifySettings, MessageWidget + + self.MessageWidgetClass = MessageWidget self.main_parent = main_parent self.parent = parent - self.clockapi = ClockifyAPI(master_parent=self) self.message_widget = None self.widget_settings = ClockifySettings(main_parent, self) self.widget_settings_required = None @@ -78,12 +87,12 @@ class ClockifyModule: self.stop_timer() def timer_started(self, data): - if hasattr(self, 'timer_manager'): + if self.timer_manager: self.timer_manager.start_timers(data) def timer_stopped(self): self.bool_timer_run = False - if hasattr(self, 'timer_manager'): + if self.timer_manager: self.timer_manager.stop_timers() def start_timer_check(self): @@ -102,7 +111,7 @@ class ClockifyModule: self.thread_timer_check = None def check_running(self): - import time + while self.bool_thread_check_running is True: bool_timer_run = False if self.clockapi.get_in_progress() is not None: @@ -156,15 +165,14 @@ class ClockifyModule: self.timer_stopped() def signed_in(self): - if hasattr(self, 'timer_manager'): - if not self.timer_manager: - return + if not self.timer_manager: + return - if not self.timer_manager.last_task: - return + if not self.timer_manager.last_task: + return - if self.timer_manager.is_running: - self.start_timer_manager(self.timer_manager.last_task) + if self.timer_manager.is_running: + self.start_timer_manager(self.timer_manager.last_task) def start_timer(self, input_data): # If not api key is not entered then skip @@ -197,11 +205,12 @@ class ClockifyModule: "

Please inform your Project Manager." ).format(project_name, str(self.clockapi.workspace_name)) - self.message_widget = MessageWidget( - self.main_parent, msg, "Clockify - Info Message" - ) - self.message_widget.closed.connect(self.on_message_widget_close) - self.message_widget.show() + if self.MessageWidgetClass: + self.message_widget = self.MessageWidgetClass( + self.main_parent, msg, "Clockify - Info Message" + ) + self.message_widget.closed.connect(self.on_message_widget_close) + self.message_widget.show() return @@ -227,31 +236,29 @@ class ClockifyModule: # Definition of Tray menu def tray_menu(self, parent_menu): # Menu for Tray App - self.menu = QtWidgets.QMenu('Clockify', parent_menu) - self.menu.setProperty('submenu', 'on') - self.menu.setStyleSheet(style.load_stylesheet()) + from Qt import QtWidgets + menu = QtWidgets.QMenu("Clockify", parent_menu) + menu.setProperty("submenu", "on") # Actions - self.aShowSettings = QtWidgets.QAction( - "Settings", self.menu - ) - self.aStopTimer = QtWidgets.QAction( - "Stop timer", self.menu - ) + action_show_settings = QtWidgets.QAction("Settings", menu) + action_stop_timer = QtWidgets.QAction("Stop timer", menu) - self.menu.addAction(self.aShowSettings) - self.menu.addAction(self.aStopTimer) + menu.addAction(action_show_settings) + menu.addAction(action_stop_timer) - self.aShowSettings.triggered.connect(self.show_settings) - self.aStopTimer.triggered.connect(self.stop_timer) + action_show_settings.triggered.connect(self.show_settings) + action_stop_timer.triggered.connect(self.stop_timer) + + self.action_stop_timer = action_stop_timer self.set_menu_visibility() - parent_menu.addMenu(self.menu) + parent_menu.addMenu(menu) def show_settings(self): self.widget_settings.input_api_key.setText(self.clockapi.get_api_key()) self.widget_settings.show() def set_menu_visibility(self): - self.aStopTimer.setVisible(self.bool_timer_run) + self.action_stop_timer.setVisible(self.bool_timer_run) From d8eec091c316dc6bdbec09d9093a2778f77281ff Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 30 Sep 2020 15:19:31 +0200 Subject: [PATCH 39/92] fix hound --- pype/modules/clockify/clockify.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pype/modules/clockify/clockify.py b/pype/modules/clockify/clockify.py index 24f1b0b39d..4309bff9f2 100644 --- a/pype/modules/clockify/clockify.py +++ b/pype/modules/clockify/clockify.py @@ -66,11 +66,10 @@ class ClockifyModule: ) if 'AvalonApps' in modules: - from launcher import lib - actions_path = os.path.sep.join([ + actions_path = os.path.join( os.path.dirname(__file__), 'launcher_actions' - ]) + ) current = os.environ.get('AVALON_ACTIONS', '') if current: current += os.pathsep @@ -209,7 +208,9 @@ class ClockifyModule: self.message_widget = self.MessageWidgetClass( self.main_parent, msg, "Clockify - Info Message" ) - self.message_widget.closed.connect(self.on_message_widget_close) + self.message_widget.closed.connect( + self.on_message_widget_close + ) self.message_widget.show() return From 4785cf26c4afdf1e9cbe82806515a35e405c773e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 30 Sep 2020 15:24:31 +0200 Subject: [PATCH 40/92] muster is not using Qt until tray_init is triggered --- pype/modules/muster/muster.py | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/pype/modules/muster/muster.py b/pype/modules/muster/muster.py index 629fb12635..beb30690ac 100644 --- a/pype/modules/muster/muster.py +++ b/pype/modules/muster/muster.py @@ -1,10 +1,7 @@ -import appdirs -from avalon import style -from Qt import QtWidgets import os import json -from .widget_login import MusterLogin -from avalon.vendor import requests +import appdirs +import requests class MusterModule: @@ -21,6 +18,11 @@ class MusterModule: self.cred_path = os.path.join( self.cred_folder_path, self.cred_filename ) + self.tray_init(main_parent, parent) + + def tray_init(self, main_parent, parent): + from .widget_login import MusterLogin + self.main_parent = main_parent self.parent = parent self.widget_login = MusterLogin(main_parent, self) @@ -38,10 +40,6 @@ class MusterModule: pass def process_modules(self, modules): - - def api_callback(): - self.aShowLogin.trigger() - if "RestApiServer" in modules: def api_show_login(): self.aShowLogin.trigger() @@ -51,13 +49,12 @@ class MusterModule: # Definition of Tray menu def tray_menu(self, parent): - """ - Add **change credentials** option to tray menu. - """ + """Add **change credentials** option to tray menu.""" + from Qt import QtWidgets + # Menu for Tray App self.menu = QtWidgets.QMenu('Muster', parent) self.menu.setProperty('submenu', 'on') - self.menu.setStyleSheet(style.load_stylesheet()) # Actions self.aShowLogin = QtWidgets.QAction( @@ -91,9 +88,9 @@ class MusterModule: if not MUSTER_REST_URL: raise AttributeError("Muster REST API url not set") params = { - 'username': username, - 'password': password - } + 'username': username, + 'password': password + } api_entry = '/api/login' response = self._requests_post( MUSTER_REST_URL + api_entry, params=params) From d8deb18b52558a7678596ab9e3c6dbb19fbd6514 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 30 Sep 2020 18:41:07 +0200 Subject: [PATCH 41/92] fix(SP): not correct filtering of activated assets in hierarchy --- .../publish/extract_hierarchy_avalon.py | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/pype/plugins/global/publish/extract_hierarchy_avalon.py b/pype/plugins/global/publish/extract_hierarchy_avalon.py index 4253c35929..eb791184ed 100644 --- a/pype/plugins/global/publish/extract_hierarchy_avalon.py +++ b/pype/plugins/global/publish/extract_hierarchy_avalon.py @@ -1,6 +1,6 @@ import pyblish.api from avalon import io - +from copy import deepcopy class ExtractHierarchyToAvalon(pyblish.api.ContextPlugin): """Create entities in Avalon based on collected data.""" @@ -14,14 +14,12 @@ class ExtractHierarchyToAvalon(pyblish.api.ContextPlugin): if "hierarchyContext" not in context.data: self.log.info("skipping IntegrateHierarchyToAvalon") return + hierarchy_context = deepcopy(context.data["hierarchyContext"]) if not io.Session: io.install() active_assets = [] - hierarchy_context = context.data["hierarchyContext"] - hierarchy_assets = self._get_assets(hierarchy_context) - # filter only the active publishing insatnces for instance in context: if instance.data.get("publish") is False: @@ -32,13 +30,13 @@ class ExtractHierarchyToAvalon(pyblish.api.ContextPlugin): active_assets.append(instance.data["asset"]) - # filter out only assets which are activated as isntances - new_hierarchy_assets = {k: v for k, v in hierarchy_assets.items() - if k in active_assets} + # remove duplicity in list + self.active_assets = list(set(active_assets)) + self.log.debug("__ self.active_assets: {}".format(self.active_assets)) - # modify the hierarchy context so there are only fitred assets - self._set_assets(hierarchy_context, new_hierarchy_assets) + hierarchy_context = self._get_assets(hierarchy_context) + self.log.debug("__ hierarchy_context: {}".format(hierarchy_context)) input_data = context.data["hierarchyContext"] = hierarchy_context self.project = None @@ -178,14 +176,21 @@ class ExtractHierarchyToAvalon(pyblish.api.ContextPlugin): Usually the last part of deep dictionary which is not having any children """ + input_dict_copy = deepcopy(input_dict) + for key in input_dict.keys(): + self.log.debug("__ key: {}".format(key)) # check if child key is available if input_dict[key].get("childs"): # loop deeper - return self._get_assets(input_dict[key]["childs"]) + input_dict_copy[key]["childs"] = self._get_assets( + input_dict[key]["childs"]) else: - # give the dictionary with assets - return input_dict + # filter out unwanted assets + if key not in self.active_assets: + input_dict_copy.pop(key, None) + + return input_dict_copy def _set_assets(self, input_dict, new_assets=None): """ Modify the hierarchy context dictionary. From 56ca9b804c0e33d3461c1ef35ef9695889bc4812 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 30 Sep 2020 18:41:52 +0200 Subject: [PATCH 42/92] clean(SP): old code cleanup --- .../publish/extract_hierarchy_avalon.py | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/pype/plugins/global/publish/extract_hierarchy_avalon.py b/pype/plugins/global/publish/extract_hierarchy_avalon.py index eb791184ed..5d11eae058 100644 --- a/pype/plugins/global/publish/extract_hierarchy_avalon.py +++ b/pype/plugins/global/publish/extract_hierarchy_avalon.py @@ -191,27 +191,3 @@ class ExtractHierarchyToAvalon(pyblish.api.ContextPlugin): input_dict_copy.pop(key, None) return input_dict_copy - - def _set_assets(self, input_dict, new_assets=None): - """ Modify the hierarchy context dictionary. - It will replace the asset dictionary with only the filtred one. - """ - for key in input_dict.keys(): - # check if child key is available - if input_dict[key].get("childs"): - # return if this is just for testing purpose and no - # new_assets property is avalable - if not new_assets: - return True - - # test for deeper inner children availabelity - if self._set_assets(input_dict[key]["childs"]): - # if one level deeper is still children available - # then process farther - self._set_assets(input_dict[key]["childs"], new_assets) - else: - # or just assign the filtred asset ditionary - input_dict[key]["childs"] = new_assets - else: - # test didnt find more childs in input dictionary - return None From a09b74369e616f6a0f65e8dfb04432b65cc6ca81 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Wed, 30 Sep 2020 18:51:18 +0200 Subject: [PATCH 43/92] fix clashing namespace of called functions --- pype/hosts/harmony/__init__.py | 37 ++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/pype/hosts/harmony/__init__.py b/pype/hosts/harmony/__init__.py index 7310e91e9b..f920e38765 100644 --- a/pype/hosts/harmony/__init__.py +++ b/pype/hosts/harmony/__init__.py @@ -1,5 +1,6 @@ import os import sys +from uuid import uuid4 from avalon import api, io, harmony from avalon.vendor import Qt @@ -8,8 +9,11 @@ import pyblish.api from pype import lib +signature = str(uuid4()) + + def set_scene_settings(settings): - func = """function func(args) + func = """function %s_func(args) { if (args[0]["fps"]) { @@ -36,8 +40,8 @@ def set_scene_settings(settings): ) } } - func - """ + %s_func + """ % (signature, signature) harmony.send({"function": func, "args": [settings]}) @@ -107,15 +111,15 @@ def check_inventory(): outdated_containers.append(container) # Colour nodes. - func = """function func(args){ + func = """function %s_func(args){ for( var i =0; i <= args[0].length - 1; ++i) { var red_color = new ColorRGBA(255, 0, 0, 255); node.setColor(args[0][i], red_color); } } - func - """ + %s_func + """ % (signature, signature) outdated_nodes = [] for container in outdated_containers: if container["loader"] == "ImageSequenceLoader": @@ -144,7 +148,7 @@ def application_launch(): def export_template(backdrops, nodes, filepath): - func = """function func(args) + func = """function %s_func(args) { var temp_node = node.add("Top", "temp_note", "NOTE", 0, 0, 0); @@ -179,8 +183,8 @@ def export_template(backdrops, nodes, filepath): Action.perform("onActionUpToParent()", "Node View"); node.deleteNode(template_group, true, true); } - func - """ + %s_func + """ % (signature, signature) harmony.send({ "function": func, "args": [ @@ -221,12 +225,15 @@ def install(): def on_pyblish_instance_toggled(instance, old_value, new_value): """Toggle node enabling on instance toggles.""" - func = """function func(args) + func = """function %s_func(args) { node.setEnable(args[0], args[1]) } - func - """ - harmony.send( - {"function": func, "args": [instance[0], new_value]} - ) + %s_func + """ % (signature, signature) + try: + harmony.send( + {"function": func, "args": [instance[0], new_value]} + ) + except IndexError: + print(f"Instance '{instance}' is missing node") From fa541d1ddb5ff7a46dc9cbeaddf751f3651fa3e2 Mon Sep 17 00:00:00 2001 From: Milan Date: Wed, 30 Sep 2020 18:44:09 +0100 Subject: [PATCH 44/92] fix(SP): adding collect for plate and render family --- .../publish/collect_instance_data.py | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 pype/plugins/standalonepublisher/publish/collect_instance_data.py diff --git a/pype/plugins/standalonepublisher/publish/collect_instance_data.py b/pype/plugins/standalonepublisher/publish/collect_instance_data.py new file mode 100644 index 0000000000..1b32ea9144 --- /dev/null +++ b/pype/plugins/standalonepublisher/publish/collect_instance_data.py @@ -0,0 +1,29 @@ +""" +Requires: + Nothing + +Provides: + Instance +""" + +import pyblish.api +from pprint import pformat + + +class CollectInstanceData(pyblish.api.InstancePlugin): + """ + Collector with only one reason for its existence - remove 'ftrack' + family implicitly added by Standalone Publisher + """ + + label = "Collect instance data" + order = pyblish.api.CollectorOrder + 0.49 + families = ["render", "plate"] + hosts = ["standalonepublisher"] + + def process(self, instance): + fps = instance.data["assetEntity"]["data"]["fps"] + instance.data.update({ + "fps": fps + }) + self.log.debug(f"instance.data: {pformat(instance.data)}") From 5c9327fe867b76d082c0e87cdba17976d9c852e7 Mon Sep 17 00:00:00 2001 From: Milan Date: Thu, 1 Oct 2020 09:32:25 +0100 Subject: [PATCH 45/92] fixing space in path --- pype/tools/standalonepublish/widgets/widget_drop_frame.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pype/tools/standalonepublish/widgets/widget_drop_frame.py b/pype/tools/standalonepublish/widgets/widget_drop_frame.py index e13f701b30..a7abe1b24c 100644 --- a/pype/tools/standalonepublish/widgets/widget_drop_frame.py +++ b/pype/tools/standalonepublish/widgets/widget_drop_frame.py @@ -268,9 +268,10 @@ class DropDataFrame(QtWidgets.QFrame): args = [ ffprobe_path, '-v', 'quiet', - '-print_format', 'json', + '-print_format json', '-show_format', - '-show_streams', filepath + '-show_streams', + '"{}"'.format(filepath) ] ffprobe_p = subprocess.Popen( ' '.join(args), From 4fc5fa46ddde884b0ff2a98f8e86020006e2adde Mon Sep 17 00:00:00 2001 From: Milan Date: Thu, 1 Oct 2020 10:15:18 +0100 Subject: [PATCH 46/92] nondestructive reformating if input res odd number --- pype/plugins/global/publish/extract_review.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pype/plugins/global/publish/extract_review.py b/pype/plugins/global/publish/extract_review.py index 0bae1b2ddc..9f638712a7 100644 --- a/pype/plugins/global/publish/extract_review.py +++ b/pype/plugins/global/publish/extract_review.py @@ -453,6 +453,7 @@ class ExtractReview(pyblish.api.InstancePlugin): if audio_filters: all_args.append("-filter:a {}".format(",".join(audio_filters))) + all_args.append('-vf pad="width=ceil(iw/2)*2:height=ceil(ih/2)*2"') all_args.extend(output_args) return all_args From 945a6d88b9b5ec1b071ab73658693c3e7ea5c40f Mon Sep 17 00:00:00 2001 From: Milan Date: Thu, 1 Oct 2020 10:15:56 +0100 Subject: [PATCH 47/92] fix(burnin): space in path was crashing --- pype/scripts/otio_burnin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/scripts/otio_burnin.py b/pype/scripts/otio_burnin.py index 156896a759..6607726c73 100644 --- a/pype/scripts/otio_burnin.py +++ b/pype/scripts/otio_burnin.py @@ -15,7 +15,7 @@ ffprobe_path = pype.lib.get_ffmpeg_tool_path("ffprobe") FFMPEG = ( - '{} -loglevel panic -i %(input)s %(filters)s %(args)s%(output)s' + '{} -loglevel panic -i "%(input)s" %(filters)s %(args)s%(output)s' ).format(ffmpeg_path) FFPROBE = ( From dbf0881415b713cfb4e3c9d1b0bbf3e43ff95aca Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 1 Oct 2020 15:19:01 +0200 Subject: [PATCH 48/92] first video filter will add padding to input if has width or height with odd numbers --- pype/plugins/global/publish/extract_review.py | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/pype/plugins/global/publish/extract_review.py b/pype/plugins/global/publish/extract_review.py index 0bae1b2ddc..db81adfcf7 100644 --- a/pype/plugins/global/publish/extract_review.py +++ b/pype/plugins/global/publish/extract_review.py @@ -633,6 +633,26 @@ class ExtractReview(pyblish.api.InstancePlugin): input_width = int(input_data["width"]) input_height = int(input_data["height"]) + # Make sure input width and height is not an odd number + input_width_is_odd = bool(input_width % 2 != 0) + inputh_height_is_odd = bool(input_height % 2 != 0) + if input_width_is_odd or inputh_height_is_odd: + # Add padding to input and make sure this filter is at first place + filters.append("pad=width=ceil(iw/2)*2:height=ceil(ih/2)*2") + + # Change input width or height as first filter will change them + if input_width_is_odd: + self.log.info(( + "Converting input width from odd to even number. {} -> {}" + ).format(input_width, input_width + 1)) + input_width += 1 + + if inputh_height_is_odd: + self.log.info(( + "Converting input height from odd to even number. {} -> {}" + ).format(input_height, input_height + 1)) + input_height += 1 + self.log.debug("pixel_aspect: `{}`".format(pixel_aspect)) self.log.debug("input_width: `{}`".format(input_width)) self.log.debug("input_height: `{}`".format(input_height)) From 70780cbc5309e1191375ccdbaf577a981d6bd30e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 1 Oct 2020 15:19:41 +0200 Subject: [PATCH 49/92] also make sure output width or height does not contain odd numbers --- pype/plugins/global/publish/extract_review.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/pype/plugins/global/publish/extract_review.py b/pype/plugins/global/publish/extract_review.py index db81adfcf7..3febff0f16 100644 --- a/pype/plugins/global/publish/extract_review.py +++ b/pype/plugins/global/publish/extract_review.py @@ -674,6 +674,22 @@ class ExtractReview(pyblish.api.InstancePlugin): output_width = int(output_width) output_height = int(output_height) + # Make sure output width and height is not an odd number + # When this can happen: + # - if output definition has set width and height with odd number + # - `instance.data` contain width and height with odd numbeer + if output_width % 2 != 0: + self.log.warning(( + "Converting output width from odd to even number. {} -> {}" + ).format(output_width, output_width + 1)) + output_width += 1 + + if output_height % 2 != 0: + self.log.warning(( + "Converting output height from odd to even number. {} -> {}" + ).format(output_height, output_height + 1)) + output_height += 1 + self.log.debug( "Output resolution is {}x{}".format(output_width, output_height) ) From 15bb321b1f25cac721a10fc357f612c5d5d9ad46 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 1 Oct 2020 15:44:17 +0200 Subject: [PATCH 50/92] system widget can store environment fields --- pype/tools/settings/settings/widgets/base.py | 5 +++++ pype/tools/settings/settings/widgets/item_types.py | 3 +++ 2 files changed, 8 insertions(+) diff --git a/pype/tools/settings/settings/widgets/base.py b/pype/tools/settings/settings/widgets/base.py index 243a8448e5..9cff07ea90 100644 --- a/pype/tools/settings/settings/widgets/base.py +++ b/pype/tools/settings/settings/widgets/base.py @@ -47,6 +47,7 @@ class SystemWidget(QtWidgets.QWidget): self._ignore_value_changes = False self.input_fields = [] + self.environ_fields = [] scroll_widget = QtWidgets.QScrollArea(self) scroll_widget.setObjectName("GroupWidget") @@ -130,10 +131,14 @@ class SystemWidget(QtWidgets.QWidget): for input_field in self.input_fields: input_field.hierarchical_style_update() + def add_environ_field(self, input_field): + self.environ_fields.append(input_field) + def reset(self): reset_default_settings() self.input_fields.clear() + self.environ_fields.clear() while self.content_layout.count() != 0: widget = self.content_layout.itemAt(0).widget() self.content_layout.removeWidget(widget) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 4124c32ba8..9c55f5dc05 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -140,6 +140,9 @@ class SettingObject: """ return self._has_studio_override or self._parent.has_studio_override + def add_environ_field(self, input_field): + self._parent.add_environ_field(input_field) + @property def as_widget(self): """Item is used as widget in parent item. From 226ddab164af10a6c3447eca7dcf912a617e1748 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 1 Oct 2020 16:15:38 +0200 Subject: [PATCH 51/92] preparation for storing to separated environments settings --- .../settings/settings/widgets/item_types.py | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 9c55f5dc05..d1c29b6ae1 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -24,6 +24,8 @@ class SettingObject: # Will allow to show actions for the item type (disabled for proxies) else # item is skipped and try to trigger actions on it's parent. allow_actions = True + # If item can store environment values + allow_to_environment = False # All item types must have implemented Qt signal which is emitted when # it's or it's children value has changed, value_changed = None @@ -49,6 +51,9 @@ class SettingObject: self._as_widget = False self._is_group = False + # If value should be stored to environments + self._env_group_key = None + self._any_parent_as_widget = None self._any_parent_is_group = None @@ -84,6 +89,20 @@ class SettingObject: self._is_group = input_data.get("is_group", False) # TODO not implemented yet self._is_nullable = input_data.get("is_nullable", False) + self._env_group_key = input_data.get("env_group_key") + + if self.is_environ: + if not self.allow_to_environment: + raise TypeError(( + "Item {} does not allow to store environment values" + ).format(input_data["type"])) + + if self.as_widget: + raise TypeError(( + "Item is used as widget and" + " marked to store environments at the same time." + )) + self.add_environ_field(self) any_parent_as_widget = parent.as_widget if not any_parent_as_widget: @@ -140,9 +159,25 @@ class SettingObject: """ return self._has_studio_override or self._parent.has_studio_override + @property + def is_environ(self): + return self._env_group_key is not None + + @property + def env_group_key(self): + return self._env_group_key + def add_environ_field(self, input_field): self._parent.add_environ_field(input_field) + @property + def has_only_environ_children(self): + raise NotImplementedError( + "{} does not have implemented `has_only_environ_children`".format( + self + ) + ) + @property def as_widget(self): """Item is used as widget in parent item. @@ -270,8 +305,17 @@ class SettingObject: def config_value(self): """Output for saving changes or overrides.""" + if self.has_only_environ_children: + return {} return {self.key: self.item_value()} + def environment_value(self): + raise NotImplementedError( + "{} Method `set_studio_default` not implemented!".format( + repr(self) + ) + ) + @classmethod def style_state( cls, has_studio_override, is_invalid, is_overriden, is_modified @@ -667,6 +711,10 @@ class InputObject(SettingObject): self.value_changed.emit(self) + @property + def has_only_environ_children(self): + return self.is_environ + def studio_overrides(self): if ( not (self.as_widget or self.any_parent_as_widget) @@ -882,6 +930,7 @@ class NumberWidget(QtWidgets.QWidget, InputObject): class TextWidget(QtWidgets.QWidget, InputObject): default_input_value = "" value_changed = QtCore.Signal(object) + # allow_to_environment = True def __init__( self, input_data, parent, @@ -985,6 +1034,7 @@ class PathInputWidget(QtWidgets.QWidget, InputObject): class EnumeratorWidget(QtWidgets.QWidget, InputObject): default_input_value = True value_changed = QtCore.Signal(object) + # allow_to_environment = True def __init__( self, input_data, parent, @@ -1136,6 +1186,7 @@ class RawJsonInput(QtWidgets.QPlainTextEdit): class RawJsonWidget(QtWidgets.QWidget, InputObject): default_input_value = "{}" value_changed = QtCore.Signal(object) + allow_to_environment = True def __init__( self, input_data, parent, @@ -1182,6 +1233,12 @@ class RawJsonWidget(QtWidgets.QWidget, InputObject): self._is_invalid = self.input_field.has_invalid_value() return super(RawJsonWidget, self)._on_value_change(*args, **kwargs) + def environment_value(self): + output = {} + for key, value in self.item_value().items(): + output[key.upper()] = value + return output + def item_value(self): if self.is_invalid: return NOT_SET @@ -1372,6 +1429,7 @@ class ListItem(QtWidgets.QWidget, SettingObject): class ListWidget(QtWidgets.QWidget, InputObject): default_input_value = [] value_changed = QtCore.Signal(object) + # allow_to_environment = True def __init__( self, input_data, parent, @@ -1944,6 +2002,7 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): # TODO this is actually input field (do not care if is group or not) value_changed = QtCore.Signal(object) expand_in_grid = True + # allow_to_environment = True def __init__( self, input_data, parent, @@ -2209,6 +2268,8 @@ class DictWidget(QtWidgets.QWidget, SettingObject): self.input_fields = [] + self._has_only_environ_children = None + self.checkbox_widget = None self.checkbox_key = input_data.get("checkbox_key") @@ -2532,6 +2593,19 @@ class DictWidget(QtWidgets.QWidget, SettingObject): return True return False + @property + def has_only_environ_children(self): + if self._has_only_environ_children is None: + has_only_environ_children = True + if not self.is_environ: + for input_field in self.input_fields: + if not input_field.has_only_environ_children: + has_only_environ_children = False + break + + self._has_only_environ_children = has_only_environ_children + return self._has_only_environ_children + def get_invalid(self): output = [] for input_field in self.input_fields: @@ -2600,6 +2674,8 @@ class DictInvisible(QtWidgets.QWidget, SettingObject): self.initial_attributes(input_data, parent, as_widget) + self._has_only_environ_children = None + if self._is_group: raise TypeError("DictInvisible can't be marked as group input.") @@ -2660,6 +2736,19 @@ class DictInvisible(QtWidgets.QWidget, SettingObject): return True return False + @property + def has_only_environ_children(self): + if self._has_only_environ_children is None: + has_only_environ_children = True + if not self.is_environ: + for input_field in self.input_fields: + if not input_field.has_only_environ_children: + has_only_environ_children = False + break + + self._has_only_environ_children = has_only_environ_children + return self._has_only_environ_children + @property def child_modified(self): for input_field in self.input_fields: @@ -2840,6 +2929,7 @@ class PathWidget(QtWidgets.QWidget, SettingObject): "darwin": "MacOS", "linux": "Linux" } + # allow_to_environment = True def __init__( self, input_data, parent, @@ -3136,6 +3226,10 @@ class PathWidget(QtWidgets.QWidget, SettingObject): def set_as_overriden(self): self._is_overriden = True + @property + def has_only_environ_children(self): + return self.is_environ + @property def child_has_studio_override(self): return self.has_studio_override @@ -3200,6 +3294,8 @@ class DictFormWidget(QtWidgets.QWidget, SettingObject): self.initial_attributes(input_data, parent, as_widget) + self._has_only_environ_children = None + self._as_widget = False self._is_group = False @@ -3307,6 +3403,19 @@ class DictFormWidget(QtWidgets.QWidget, SettingObject): return True return False + @property + def has_only_environ_children(self): + if self._has_only_environ_children is None: + has_only_environ_children = True + if not self.is_environ: + for input_field in self.input_fields: + if not input_field.has_only_environ_children: + has_only_environ_children = False + break + + self._has_only_environ_children = has_only_environ_children + return self._has_only_environ_children + @property def child_modified(self): for input_field in self.input_fields: From 2a14a586c867ee3253ecb159e13a3be3cfe343b8 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 1 Oct 2020 17:05:34 +0200 Subject: [PATCH 52/92] fix(standalone): fixing space in path for thumbnailer --- pype/plugins/standalonepublisher/publish/extract_thumbnail.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pype/plugins/standalonepublisher/publish/extract_thumbnail.py b/pype/plugins/standalonepublisher/publish/extract_thumbnail.py index cddc9c3a82..5882775083 100644 --- a/pype/plugins/standalonepublisher/publish/extract_thumbnail.py +++ b/pype/plugins/standalonepublisher/publish/extract_thumbnail.py @@ -64,6 +64,7 @@ class ExtractThumbnailSP(pyblish.api.InstancePlugin): else: # Convert to jpeg if not yet full_input_path = os.path.join(thumbnail_repre["stagingDir"], file) + full_input_path = '"{}"'.format(full_input_path) self.log.info("input {}".format(full_input_path)) full_thumbnail_path = tempfile.mkstemp(suffix=".jpg")[1] From 4b754eed90982407f2c8abef066921682c06c9b2 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 1 Oct 2020 17:07:59 +0200 Subject: [PATCH 53/92] fix(global): removing reformating of odd resolution --- pype/plugins/global/publish/extract_review.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pype/plugins/global/publish/extract_review.py b/pype/plugins/global/publish/extract_review.py index 9f638712a7..0bae1b2ddc 100644 --- a/pype/plugins/global/publish/extract_review.py +++ b/pype/plugins/global/publish/extract_review.py @@ -453,7 +453,6 @@ class ExtractReview(pyblish.api.InstancePlugin): if audio_filters: all_args.append("-filter:a {}".format(",".join(audio_filters))) - all_args.append('-vf pad="width=ceil(iw/2)*2:height=ceil(ih/2)*2"') all_args.extend(output_args) return all_args From 2c6797e63a3cdeebfb047f16ef531c298aed6bbf Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 1 Oct 2020 17:11:06 +0200 Subject: [PATCH 54/92] fixed typo --- pype/plugins/global/publish/extract_review.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pype/plugins/global/publish/extract_review.py b/pype/plugins/global/publish/extract_review.py index 3febff0f16..318c843b80 100644 --- a/pype/plugins/global/publish/extract_review.py +++ b/pype/plugins/global/publish/extract_review.py @@ -635,8 +635,8 @@ class ExtractReview(pyblish.api.InstancePlugin): # Make sure input width and height is not an odd number input_width_is_odd = bool(input_width % 2 != 0) - inputh_height_is_odd = bool(input_height % 2 != 0) - if input_width_is_odd or inputh_height_is_odd: + input_height_is_odd = bool(input_height % 2 != 0) + if input_width_is_odd or input_height_is_odd: # Add padding to input and make sure this filter is at first place filters.append("pad=width=ceil(iw/2)*2:height=ceil(ih/2)*2") @@ -647,7 +647,7 @@ class ExtractReview(pyblish.api.InstancePlugin): ).format(input_width, input_width + 1)) input_width += 1 - if inputh_height_is_odd: + if input_height_is_odd: self.log.info(( "Converting input height from odd to even number. {} -> {}" ).format(input_height, input_height + 1)) From 227755669f444f903e6cf73d50d3944250c617e2 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 1 Oct 2020 17:57:21 +0200 Subject: [PATCH 55/92] METATADATA_KEY is object not type --- pype/tools/settings/settings/widgets/lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/tools/settings/settings/widgets/lib.py b/pype/tools/settings/settings/widgets/lib.py index cf2bd7f8af..f9c0532a97 100644 --- a/pype/tools/settings/settings/widgets/lib.py +++ b/pype/tools/settings/settings/widgets/lib.py @@ -11,7 +11,7 @@ class TypeToKlass: NOT_SET = type("NOT_SET", (), {"__bool__": lambda obj: False})() -METADATA_KEY = type("METADATA_KEY", (), {}) +METADATA_KEY = type("METADATA_KEY", (), {})() OVERRIDE_VERSION = 1 CHILD_OFFSET = 15 From 8a8aa0422db964e2ed1162699e1f5ad33badb917 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 1 Oct 2020 17:57:51 +0200 Subject: [PATCH 56/92] raise KeyError exception not just create --- pype/tools/settings/settings/widgets/lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/tools/settings/settings/widgets/lib.py b/pype/tools/settings/settings/widgets/lib.py index f9c0532a97..88e4015198 100644 --- a/pype/tools/settings/settings/widgets/lib.py +++ b/pype/tools/settings/settings/widgets/lib.py @@ -30,7 +30,7 @@ def convert_gui_data_to_overrides(data, first=True): if key == "groups": output[OVERRIDEN_KEY] = value else: - KeyError("Unknown metadata key \"{}\"".format(key)) + raise KeyError("Unknown metadata key \"{}\"".format(key)) for key, value in data.items(): output[key] = convert_gui_data_to_overrides(value, False) From 9afcdd3546b79ad25c81b223f94410fb37250ddc Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 1 Oct 2020 17:59:04 +0200 Subject: [PATCH 57/92] renamed variable OVERRIDEN_KEY to M_OVERRIDEN_KEY --- pype/settings/lib.py | 6 +++--- pype/tools/settings/settings/widgets/lib.py | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pype/settings/lib.py b/pype/settings/lib.py index 848bdeea92..f8b483cae9 100644 --- a/pype/settings/lib.py +++ b/pype/settings/lib.py @@ -6,7 +6,7 @@ import copy log = logging.getLogger(__name__) # Metadata keys for work with studio and project overrides -OVERRIDEN_KEY = "__overriden_keys__" +M_OVERRIDEN_KEY = "__overriden_keys__" # NOTE key popping not implemented yet POP_KEY = "__pop_key__" @@ -223,8 +223,8 @@ def project_anatomy_overrides(project_name): def merge_overrides(global_dict, override_dict): - if OVERRIDEN_KEY in override_dict: - overriden_keys = set(override_dict.pop(OVERRIDEN_KEY)) + if M_OVERRIDEN_KEY in override_dict: + overriden_keys = set(override_dict.pop(M_OVERRIDEN_KEY)) else: overriden_keys = set() diff --git a/pype/tools/settings/settings/widgets/lib.py b/pype/tools/settings/settings/widgets/lib.py index 88e4015198..479653e4d4 100644 --- a/pype/tools/settings/settings/widgets/lib.py +++ b/pype/tools/settings/settings/widgets/lib.py @@ -1,7 +1,7 @@ import os import json import copy -from pype.settings.lib import OVERRIDEN_KEY +from pype.settings.lib import M_OVERRIDEN_KEY, M_ENVIRONMENT_KEY from queue import Queue @@ -28,7 +28,7 @@ def convert_gui_data_to_overrides(data, first=True): metadata = data.pop(METADATA_KEY) for key, value in metadata.items(): if key == "groups": - output[OVERRIDEN_KEY] = value + output[M_OVERRIDEN_KEY] = value else: raise KeyError("Unknown metadata key \"{}\"".format(key)) @@ -42,8 +42,8 @@ def convert_overrides_to_gui_data(data, first=True): return data output = {} - if OVERRIDEN_KEY in data: - groups = data.pop(OVERRIDEN_KEY) + if M_OVERRIDEN_KEY in data: + groups = data.pop(M_OVERRIDEN_KEY) if METADATA_KEY not in output: output[METADATA_KEY] = {} output[METADATA_KEY]["groups"] = groups From 4903c09f40a870c802f84f0c070d70c1e8d7052f Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 1 Oct 2020 17:59:20 +0200 Subject: [PATCH 58/92] variable POP_KEY renamed to M_POP_KEY --- pype/settings/lib.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/settings/lib.py b/pype/settings/lib.py index f8b483cae9..4faf757198 100644 --- a/pype/settings/lib.py +++ b/pype/settings/lib.py @@ -8,7 +8,7 @@ log = logging.getLogger(__name__) # Metadata keys for work with studio and project overrides M_OVERRIDEN_KEY = "__overriden_keys__" # NOTE key popping not implemented yet -POP_KEY = "__pop_key__" +M_POP_KEY = "__pop_key__" # Folder where studio overrides are stored STUDIO_OVERRIDES_PATH = os.environ["PYPE_PROJECT_CONFIGS"] @@ -229,7 +229,7 @@ def merge_overrides(global_dict, override_dict): overriden_keys = set() for key, value in override_dict.items(): - if value == POP_KEY: + if value == M_POP_KEY: global_dict.pop(key) elif ( From ec445fcc2d28ea775c63227b265c3c03127aeabc Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 1 Oct 2020 17:59:32 +0200 Subject: [PATCH 59/92] added new M_ENVIRONMENT_KEY for storing environments --- pype/settings/lib.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pype/settings/lib.py b/pype/settings/lib.py index 4faf757198..b01c80038f 100644 --- a/pype/settings/lib.py +++ b/pype/settings/lib.py @@ -7,6 +7,8 @@ log = logging.getLogger(__name__) # Metadata keys for work with studio and project overrides M_OVERRIDEN_KEY = "__overriden_keys__" +# Metadata key for storing information about environments +M_ENVIRONMENT_KEY = "__environment_keys__" # NOTE key popping not implemented yet M_POP_KEY = "__pop_key__" From e6749a53f7f0285ab2b5ec767d9c6b1f463ec715 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 1 Oct 2020 18:00:11 +0200 Subject: [PATCH 60/92] added 2 functions for converting gui data for storing and oposite --- pype/tools/settings/settings/widgets/lib.py | 38 +++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/pype/tools/settings/settings/widgets/lib.py b/pype/tools/settings/settings/widgets/lib.py index 479653e4d4..e7ab00d7bd 100644 --- a/pype/tools/settings/settings/widgets/lib.py +++ b/pype/tools/settings/settings/widgets/lib.py @@ -16,6 +16,44 @@ OVERRIDE_VERSION = 1 CHILD_OFFSET = 15 +def convert_gui_data_with_metadata(data, ignored_keys=None): + if not data or not isinstance(data, dict): + return data + + if ignored_keys is None: + ignored_keys = tuple() + + output = {} + if METADATA_KEY in data: + metadata = data.pop(METADATA_KEY) + for key, value in metadata.items(): + if key in ignored_keys or key == "groups": + continue + + if key == "environments": + output[M_ENVIRONMENT_KEY] = value + else: + raise KeyError("Unknown metadata key \"{}\"".format(key)) + + for key, value in data.items(): + output[key] = convert_gui_data_with_metadata(value, ignored_keys) + return output + + +def convert_data_to_gui_data(data, first=True): + if not data or not isinstance(data, dict): + return data + + output = {} + if M_ENVIRONMENT_KEY in data: + data.pop(M_ENVIRONMENT_KEY) + + for key, value in data.items(): + output[key] = convert_data_to_gui_data(value, False) + + return output + + def convert_gui_data_to_overrides(data, first=True): if not data or not isinstance(data, dict): return data From 116c9469c8394ea55b03b2e1d56755c00bad1963 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 1 Oct 2020 18:01:04 +0200 Subject: [PATCH 61/92] fix not implemented message --- pype/tools/settings/settings/widgets/item_types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index d1c29b6ae1..bad1ce08fe 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -311,7 +311,7 @@ class SettingObject: def environment_value(self): raise NotImplementedError( - "{} Method `set_studio_default` not implemented!".format( + "{} Method `environment_value` not implemented!".format( repr(self) ) ) From 08554af5fdea03b76bf2a02bdb239848231f70bb Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 1 Oct 2020 18:01:38 +0200 Subject: [PATCH 62/92] bases use converting in and out data --- pype/tools/settings/settings/widgets/base.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/pype/tools/settings/settings/widgets/base.py b/pype/tools/settings/settings/widgets/base.py index 9cff07ea90..7cbe7c2f6f 100644 --- a/pype/tools/settings/settings/widgets/base.py +++ b/pype/tools/settings/settings/widgets/base.py @@ -219,7 +219,7 @@ class SystemWidget(QtWidgets.QWidget): all_values = _all_values # Skip first key - all_values = all_values["system"] + all_values = lib.convert_gui_data_with_metadata(all_values["system"]) prject_defaults_dir = os.path.join( DEFAULTS_DIR, SYSTEM_SETTINGS_KEY @@ -251,16 +251,19 @@ class SystemWidget(QtWidgets.QWidget): def _update_values(self): self.ignore_value_changes = True - default_values = { + default_values = lib.convert_data_to_gui_data({ "system": default_settings()[SYSTEM_SETTINGS_KEY] - } + }) for input_field in self.input_fields: input_field.update_default_values(default_values) if self._hide_studio_overrides: system_values = lib.NOT_SET else: - system_values = {"system": studio_system_settings()} + system_values = lib.convert_overrides_to_gui_data( + {"system": studio_system_settings()} + ) + for input_field in self.input_fields: input_field.update_studio_values(system_values) @@ -735,17 +738,20 @@ class ProjectWidget(QtWidgets.QWidget): def _update_values(self): self.ignore_value_changes = True - default_values = {"project": default_settings()} + default_values = default_values = lib.convert_data_to_gui_data( + {"project": default_settings()} + ) for input_field in self.input_fields: input_field.update_default_values(default_values) if self._hide_studio_overrides: studio_values = lib.NOT_SET else: - studio_values = {"project": { + studio_values = lib.convert_overrides_to_gui_data({"project": { PROJECT_SETTINGS_KEY: studio_project_settings(), PROJECT_ANATOMY_KEY: studio_project_anatomy() - }} + }}) + for input_field in self.input_fields: input_field.update_studio_values(studio_values) From e804c47214fb451f02fd988c1a5d0c74f59d17e8 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 1 Oct 2020 18:02:05 +0200 Subject: [PATCH 63/92] converting functions for overrides use new implemented conversions --- pype/tools/settings/settings/widgets/lib.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pype/tools/settings/settings/widgets/lib.py b/pype/tools/settings/settings/widgets/lib.py index e7ab00d7bd..31e4401ab0 100644 --- a/pype/tools/settings/settings/widgets/lib.py +++ b/pype/tools/settings/settings/widgets/lib.py @@ -61,6 +61,7 @@ def convert_gui_data_to_overrides(data, first=True): output = {} if first: output["__override_version__"] = OVERRIDE_VERSION + data = convert_gui_data_with_metadata(data, ("environments",)) if METADATA_KEY in data: metadata = data.pop(METADATA_KEY) @@ -79,6 +80,9 @@ def convert_overrides_to_gui_data(data, first=True): if not data or not isinstance(data, dict): return data + if first: + data = convert_data_to_gui_data(data) + output = {} if M_OVERRIDEN_KEY in data: groups = data.pop(M_OVERRIDEN_KEY) From d33de6856674304ba6008690c2906e7cf13e4b06 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 1 Oct 2020 18:02:48 +0200 Subject: [PATCH 64/92] removed `has_only_environ_children` --- .../settings/settings/widgets/item_types.py | 63 ------------------- 1 file changed, 63 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index bad1ce08fe..76b8504685 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -170,14 +170,6 @@ class SettingObject: def add_environ_field(self, input_field): self._parent.add_environ_field(input_field) - @property - def has_only_environ_children(self): - raise NotImplementedError( - "{} does not have implemented `has_only_environ_children`".format( - self - ) - ) - @property def as_widget(self): """Item is used as widget in parent item. @@ -305,8 +297,6 @@ class SettingObject: def config_value(self): """Output for saving changes or overrides.""" - if self.has_only_environ_children: - return {} return {self.key: self.item_value()} def environment_value(self): @@ -711,10 +701,6 @@ class InputObject(SettingObject): self.value_changed.emit(self) - @property - def has_only_environ_children(self): - return self.is_environ - def studio_overrides(self): if ( not (self.as_widget or self.any_parent_as_widget) @@ -2268,8 +2254,6 @@ class DictWidget(QtWidgets.QWidget, SettingObject): self.input_fields = [] - self._has_only_environ_children = None - self.checkbox_widget = None self.checkbox_key = input_data.get("checkbox_key") @@ -2593,19 +2577,6 @@ class DictWidget(QtWidgets.QWidget, SettingObject): return True return False - @property - def has_only_environ_children(self): - if self._has_only_environ_children is None: - has_only_environ_children = True - if not self.is_environ: - for input_field in self.input_fields: - if not input_field.has_only_environ_children: - has_only_environ_children = False - break - - self._has_only_environ_children = has_only_environ_children - return self._has_only_environ_children - def get_invalid(self): output = [] for input_field in self.input_fields: @@ -2674,8 +2645,6 @@ class DictInvisible(QtWidgets.QWidget, SettingObject): self.initial_attributes(input_data, parent, as_widget) - self._has_only_environ_children = None - if self._is_group: raise TypeError("DictInvisible can't be marked as group input.") @@ -2736,19 +2705,6 @@ class DictInvisible(QtWidgets.QWidget, SettingObject): return True return False - @property - def has_only_environ_children(self): - if self._has_only_environ_children is None: - has_only_environ_children = True - if not self.is_environ: - for input_field in self.input_fields: - if not input_field.has_only_environ_children: - has_only_environ_children = False - break - - self._has_only_environ_children = has_only_environ_children - return self._has_only_environ_children - @property def child_modified(self): for input_field in self.input_fields: @@ -3226,10 +3182,6 @@ class PathWidget(QtWidgets.QWidget, SettingObject): def set_as_overriden(self): self._is_overriden = True - @property - def has_only_environ_children(self): - return self.is_environ - @property def child_has_studio_override(self): return self.has_studio_override @@ -3294,8 +3246,6 @@ class DictFormWidget(QtWidgets.QWidget, SettingObject): self.initial_attributes(input_data, parent, as_widget) - self._has_only_environ_children = None - self._as_widget = False self._is_group = False @@ -3403,19 +3353,6 @@ class DictFormWidget(QtWidgets.QWidget, SettingObject): return True return False - @property - def has_only_environ_children(self): - if self._has_only_environ_children is None: - has_only_environ_children = True - if not self.is_environ: - for input_field in self.input_fields: - if not input_field.has_only_environ_children: - has_only_environ_children = False - break - - self._has_only_environ_children = has_only_environ_children - return self._has_only_environ_children - @property def child_modified(self): for input_field in self.input_fields: From 88bbe2d156f8fd542afc0e04bad554660aae57a3 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 1 Oct 2020 18:34:32 +0200 Subject: [PATCH 65/92] fix(ftrack): print not for p27 --- pype/plugins/ftrack/publish/integrate_hierarchy_ftrack.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pype/plugins/ftrack/publish/integrate_hierarchy_ftrack.py b/pype/plugins/ftrack/publish/integrate_hierarchy_ftrack.py index 0616382569..f6cb512518 100644 --- a/pype/plugins/ftrack/publish/integrate_hierarchy_ftrack.py +++ b/pype/plugins/ftrack/publish/integrate_hierarchy_ftrack.py @@ -2,7 +2,6 @@ import sys import six import pyblish.api from avalon import io -from pprint import pformat try: from pype.modules.ftrack.lib.avalon_sync import CUST_ATTR_AUTO_SYNC @@ -46,9 +45,6 @@ class IntegrateHierarchyToFtrack(pyblish.api.ContextPlugin): hierarchy_context = self.context.data["hierarchyContext"] - self.log.debug( - f"__ hierarchy_context: `{pformat(hierarchy_context)}`") - self.session = self.context.data["ftrackSession"] project_name = self.context.data["projectEntity"]["name"] query = 'Project where full_name is "{}"'.format(project_name) From 20b74ec2341fcf3e3dcc8399d8fddf956bec5598 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 1 Oct 2020 18:34:46 +0200 Subject: [PATCH 66/92] fix(nks): space in path crashing --- .../nukestudio/publish/extract_review_cutup_video.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/pype/plugins/nukestudio/publish/extract_review_cutup_video.py b/pype/plugins/nukestudio/publish/extract_review_cutup_video.py index a4fbf90bed..d1ce3675b1 100644 --- a/pype/plugins/nukestudio/publish/extract_review_cutup_video.py +++ b/pype/plugins/nukestudio/publish/extract_review_cutup_video.py @@ -76,7 +76,7 @@ class ExtractReviewCutUpVideo(pype.api.Extractor): # check if audio stream is in input video file ffprob_cmd = ( - "{ffprobe_path} -i {full_input_path} -show_streams " + "{ffprobe_path} -i \"{full_input_path}\" -show_streams " "-select_streams a -loglevel error" ).format(**locals()) self.log.debug("ffprob_cmd: {}".format(ffprob_cmd)) @@ -106,7 +106,7 @@ class ExtractReviewCutUpVideo(pype.api.Extractor): # try to get video native resolution data try: resolution_output = pype.api.subprocess(( - "{ffprobe_path} -i {full_input_path} -v error " + "{ffprobe_path} -i \"{full_input_path}\" -v error " "-select_streams v:0 -show_entries " "stream=width,height -of csv=s=x:p=0" ).format(**locals())) @@ -193,7 +193,7 @@ class ExtractReviewCutUpVideo(pype.api.Extractor): # append ffmpeg input video clip input_args.append("-ss {:0.2f}".format(start_sec)) input_args.append("-t {:0.2f}".format(duration_sec)) - input_args.append("-i {}".format(full_input_path)) + input_args.append("-i \"{}\"".format(full_input_path)) # add copy audio video codec if only shortening clip if ("_cut-bigger" in tags) and (not empty_add): @@ -203,8 +203,7 @@ class ExtractReviewCutUpVideo(pype.api.Extractor): output_args.append("-intra") # output filename - output_args.append("-y") - output_args.append(full_output_path) + output_args.append("-y \"{}\"".format(full_output_path)) mov_args = [ ffmpeg_path, From 9de4efc7c35fcd91557d865810db2e4585abfabc Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 1 Oct 2020 18:50:10 +0200 Subject: [PATCH 67/92] prepared method for merging metadata under same key --- .../settings/settings/widgets/item_types.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 76b8504685..6b35246db9 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -32,6 +32,24 @@ class SettingObject: # Item will expand to full width in grid layout expand_in_grid = False + def merge_metadata(self, current_metadata, new_metadata): + for key, value in new_metadata.items(): + if key not in current_metadata: + current_metadata[key] = value + + elif key == "groups": + current_metadata[key].extend(value) + + elif key == "environments": + for group_key, subvalue in value.items(): + if group_key not in current_metadata[key]: + current_metadata[key][group_key] = [] + current_metadata[key][group_key].extend(subvalue) + + else: + raise KeyError("Unknown metadata key: \"{}\"".format(key)) + return current_metadata + def _set_default_attributes(self): """Create and reset attributes required for all item types. From 2933eab903716e43ea4c44b0d5395dd2dd46ac02 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 1 Oct 2020 18:51:20 +0200 Subject: [PATCH 68/92] dicitonaries can merge metadata keys --- .../settings/settings/widgets/item_types.py | 106 ++++++++++-------- 1 file changed, 58 insertions(+), 48 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 6b35246db9..74eab078e1 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -2609,6 +2609,33 @@ class DictWidget(QtWidgets.QWidget, SettingObject): output.update(input_field.config_value()) return output + def _override_values(self, project_overrides): + values = {} + groups = [] + for input_field in self.input_fields: + if project_overrides: + value, is_group = input_field.overrides() + else: + value, is_group = input_field.studio_overrides() + if value is NOT_SET: + continue + + if METADATA_KEY in value and METADATA_KEY in values: + new_metadata = value.pop(METADATA_KEY) + values[METADATA_KEY] = self.merge_metadata( + values[METADATA_KEY], new_metadata + ) + + values.update(value) + if is_group: + groups.extend(value.keys()) + + if groups: + if METADATA_KEY not in values: + values[METADATA_KEY] = {} + values[METADATA_KEY]["groups"] = groups + return {self.key: values}, self.is_group + def studio_overrides(self): if ( not (self.as_widget or self.any_parent_as_widget) @@ -2616,34 +2643,12 @@ class DictWidget(QtWidgets.QWidget, SettingObject): and not self.child_has_studio_override ): return NOT_SET, False - - values = {} - groups = [] - for input_field in self.input_fields: - value, is_group = input_field.studio_overrides() - if value is not NOT_SET: - values.update(value) - if is_group: - groups.extend(value.keys()) - if groups: - values[METADATA_KEY] = {"groups": groups} - return {self.key: values}, self.is_group + return self._override_values(False) def overrides(self): if not self.is_overriden and not self.child_overriden: return NOT_SET, False - - values = {} - groups = [] - for input_field in self.input_fields: - value, is_group = input_field.overrides() - if value is not NOT_SET: - values.update(value) - if is_group: - groups.extend(value.keys()) - if groups: - values[METADATA_KEY] = {"groups": groups} - return {self.key: values}, self.is_group + return self._override_values(True) class DictInvisible(QtWidgets.QWidget, SettingObject): @@ -2858,6 +2863,33 @@ class DictInvisible(QtWidgets.QWidget, SettingObject): ) self._was_overriden = bool(self._is_overriden) + def _override_values(self, project_overrides): + values = {} + groups = [] + for input_field in self.input_fields: + if project_overrides: + value, is_group = input_field.overrides() + else: + value, is_group = input_field.studio_overrides() + if value is NOT_SET: + continue + + if METADATA_KEY in value and METADATA_KEY in values: + new_metadata = value.pop(METADATA_KEY) + values[METADATA_KEY] = self.merge_metadata( + values[METADATA_KEY], new_metadata + ) + + values.update(value) + if is_group: + groups.extend(value.keys()) + + if groups: + if METADATA_KEY not in values: + values[METADATA_KEY] = {} + values[METADATA_KEY]["groups"] = groups + return {self.key: values}, self.is_group + def studio_overrides(self): if ( not (self.as_widget or self.any_parent_as_widget) @@ -2865,34 +2897,12 @@ class DictInvisible(QtWidgets.QWidget, SettingObject): and not self.child_has_studio_override ): return NOT_SET, False - - values = {} - groups = [] - for input_field in self.input_fields: - value, is_group = input_field.studio_overrides() - if value is not NOT_SET: - values.update(value) - if is_group: - groups.extend(value.keys()) - if groups: - values[METADATA_KEY] = {"groups": groups} - return {self.key: values}, self.is_group + return self._override_values(False) def overrides(self): if not self.is_overriden and not self.child_overriden: return NOT_SET, False - - values = {} - groups = [] - for input_field in self.input_fields: - value, is_group = input_field.overrides() - if value is not NOT_SET: - values.update(value) - if is_group: - groups.extend(value.keys()) - if groups: - values[METADATA_KEY] = {"groups": groups} - return {self.key: values}, self.is_group + return self._override_values(True) class PathWidget(QtWidgets.QWidget, SettingObject): From 65d51d56a80638c5e0a6c79d6a1bbc67dd42093b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 1 Oct 2020 18:51:42 +0200 Subject: [PATCH 69/92] raw-json can store environments --- pype/tools/settings/settings/widgets/item_types.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 74eab078e1..70f0506a2b 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -1246,7 +1246,15 @@ class RawJsonWidget(QtWidgets.QWidget, InputObject): def item_value(self): if self.is_invalid: return NOT_SET - return self.input_field.json_value() + + value = self.input_field.json_value() + if self.is_environ: + value[METADATA_KEY] = { + "environments": { + self.env_group_key: list(value.keys()) + } + } + return value class ListItem(QtWidgets.QWidget, SettingObject): From 03bbc6e839eb8f71df7dab7d5701a0916bc6b777 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 1 Oct 2020 19:01:16 +0200 Subject: [PATCH 70/92] raw json can return env value/keys --- .../settings/settings/widgets/item_types.py | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 70f0506a2b..1adebddff7 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -1237,24 +1237,24 @@ class RawJsonWidget(QtWidgets.QWidget, InputObject): self._is_invalid = self.input_field.has_invalid_value() return super(RawJsonWidget, self)._on_value_change(*args, **kwargs) - def environment_value(self): - output = {} - for key, value in self.item_value().items(): - output[key.upper()] = value - return output - def item_value(self): if self.is_invalid: return NOT_SET value = self.input_field.json_value() - if self.is_environ: - value[METADATA_KEY] = { - "environments": { - self.env_group_key: list(value.keys()) - } + if not self.is_environ: + return value + + output = {} + for key, value in value.items(): + output[key.upper()] = value + + output[METADATA_KEY] = { + "environments": { + self.env_group_key: list(output.keys()) } - return value + } + return output class ListItem(QtWidgets.QWidget, SettingObject): From 484a35ae528dbf6e79610cc343591316098e5fad Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 1 Oct 2020 19:15:23 +0200 Subject: [PATCH 71/92] modified way how environments are loaded --- pype/settings/lib.py | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/pype/settings/lib.py b/pype/settings/lib.py index b01c80038f..7c1adcd8a7 100644 --- a/pype/settings/lib.py +++ b/pype/settings/lib.py @@ -113,6 +113,32 @@ def load_json(fpath): return {} +def find_environments(data): + if not data or not isinstance(data, dict): + return + + output = {} + if M_ENVIRONMENT_KEY in data: + metadata = data.pop(M_ENVIRONMENT_KEY) + for env_group_key, env_keys in metadata.items(): + output[env_group_key] = {} + for key in env_keys: + output[env_group_key][key] = data[key] + + for value in data.values(): + result = find_environments(value) + if not result: + continue + + for env_group_key, env_values in result.items(): + if env_group_key not in output: + output[env_group_key] = {} + + for env_key, env_value in env_values.items(): + output[env_group_key][env_key] = env_value + return output + + def subkey_merge(_dict, value, keys): key = keys.pop(0) if not keys: @@ -274,5 +300,5 @@ def project_settings(project_name): def environments(): default_values = default_settings()[ENVIRONMENTS_KEY] - studio_values = studio_system_settings() + studio_values = find_environments(system_settings()) return apply_overrides(default_values, studio_values) From 87df2ea1019505911e908ac862afc3761719af52 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 1 Oct 2020 19:30:18 +0200 Subject: [PATCH 72/92] added validation for env group keys --- pype/tools/settings/settings/widgets/lib.py | 55 +++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/pype/tools/settings/settings/widgets/lib.py b/pype/tools/settings/settings/widgets/lib.py index 31e4401ab0..6d8694dc7a 100644 --- a/pype/tools/settings/settings/widgets/lib.py +++ b/pype/tools/settings/settings/widgets/lib.py @@ -162,6 +162,21 @@ class SchemaDuplicatedKeys(Exception): super(SchemaDuplicatedKeys, self).__init__(msg) +class SchemaDuplicatedEnvGroupKeys(Exception): + def __init__(self, invalid): + items = [] + for key_path, keys in invalid.items(): + joined_keys = ", ".join([ + "\"{}\"".format(key) for key in keys + ]) + items.append("\"{}\" ({})".format(key_path, joined_keys)) + + msg = ( + "Schema items contain duplicated environment group keys. {}" + ).format(" || ".join(items)) + super(SchemaDuplicatedEnvGroupKeys, self).__init__(msg) + + def file_keys_from_schema(schema_data): output = [] item_type = schema_data["type"] @@ -319,10 +334,50 @@ def validate_keys_are_unique(schema_data, keys=None): raise SchemaDuplicatedKeys(invalid) +def validate_environment_groups_uniquenes( + schema_data, env_groups=None, keys=None +): + is_first = False + if env_groups is None: + is_first = True + env_groups = {} + keys = [] + + my_keys = copy.deepcopy(keys) + key = schema_data.get("key") + if key: + my_keys.append(key) + + env_group_key = schema_data.get("env_group_key") + if env_group_key: + if env_group_key not in env_groups: + env_groups[env_group_key] = [] + env_groups[env_group_key].append("/".join(my_keys)) + + children = schema_data.get("children") + if not children: + return + + for child in children: + validate_environment_groups_uniquenes( + child, env_groups, copy.deepcopy(my_keys) + ) + + if is_first: + invalid = {} + for env_group_key, key_paths in env_groups.items(): + if len(key_paths) > 1: + invalid[env_group_key] = key_paths + + if invalid: + raise SchemaDuplicatedEnvGroupKeys(invalid) + + def validate_schema(schema_data): validate_all_has_ending_file(schema_data) validate_is_group_is_unique_in_hierarchy(schema_data) validate_keys_are_unique(schema_data) + validate_environment_groups_uniquenes(schema_data) def gui_schema(subfolder, main_schema_name): From d7492a13c4c828f200b2de74af2ba5a521506a7d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 1 Oct 2020 19:36:43 +0200 Subject: [PATCH 73/92] added example of usage raw json for environments --- .../gui_schemas/system_schema/1_examples.json | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/1_examples.json b/pype/tools/settings/settings/gui_schemas/system_schema/1_examples.json index dd0f7f20d1..06ce23321a 100644 --- a/pype/tools/settings/settings/gui_schemas/system_schema/1_examples.json +++ b/pype/tools/settings/settings/gui_schemas/system_schema/1_examples.json @@ -5,6 +5,18 @@ "is_file": true, "children": [ { + "key": "env_group_test", + "label": "EnvGroup Test", + "type": "dict", + "children": [ + { + "key": "key_to_store_in_system_settings", + "label": "Testing environment group", + "type": "raw-json", + "env_group_key": "test_group" + } + ] + }, { "key": "dict_wrapper", "type": "dict-invisible", "children": [ From fb6061e80be838e113039083b3d00a824fb85a60 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 1 Oct 2020 19:44:02 +0200 Subject: [PATCH 74/92] changed logic of environment keys loading --- pype/settings/lib.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pype/settings/lib.py b/pype/settings/lib.py index 7c1adcd8a7..96c3829388 100644 --- a/pype/settings/lib.py +++ b/pype/settings/lib.py @@ -299,6 +299,8 @@ def project_settings(project_name): def environments(): - default_values = default_settings()[ENVIRONMENTS_KEY] - studio_values = find_environments(system_settings()) - return apply_overrides(default_values, studio_values) + envs = copy.deepcopy(default_settings()[ENVIRONMENTS_KEY]) + envs_from_system_settings = find_environments(system_settings()) + for env_group_key, values in envs_from_system_settings.items(): + envs[env_group_key] = values + return envs From de6daa65ca5e746dcf6dbc8ed8b90a7426846188 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 1 Oct 2020 19:52:37 +0200 Subject: [PATCH 75/92] cleanup --- pype/tools/settings/settings/widgets/item_types.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 1adebddff7..1127d611d7 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -934,7 +934,6 @@ class NumberWidget(QtWidgets.QWidget, InputObject): class TextWidget(QtWidgets.QWidget, InputObject): default_input_value = "" value_changed = QtCore.Signal(object) - # allow_to_environment = True def __init__( self, input_data, parent, @@ -1038,7 +1037,6 @@ class PathInputWidget(QtWidgets.QWidget, InputObject): class EnumeratorWidget(QtWidgets.QWidget, InputObject): default_input_value = True value_changed = QtCore.Signal(object) - # allow_to_environment = True def __init__( self, input_data, parent, @@ -1441,7 +1439,6 @@ class ListItem(QtWidgets.QWidget, SettingObject): class ListWidget(QtWidgets.QWidget, InputObject): default_input_value = [] value_changed = QtCore.Signal(object) - # allow_to_environment = True def __init__( self, input_data, parent, @@ -2014,7 +2011,6 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): # TODO this is actually input field (do not care if is group or not) value_changed = QtCore.Signal(object) expand_in_grid = True - # allow_to_environment = True def __init__( self, input_data, parent, @@ -2921,7 +2917,6 @@ class PathWidget(QtWidgets.QWidget, SettingObject): "darwin": "MacOS", "linux": "Linux" } - # allow_to_environment = True def __init__( self, input_data, parent, From 14ce8eb4428fead21aaafa8df55d90e5b6a5d26e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 1 Oct 2020 22:54:54 +0200 Subject: [PATCH 76/92] fix saving of environmentes to studio overrides --- pype/tools/settings/settings/widgets/lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/tools/settings/settings/widgets/lib.py b/pype/tools/settings/settings/widgets/lib.py index 6d8694dc7a..87f6554612 100644 --- a/pype/tools/settings/settings/widgets/lib.py +++ b/pype/tools/settings/settings/widgets/lib.py @@ -61,7 +61,7 @@ def convert_gui_data_to_overrides(data, first=True): output = {} if first: output["__override_version__"] = OVERRIDE_VERSION - data = convert_gui_data_with_metadata(data, ("environments",)) + data = convert_gui_data_with_metadata(data) if METADATA_KEY in data: metadata = data.pop(METADATA_KEY) From d27be1914da9a52074d8fdb1348c493de6dfd501 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 2 Oct 2020 10:06:02 +0200 Subject: [PATCH 77/92] feat(hiero): adding icon --- pype/resources/app_icons/hiero.png | Bin 0 -> 46366 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 pype/resources/app_icons/hiero.png diff --git a/pype/resources/app_icons/hiero.png b/pype/resources/app_icons/hiero.png new file mode 100644 index 0000000000000000000000000000000000000000..04bbf6265bb63f0615c2b98ab48891d03483f6b2 GIT binary patch literal 46366 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 literal 0 HcmV?d00001 From a1d3b8180701faae9270a8675197a613bd8480a4 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 2 Oct 2020 14:03:52 +0200 Subject: [PATCH 78/92] renamed `1_examples.json` to `example_schema.json` --- .../system_schema/{1_examples.json => example_schema.json} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename pype/tools/settings/settings/gui_schemas/system_schema/{1_examples.json => example_schema.json} (100%) diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/1_examples.json b/pype/tools/settings/settings/gui_schemas/system_schema/example_schema.json similarity index 100% rename from pype/tools/settings/settings/gui_schemas/system_schema/1_examples.json rename to pype/tools/settings/settings/gui_schemas/system_schema/example_schema.json From 39cf227447f2d82d8631dbae40ec3199a4afacfb Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 2 Oct 2020 14:09:48 +0200 Subject: [PATCH 79/92] added example template --- .../system_schema/example_template.json | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 pype/tools/settings/settings/gui_schemas/system_schema/example_template.json diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/example_template.json b/pype/tools/settings/settings/gui_schemas/system_schema/example_template.json new file mode 100644 index 0000000000..bf09ea6716 --- /dev/null +++ b/pype/tools/settings/settings/gui_schemas/system_schema/example_template.json @@ -0,0 +1,14 @@ +[ + { + "type": "rawj-json", + "label": "{env_label}", + "key": "{env_group_key}", + "env_group_key": "{env_group_key}" + }, { + "type": "path-widget", + "key": "executable_paths", + "label": "{executable_label} - Full paths to executables", + "multiplatform": true, + "multipath": true + } +] From be135af8cee02d6c762311c04bbbec07b5799446 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 2 Oct 2020 14:17:28 +0200 Subject: [PATCH 80/92] fixed example template --- .../gui_schemas/system_schema/example_template.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/example_template.json b/pype/tools/settings/settings/gui_schemas/system_schema/example_template.json index bf09ea6716..fb629c6170 100644 --- a/pype/tools/settings/settings/gui_schemas/system_schema/example_template.json +++ b/pype/tools/settings/settings/gui_schemas/system_schema/example_template.json @@ -1,13 +1,13 @@ [ { - "type": "rawj-json", - "label": "{env_label}", - "key": "{env_group_key}", - "env_group_key": "{env_group_key}" + "type": "raw-json", + "label": "{host_label} Environments", + "key": "{host_name}_environments", + "env_group_key": "{host_name}" }, { "type": "path-widget", - "key": "executable_paths", - "label": "{executable_label} - Full paths to executables", + "key": "{host_name}_executables", + "label": "{host_label} - Full paths to executables", "multiplatform": true, "multipath": true } From 3d07421dd95acc7a16dc2efa648a0943aecc79ec Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 2 Oct 2020 14:17:43 +0200 Subject: [PATCH 81/92] added schema template to example schema --- .../system_schema/example_schema.json | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/example_schema.json b/pype/tools/settings/settings/gui_schemas/system_schema/example_schema.json index 06ce23321a..ddd4fc7235 100644 --- a/pype/tools/settings/settings/gui_schemas/system_schema/example_schema.json +++ b/pype/tools/settings/settings/gui_schemas/system_schema/example_schema.json @@ -5,6 +5,27 @@ "is_file": true, "children": [ { + "type": "dict", + "key": "schema_templates", + "label": "Schema template examples", + "children": [ + { + "type": "schema_template", + "name": "example_template", + "template_data": { + "host_label": "Maya 2019", + "host_name": "maya_2019" + } + }, { + "type": "schema_template", + "name": "example_template", + "template_data": { + "host_label": "Maya 2020", + "host_name": "maya_2020" + } + } + ] + }, { "key": "env_group_test", "label": "EnvGroup Test", "type": "dict", From 42fa4c9871ddb9b7b07eab198f39fb6884ba7835 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 2 Oct 2020 14:18:58 +0200 Subject: [PATCH 82/92] schemas are also loaded in all subfolders --- pype/tools/settings/settings/widgets/lib.py | 27 ++++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/pype/tools/settings/settings/widgets/lib.py b/pype/tools/settings/settings/widgets/lib.py index 87f6554612..69f88df954 100644 --- a/pype/tools/settings/settings/widgets/lib.py +++ b/pype/tools/settings/settings/widgets/lib.py @@ -389,19 +389,22 @@ def gui_schema(subfolder, main_schema_name): ) loaded_schemas = {} - for filename in os.listdir(dirpath): - basename, ext = os.path.splitext(filename) - if ext != ".json": - continue + for root, _, filenames in os.walk(dirpath): + for filename in filenames: + basename, ext = os.path.splitext(filename) + if ext != ".json": + continue - filepath = os.path.join(dirpath, filename) - with open(filepath, "r") as json_stream: - try: - schema_data = json.load(json_stream) - except Exception as e: - raise Exception((f"Unable to parse JSON file {json_stream}\n " - f" - {e}")) from e - loaded_schemas[basename] = schema_data + filepath = os.path.join(root, filename) + with open(filepath, "r") as json_stream: + try: + schema_data = json.load(json_stream) + except Exception as exc: + raise Exception(( + f"Unable to parse JSON file {filepath}\n{exc}" + )) from exc + + loaded_schemas[basename] = schema_data main_schema = _fill_inner_schemas( loaded_schemas[main_schema_name], From c81d491198896290c7b0589f675514b7e58fd267 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 2 Oct 2020 14:19:35 +0200 Subject: [PATCH 83/92] if schema json contain list it is stored as template --- pype/tools/settings/settings/widgets/lib.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pype/tools/settings/settings/widgets/lib.py b/pype/tools/settings/settings/widgets/lib.py index 69f88df954..377f947d83 100644 --- a/pype/tools/settings/settings/widgets/lib.py +++ b/pype/tools/settings/settings/widgets/lib.py @@ -389,6 +389,7 @@ def gui_schema(subfolder, main_schema_name): ) loaded_schemas = {} + loaded_schema_templates = {} for root, _, filenames in os.walk(dirpath): for filename in filenames: basename, ext = os.path.splitext(filename) @@ -403,8 +404,10 @@ def gui_schema(subfolder, main_schema_name): raise Exception(( f"Unable to parse JSON file {filepath}\n{exc}" )) from exc - - loaded_schemas[basename] = schema_data + if isinstance(schema_data, list): + loaded_schema_templates[basename] = schema_data + else: + loaded_schemas[basename] = schema_data main_schema = _fill_inner_schemas( loaded_schemas[main_schema_name], From dd29192ffe970587b5288043ae7994911670ecd6 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 2 Oct 2020 14:20:48 +0200 Subject: [PATCH 84/92] implemented filling data of schema template --- pype/tools/settings/settings/widgets/lib.py | 55 +++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/pype/tools/settings/settings/widgets/lib.py b/pype/tools/settings/settings/widgets/lib.py index 377f947d83..7e4d5b126c 100644 --- a/pype/tools/settings/settings/widgets/lib.py +++ b/pype/tools/settings/settings/widgets/lib.py @@ -1,4 +1,5 @@ import os +import re import json import copy from pype.settings.lib import M_OVERRIDEN_KEY, M_ENVIRONMENT_KEY @@ -15,6 +16,8 @@ METADATA_KEY = type("METADATA_KEY", (), {})() OVERRIDE_VERSION = 1 CHILD_OFFSET = 15 +key_pattern = re.compile(r"(\{.*?[^{0]*\})") + def convert_gui_data_with_metadata(data, ignored_keys=None): if not data or not isinstance(data, dict): @@ -96,6 +99,58 @@ def convert_overrides_to_gui_data(data, first=True): return output +def _fill_schema_template_data( + template, template_data, required_keys=None, missing_keys=None +): + first = False + if required_keys is None: + first = True + required_keys = set() + missing_keys = set() + + if not template: + output = template + + elif isinstance(template, list): + output = [] + for item in template: + output.append(_fill_schema_template_data( + item, template_data, required_keys, missing_keys + )) + + elif isinstance(template, dict): + output = {} + for key, value in template.items(): + output[key] = _fill_schema_template_data( + value, template_data, required_keys, missing_keys + ) + + elif isinstance(template, str): + # TODO find much better way how to handle filling template data + for replacement_string in key_pattern.findall(template): + key = str(replacement_string[1:-1]) + required_keys.add(key) + if key not in template_data: + missing_keys.add(key) + continue + + value = template_data[key] + if replacement_string == template: + # Replace the value with value from templates data + # - with this is possible to set value with different type + template = value + else: + # Only replace the key in string + template = template.replace(replacement_string, value) + output = template + + else: + output = template + + if first and missing_keys: + raise SchemaTemplateMissingKeys(missing_keys, required_keys) + + return output def _fill_inner_schemas(schema_data, schema_collection): if schema_data["type"] == "schema": raise ValueError("First item in schema data can't be schema.") From 3a53f8a9441e471d989e5be22dff2fcfb10d6d92 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 2 Oct 2020 14:21:26 +0200 Subject: [PATCH 85/92] implemented function for handling `schema_template` item --- pype/tools/settings/settings/widgets/lib.py | 39 +++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/pype/tools/settings/settings/widgets/lib.py b/pype/tools/settings/settings/widgets/lib.py index 7e4d5b126c..aec28c949c 100644 --- a/pype/tools/settings/settings/widgets/lib.py +++ b/pype/tools/settings/settings/widgets/lib.py @@ -151,6 +151,45 @@ def _fill_schema_template_data( raise SchemaTemplateMissingKeys(missing_keys, required_keys) return output + + +def _fill_schema_template(child_data, schema_collection, schema_templates): + template_name = child_data["name"] + template = schema_templates.get(template_name) + if template is None: + if template_name in schema_collection: + raise KeyError(( + "Schema \"{}\" is used as `schema_template`" + ).format(template_name)) + raise KeyError("Schema template \"{}\" was not found".format( + template_name + )) + + template_data = child_data.get("template_data") or {} + try: + filled_child = _fill_schema_template_data( + template, template_data + ) + + except SchemaTemplateMissingKeys as exc: + raise SchemaTemplateMissingKeys( + exc.missing_keys, exc.required_keys, template_name + ) + + output = [] + for item in filled_child: + filled_item = _fill_inner_schemas( + item, schema_collection, schema_templates + ) + if filled_item["type"] == "schema_template": + output.extend(_fill_schema_template( + filled_item, schema_collection, schema_templates + )) + else: + output.append(filled_item) + return output + + def _fill_inner_schemas(schema_data, schema_collection): if schema_data["type"] == "schema": raise ValueError("First item in schema data can't be schema.") From 93f489d0c36cf52ef601e17c51271a918461bf94 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 2 Oct 2020 14:21:52 +0200 Subject: [PATCH 86/92] implemented exception for missing keys --- pype/tools/settings/settings/widgets/lib.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/pype/tools/settings/settings/widgets/lib.py b/pype/tools/settings/settings/widgets/lib.py index aec28c949c..a20bf0608f 100644 --- a/pype/tools/settings/settings/widgets/lib.py +++ b/pype/tools/settings/settings/widgets/lib.py @@ -215,6 +215,26 @@ def _fill_inner_schemas(schema_data, schema_collection): return schema_data +class SchemaTemplateMissingKeys(Exception): + def __init__(self, missing_keys, required_keys, template_name=None): + self.missing_keys = missing_keys + self.required_keys = required_keys + if template_name: + msg = f"Schema template \"{template_name}\" require more keys.\n" + else: + msg = "" + msg += "Required keys: {}\nMissing keys: {}".format( + self.join_keys(required_keys), + self.join_keys(missing_keys) + ) + super(SchemaTemplateMissingKeys, self).__init__(msg) + + def join_keys(self, keys): + return ", ".join([ + f"\"{key}\"" for key in keys + ]) + + class SchemaMissingFileInfo(Exception): def __init__(self, invalid): full_path_keys = [] From bb786b27db619e4107bb339921dcbb2eb3f011fe Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 2 Oct 2020 14:23:12 +0200 Subject: [PATCH 87/92] _fill_inner_schemas now can work with schema templates --- pype/tools/settings/settings/widgets/lib.py | 42 ++++++++++++++++----- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/pype/tools/settings/settings/widgets/lib.py b/pype/tools/settings/settings/widgets/lib.py index a20bf0608f..9624a0df6a 100644 --- a/pype/tools/settings/settings/widgets/lib.py +++ b/pype/tools/settings/settings/widgets/lib.py @@ -190,7 +190,7 @@ def _fill_schema_template(child_data, schema_collection, schema_templates): return output -def _fill_inner_schemas(schema_data, schema_collection): +def _fill_inner_schemas(schema_data, schema_collection, schema_templates): if schema_data["type"] == "schema": raise ValueError("First item in schema data can't be schema.") @@ -200,16 +200,37 @@ def _fill_inner_schemas(schema_data, schema_collection): new_children = [] for child in children: - if child["type"] != "schema": - new_child = _fill_inner_schemas(child, schema_collection) - new_children.append(new_child) + child_type = child["type"] + if child_type == "schema": + schema_name = child["name"] + if schema_name not in schema_collection: + if schema_name in schema_templates: + raise KeyError(( + "Schema template \"{}\" is used as `schema`" + ).format(schema_name)) + raise KeyError( + "Schema \"{}\" was not found".format(schema_name) + ) + + filled_child = _fill_inner_schemas( + schema_collection[schema_name], + schema_collection, + schema_templates + ) + + elif child_type == "schema_template": + for filled_child in _fill_schema_template( + child, schema_collection, schema_templates + ): + new_children.append(filled_child) continue - new_child = _fill_inner_schemas( - schema_collection[child["name"]], - schema_collection - ) - new_children.append(new_child) + else: + filled_child = _fill_inner_schemas( + child, schema_collection, schema_templates + ) + + new_children.append(filled_child) schema_data["children"] = new_children return schema_data @@ -525,7 +546,8 @@ def gui_schema(subfolder, main_schema_name): main_schema = _fill_inner_schemas( loaded_schemas[main_schema_name], - loaded_schemas + loaded_schemas, + loaded_schema_templates ) validate_schema(main_schema) return main_schema From 0c79c09a7e182fe23f325f6d353142ad0ac9af8c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 2 Oct 2020 14:30:41 +0200 Subject: [PATCH 88/92] added possibility of default values in template --- pype/tools/settings/settings/widgets/lib.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pype/tools/settings/settings/widgets/lib.py b/pype/tools/settings/settings/widgets/lib.py index 9624a0df6a..569e7bfbb7 100644 --- a/pype/tools/settings/settings/widgets/lib.py +++ b/pype/tools/settings/settings/widgets/lib.py @@ -108,6 +108,19 @@ def _fill_schema_template_data( required_keys = set() missing_keys = set() + _template = [] + default_values = {} + for item in template: + if isinstance(item, dict) and "__default_values__" in item: + default_values = item["__default_values__"] + else: + _template.append(item) + template = _template + + for key, value in default_values.items(): + if key not in template_data: + template_data[key] = value + if not template: output = template From af9be455d9d1a09c197efab502cc258e1bb0645d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 2 Oct 2020 14:31:02 +0200 Subject: [PATCH 89/92] added default values of templates ability to examples --- .../settings/gui_schemas/system_schema/example_schema.json | 3 ++- .../gui_schemas/system_schema/example_template.json | 6 +++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/example_schema.json b/pype/tools/settings/settings/gui_schemas/system_schema/example_schema.json index ddd4fc7235..09624006f9 100644 --- a/pype/tools/settings/settings/gui_schemas/system_schema/example_schema.json +++ b/pype/tools/settings/settings/gui_schemas/system_schema/example_schema.json @@ -14,7 +14,8 @@ "name": "example_template", "template_data": { "host_label": "Maya 2019", - "host_name": "maya_2019" + "host_name": "maya_2019", + "multiplath_executables": false } }, { "type": "schema_template", diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/example_template.json b/pype/tools/settings/settings/gui_schemas/system_schema/example_template.json index fb629c6170..29d3da26c9 100644 --- a/pype/tools/settings/settings/gui_schemas/system_schema/example_template.json +++ b/pype/tools/settings/settings/gui_schemas/system_schema/example_template.json @@ -1,5 +1,9 @@ [ { + "__default_values__": { + "multiplath_executables": true + } + }, { "type": "raw-json", "label": "{host_label} Environments", "key": "{host_name}_environments", @@ -8,7 +12,7 @@ "type": "path-widget", "key": "{host_name}_executables", "label": "{host_label} - Full paths to executables", - "multiplatform": true, + "multiplatform": "{multiplath_executables}", "multipath": true } ] From d241877895cee6c55a3239b5a6f82e5cdc2b9ce6 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 2 Oct 2020 15:02:46 +0200 Subject: [PATCH 90/92] added schema_template to readme --- pype/tools/settings/settings/README.md | 82 ++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/pype/tools/settings/settings/README.md b/pype/tools/settings/settings/README.md index e8b7fcdb57..974a9c932b 100644 --- a/pype/tools/settings/settings/README.md +++ b/pype/tools/settings/settings/README.md @@ -19,6 +19,7 @@ - GUI schemas are huge json files, to be able to split whole configuration into multiple schema there's type `schema` - system configuration schemas are stored in `~/tools/settings/settings/gui_schemas/system_schema/` and project configurations in `~/tools/settings/settings/gui_schemas/projects_schema/` - each schema name is filename of json file except extension (without ".json") +- if content is dictionary content will be used as `schema` else will be used as `schema_template` ### schema - can have only key `"children"` which is list of strings, each string should represent another schema (order matters) string represebts name of the schema @@ -31,6 +32,87 @@ } ``` +### schema_template +- allows to define schema "templates" to not duplicate same content multiple times +```javascript +// EXAMPLE json file content (filename: example_template.json) +[ + { + "__default_values__": { + "multiplath_executables": true + } + }, { + "type": "raw-json", + "label": "{host_label} Environments", + "key": "{host_name}_environments", + "env_group_key": "{host_name}" + }, { + "type": "path-widget", + "key": "{host_name}_executables", + "label": "{host_label} - Full paths to executables", + "multiplatform": "{multiplath_executables}", + "multipath": true + } +] +``` +```javascript +// EXAMPLE usage of the template in schema +{ + "type": "dict", + "key": "schema_template_examples", + "label": "Schema template examples", + "children": [ + { + "type": "schema_template", + // filename of template (example_template.json) + "name": "example_template", + "template_data": { + "host_label": "Maya 2019", + "host_name": "maya_2019", + "multiplath_executables": false + } + }, { + "type": "schema_template", + "name": "example_template", + "template_data": { + "host_label": "Maya 2020", + "host_name": "maya_2020" + } + } + ] +} +``` +- item in schema mush contain `"type"` and `"name"` keys but it is also expected that `"template_data"` will be entered too +- all items in the list, except `__default_values__`, will replace `schema_template` item in schema +- template may contain another template or schema +- it is expected that schema template will have unfilled fields as in example + - unfilled fields are allowed only in values of schema dictionary +```javascript +{ + ... + // Allowed + "key": "{to_fill}" + ... + // Not allowed + "{to_fill}": "value" + ... +} +``` +- Unfilled fields can be also used for non string values, in that case value must contain only one key and value for fill must contain right type. +```javascript +{ + ... + // Allowed + "multiplatform": "{executable_multiplatform}" + ... + // Not allowed + "multiplatform": "{executable_multiplatform}_enhanced_string" + ... +} +``` +- It is possible to define default values for unfilled fields to do so one of items in list must be dictionary with key `"__default_values__"` and value as dictionary with default key: values (as in example above). + + ## Basic Dictionary inputs - these inputs wraps another inputs into {key: value} relation From 03e8f647fd2799652355ec4532ccc30b030698dd Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 2 Oct 2020 15:03:16 +0200 Subject: [PATCH 91/92] modified example key --- .../settings/gui_schemas/system_schema/example_schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/example_schema.json b/pype/tools/settings/settings/gui_schemas/system_schema/example_schema.json index 09624006f9..814fe95d0c 100644 --- a/pype/tools/settings/settings/gui_schemas/system_schema/example_schema.json +++ b/pype/tools/settings/settings/gui_schemas/system_schema/example_schema.json @@ -6,7 +6,7 @@ "children": [ { "type": "dict", - "key": "schema_templates", + "key": "schema_template_exaples", "label": "Schema template examples", "children": [ { From 7364c6c29def5d779266b451069133d28898c7ab Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 2 Oct 2020 15:09:19 +0200 Subject: [PATCH 92/92] fixed typo --- pype/tools/settings/settings/README.md | 6 +++--- .../settings/gui_schemas/system_schema/example_schema.json | 2 +- .../gui_schemas/system_schema/example_template.json | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pype/tools/settings/settings/README.md b/pype/tools/settings/settings/README.md index 974a9c932b..4f4e9d305a 100644 --- a/pype/tools/settings/settings/README.md +++ b/pype/tools/settings/settings/README.md @@ -39,7 +39,7 @@ [ { "__default_values__": { - "multiplath_executables": true + "multipath_executables": true } }, { "type": "raw-json", @@ -50,7 +50,7 @@ "type": "path-widget", "key": "{host_name}_executables", "label": "{host_label} - Full paths to executables", - "multiplatform": "{multiplath_executables}", + "multiplatform": "{multipath_executables}", "multipath": true } ] @@ -69,7 +69,7 @@ "template_data": { "host_label": "Maya 2019", "host_name": "maya_2019", - "multiplath_executables": false + "multipath_executables": false } }, { "type": "schema_template", diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/example_schema.json b/pype/tools/settings/settings/gui_schemas/system_schema/example_schema.json index 814fe95d0c..7612e54116 100644 --- a/pype/tools/settings/settings/gui_schemas/system_schema/example_schema.json +++ b/pype/tools/settings/settings/gui_schemas/system_schema/example_schema.json @@ -15,7 +15,7 @@ "template_data": { "host_label": "Maya 2019", "host_name": "maya_2019", - "multiplath_executables": false + "multipath_executables": false } }, { "type": "schema_template", diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/example_template.json b/pype/tools/settings/settings/gui_schemas/system_schema/example_template.json index 29d3da26c9..48a3c955b9 100644 --- a/pype/tools/settings/settings/gui_schemas/system_schema/example_template.json +++ b/pype/tools/settings/settings/gui_schemas/system_schema/example_template.json @@ -1,7 +1,7 @@ [ { "__default_values__": { - "multiplath_executables": true + "multipath_executables": true } }, { "type": "raw-json", @@ -12,7 +12,7 @@ "type": "path-widget", "key": "{host_name}_executables", "label": "{host_label} - Full paths to executables", - "multiplatform": "{multiplath_executables}", + "multiplatform": "{multipath_executables}", "multipath": true } ]