From ae7a6555139219c0bb384192965055a0467184de Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 12 Jun 2020 13:03:10 +0300 Subject: [PATCH 001/813] fix(resolve): transition to new structure --- pype/hooks/resolve/prelaunch.py | 6 +- pype/hosts/resolve/lib.py | 2 +- pype/hosts/resolve/pipeline.py | 14 ++-- pype/hosts/resolve/preload_console.py | 2 +- .../resolve/utility_scripts/Pype_menu.py | 2 +- .../utility_scripts/__dev_compound_clip.py | 65 ------------------- .../resolve/utility_scripts/__test_pyblish.py | 57 ---------------- .../utility_scripts/__test_subprocess.py | 35 ---------- 8 files changed, 13 insertions(+), 170 deletions(-) delete mode 100644 pype/hosts/resolve/utility_scripts/__dev_compound_clip.py delete mode 100644 pype/hosts/resolve/utility_scripts/__test_pyblish.py delete mode 100644 pype/hosts/resolve/utility_scripts/__test_subprocess.py diff --git a/pype/hooks/resolve/prelaunch.py b/pype/hooks/resolve/prelaunch.py index bddeccf4a3..a122b87868 100644 --- a/pype/hooks/resolve/prelaunch.py +++ b/pype/hooks/resolve/prelaunch.py @@ -46,13 +46,14 @@ class ResolvePrelaunch(PypeHook): "`RESOLVE_UTILITY_SCRIPTS_DIR` or reinstall DaVinci Resolve. \n" f"RESOLVE_UTILITY_SCRIPTS_DIR: `{us_dir}`" ) + self.log.debug(f"-- us_dir: `{us_dir}`") # correctly format path for pre python script pre_py_sc = os.path.normpath(env.get("PRE_PYTHON_SCRIPT", "")) env["PRE_PYTHON_SCRIPT"] = pre_py_sc - + self.log.debug(f"-- pre_py_sc: `{pre_py_sc}`...") try: - __import__("pype.resolve") + __import__("pype.hosts.resolve") __import__("pyblish") except ImportError as e: @@ -62,6 +63,7 @@ class ResolvePrelaunch(PypeHook): else: # Resolve Setup integration importlib.reload(utils) + self.log.debug(f"-- utils.__file__: `{utils.__file__}`") utils.setup(env) return True diff --git a/pype/hosts/resolve/lib.py b/pype/hosts/resolve/lib.py index 2576136df5..2e759ab96c 100644 --- a/pype/hosts/resolve/lib.py +++ b/pype/hosts/resolve/lib.py @@ -1,6 +1,6 @@ import sys from .utils import get_resolve_module -from pypeapp import Logger +from pype.api import Logger log = Logger().get_logger(__name__, "resolve") diff --git a/pype/hosts/resolve/pipeline.py b/pype/hosts/resolve/pipeline.py index 967aed1436..8dfb94486b 100644 --- a/pype/hosts/resolve/pipeline.py +++ b/pype/hosts/resolve/pipeline.py @@ -6,23 +6,21 @@ import os from avalon.tools import workfiles from avalon import api as avalon from pyblish import api as pyblish -from pypeapp import Logger +import pype +from pype.api import Logger log = Logger().get_logger(__name__, "resolve") # self = sys.modules[__name__] AVALON_CONFIG = os.environ["AVALON_CONFIG"] -PARENT_DIR = os.path.dirname(__file__) -PACKAGE_DIR = os.path.dirname(PARENT_DIR) -PLUGINS_DIR = os.path.join(PACKAGE_DIR, "plugins") -LOAD_PATH = os.path.join(PLUGINS_DIR, "resolve", "load") -CREATE_PATH = os.path.join(PLUGINS_DIR, "resolve", "create") -INVENTORY_PATH = os.path.join(PLUGINS_DIR, "resolve", "inventory") +LOAD_PATH = os.path.join(pype.PLUGINS_DIR, "resolve", "load") +CREATE_PATH = os.path.join(pype.PLUGINS_DIR, "resolve", "create") +INVENTORY_PATH = os.path.join(pype.PLUGINS_DIR, "resolve", "inventory") PUBLISH_PATH = os.path.join( - PLUGINS_DIR, "resolve", "publish" + pype.PLUGINS_DIR, "resolve", "publish" ).replace("\\", "/") AVALON_CONTAINERS = ":AVALON_CONTAINERS" diff --git a/pype/hosts/resolve/preload_console.py b/pype/hosts/resolve/preload_console.py index ea1bd4f180..58975777b8 100644 --- a/pype/hosts/resolve/preload_console.py +++ b/pype/hosts/resolve/preload_console.py @@ -1,7 +1,7 @@ #!/usr/bin/env python import time from pype.hosts.resolve.utils import get_resolve_module -from pypeapp import Logger +from pype.api import Logger log = Logger().get_logger(__name__, "resolve") diff --git a/pype/hosts/resolve/utility_scripts/Pype_menu.py b/pype/hosts/resolve/utility_scripts/Pype_menu.py index 1f5cd36277..230a7a80f0 100644 --- a/pype/hosts/resolve/utility_scripts/Pype_menu.py +++ b/pype/hosts/resolve/utility_scripts/Pype_menu.py @@ -3,7 +3,7 @@ import sys import avalon.api as avalon import pype -from pypeapp import Logger +from pype.api import Logger log = Logger().get_logger(__name__) diff --git a/pype/hosts/resolve/utility_scripts/__dev_compound_clip.py b/pype/hosts/resolve/utility_scripts/__dev_compound_clip.py deleted file mode 100644 index fe47008c70..0000000000 --- a/pype/hosts/resolve/utility_scripts/__dev_compound_clip.py +++ /dev/null @@ -1,65 +0,0 @@ -#! python3 -# -*- coding: utf-8 -*- - - -# convert clip def -def convert_clip(timeline=None): - """Convert timeline item (clip) into compound clip pype container - - Args: - timeline (MediaPool.Timeline): Object of timeline - - Returns: - bool: `True` if success - - Raises: - Exception: description - - """ - pass - - -# decorator function create_current_timeline_media_bin() -def create_current_timeline_media_bin(timeline=None): - """Convert timeline item (clip) into compound clip pype container - - Args: - timeline (MediaPool.Timeline): Object of timeline - - Returns: - bool: `True` if success - - Raises: - Exception: description - - """ - pass - - -# decorator function get_selected_track_items() -def get_selected_track_items(): - """Convert timeline item (clip) into compound clip pype container - - Args: - timeline (MediaPool.Timeline): Object of timeline - - Returns: - bool: `True` if success - - Raises: - Exception: description - - """ - print("testText") - - -# PypeCompoundClip() class -class PypeCompoundClip(object): - """docstring for .""" - - def __init__(self, arg): - super(self).__init__() - self.arg = arg - - def create_compound_clip(self): - pass diff --git a/pype/hosts/resolve/utility_scripts/__test_pyblish.py b/pype/hosts/resolve/utility_scripts/__test_pyblish.py deleted file mode 100644 index a6fe991025..0000000000 --- a/pype/hosts/resolve/utility_scripts/__test_pyblish.py +++ /dev/null @@ -1,57 +0,0 @@ -import os -import sys -import pype -import importlib -import pyblish.api -import pyblish.util -import avalon.api -from avalon.tools import publish -from pypeapp import Logger - -log = Logger().get_logger(__name__) - - -def main(env): - # Registers pype's Global pyblish plugins - pype.install() - - # Register Host (and it's pyblish plugins) - host_name = env["AVALON_APP"] - # TODO not sure if use "pype." or "avalon." for host import - host_import_str = f"pype.{host_name}" - - try: - host_module = importlib.import_module(host_import_str) - except ModuleNotFoundError: - log.error(( - f"Host \"{host_name}\" can't be imported." - f" Import string \"{host_import_str}\" failed." - )) - return False - - avalon.api.install(host_module) - - # Register additional paths - addition_paths_str = env.get("PUBLISH_PATHS") or "" - addition_paths = addition_paths_str.split(os.pathsep) - for path in addition_paths: - path = os.path.normpath(path) - if not os.path.exists(path): - continue - - pyblish.api.register_plugin_path(path) - - # Register project specific plugins - project_name = os.environ["AVALON_PROJECT"] - project_plugins_paths = env.get("PYPE_PROJECT_PLUGINS") or "" - for path in project_plugins_paths.split(os.pathsep): - plugin_path = os.path.join(path, project_name, "plugins") - if os.path.exists(plugin_path): - pyblish.api.register_plugin_path(plugin_path) - - return publish.show() - - -if __name__ == "__main__": - result = main(os.environ) - sys.exit(not bool(result)) diff --git a/pype/hosts/resolve/utility_scripts/__test_subprocess.py b/pype/hosts/resolve/utility_scripts/__test_subprocess.py deleted file mode 100644 index bdc57bbf00..0000000000 --- a/pype/hosts/resolve/utility_scripts/__test_subprocess.py +++ /dev/null @@ -1,35 +0,0 @@ -#! python3 -# -*- coding: utf-8 -*- -import os -from pypeapp import execute, Logger -from pype.hosts.resolve.utils import get_resolve_module - -log = Logger().get_logger("Resolve") - -CURRENT_DIR = os.getenv("RESOLVE_UTILITY_SCRIPTS_DIR", "") -python_dir = os.getenv("PYTHON36_RESOLVE") -python_exe = os.path.normpath( - os.path.join(python_dir, "python.exe") -) - -resolve = get_resolve_module() -PM = resolve.GetProjectManager() -P = PM.GetCurrentProject() - -log.info(P.GetName()) - - -# ______________________________________________________ -# testing subprocessing Scripts -testing_py = os.path.join(CURRENT_DIR, "ResolvePageSwitcher.py") -testing_py = os.path.normpath(testing_py) -log.info(f"Testing path to script: `{testing_py}`") - -returncode = execute( - [python_exe, os.path.normpath(testing_py)], - env=dict(os.environ) -) - -# Check if output file exists -if returncode != 0: - log.error("Executing failed!") From a4d3b40136175ae299ae0306da15c62507e8d998 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 12 Jun 2020 13:03:38 +0300 Subject: [PATCH 002/813] feat(resolve): adding currentFile to collect project --- pype/plugins/resolve/publish/collect_host.py | 17 ----------- .../resolve/publish/collect_project.py | 29 +++++++++++++++++++ 2 files changed, 29 insertions(+), 17 deletions(-) delete mode 100644 pype/plugins/resolve/publish/collect_host.py create mode 100644 pype/plugins/resolve/publish/collect_project.py diff --git a/pype/plugins/resolve/publish/collect_host.py b/pype/plugins/resolve/publish/collect_host.py deleted file mode 100644 index a5c4b0936c..0000000000 --- a/pype/plugins/resolve/publish/collect_host.py +++ /dev/null @@ -1,17 +0,0 @@ -import pyblish.api -from pype.hosts.resolve.utils import get_resolve_module - - -class CollectProject(pyblish.api.ContextPlugin): - """Collect Project object""" - - order = pyblish.api.CollectorOrder - 0.1 - label = "Collect Project" - hosts = ["resolve"] - - def process(self, context): - resolve = get_resolve_module() - PM = resolve.GetProjectManager() - P = PM.GetCurrentProject() - - self.log.info(P.GetName()) diff --git a/pype/plugins/resolve/publish/collect_project.py b/pype/plugins/resolve/publish/collect_project.py new file mode 100644 index 0000000000..aa57f93619 --- /dev/null +++ b/pype/plugins/resolve/publish/collect_project.py @@ -0,0 +1,29 @@ +import os +import pyblish.api +from pype.hosts.resolve.utils import get_resolve_module + + +class CollectProject(pyblish.api.ContextPlugin): + """Collect Project object""" + + order = pyblish.api.CollectorOrder - 0.1 + label = "Collect Project" + hosts = ["resolve"] + + def process(self, context): + exported_projet_ext = ".drp" + current_dir = os.getenv("AVALON_WORKDIR") + resolve = get_resolve_module() + PM = resolve.GetProjectManager() + P = PM.GetCurrentProject() + name = P.GetName() + + fname = name + exported_projet_ext + current_file = os.path.join(current_dir, fname) + normalised = os.path.normpath(current_file) + + context.data["project"] = P + context.data["currentFile"] = normalised + + self.log.info(name) + self.log.debug(normalised) From 700c62617814d3e9e1c1026bf3423d7cf620eee3 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 17 Jun 2020 17:13:17 +0300 Subject: [PATCH 003/813] feat(resolve): adding create shot clip --- pype/plugins/resolve/create/create_shot_clip.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 pype/plugins/resolve/create/create_shot_clip.py diff --git a/pype/plugins/resolve/create/create_shot_clip.py b/pype/plugins/resolve/create/create_shot_clip.py new file mode 100644 index 0000000000..97d5663922 --- /dev/null +++ b/pype/plugins/resolve/create/create_shot_clip.py @@ -0,0 +1,15 @@ +import avalon.api +from pype.hosts import resolve + + +class CreateShotClip(avalon.api.Creator): + """Publishable clip""" + + label = "Shot" + family = "clip" + icon = "film" + defaults = ["Main"] + + def process(self): + project = resolve.get_current_project() + self.log.info(f"Project name: {project.GetName()}") From 37fff09ecdafb1d4eb3ea8af070d387415c41ec9 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 17 Jun 2020 18:27:51 +0300 Subject: [PATCH 004/813] feat(resolve): wip create plugins --- pype/hosts/resolve/__init__.py | 38 ++++++++++---- pype/hosts/resolve/action.py | 6 +-- pype/hosts/resolve/lib.py | 19 +++++-- pype/hosts/resolve/pipeline.py | 51 +++++++++++++++++-- pype/hosts/resolve/plugin.py | 21 +++++++- pype/hosts/resolve/utility_scripts/test.py | 19 +++++++ pype/hosts/resolve/utils.py | 18 +++---- pype/hosts/resolve/workio.py | 5 +- .../resolve/create/create_shot_clip.py | 10 ++-- 9 files changed, 150 insertions(+), 37 deletions(-) create mode 100644 pype/hosts/resolve/utility_scripts/test.py diff --git a/pype/hosts/resolve/__init__.py b/pype/hosts/resolve/__init__.py index 72d6314b5e..b7e6c7dee3 100644 --- a/pype/hosts/resolve/__init__.py +++ b/pype/hosts/resolve/__init__.py @@ -1,17 +1,29 @@ +from .utils import ( + setup, + get_resolve_module +) + from .pipeline import ( install, uninstall, ls, containerise, publish, - launch_workfiles_app + launch_workfiles_app, + maintained_selection ) -from .utils import ( - setup, - get_resolve_module +from .lib import ( + get_project_manager, + get_current_project, + get_current_sequence, + set_project_manager_to_folder_name ) +from .menu import launch_pype_menu + +from .plugin import Creator + from .workio import ( open_file, save_file, @@ -21,12 +33,7 @@ from .workio import ( work_root ) -from .lib import ( - get_project_manager, - set_project_manager_to_folder_name -) - -from .menu import launch_pype_menu +bmd = None __all__ = [ # pipeline @@ -37,6 +44,7 @@ __all__ = [ "reload_pipeline", "publish", "launch_workfiles_app", + "maintained_selection", # utils "setup", @@ -44,16 +52,24 @@ __all__ = [ # lib "get_project_manager", + "get_current_project", + "get_current_sequence", "set_project_manager_to_folder_name", # menu "launch_pype_menu", + # plugin + "Creator", + # workio "open_file", "save_file", "current_file", "has_unsaved_changes", "file_extensions", - "work_root" + "work_root", + + # singleton with black magic resolve module + "bmd" ] diff --git a/pype/hosts/resolve/action.py b/pype/hosts/resolve/action.py index 31830937c1..a9803cef4e 100644 --- a/pype/hosts/resolve/action.py +++ b/pype/hosts/resolve/action.py @@ -21,9 +21,9 @@ class SelectInvalidAction(pyblish.api.Action): def process(self, context, plugin): try: - from pype.hosts.resolve.utils import get_resolve_module - resolve = get_resolve_module() - self.log.debug(resolve) + from . import get_project_manager + pm = get_project_manager() + self.log.debug(pm) except ImportError: raise ImportError("Current host is not Resolve") diff --git a/pype/hosts/resolve/lib.py b/pype/hosts/resolve/lib.py index 2e759ab96c..25e177eb1c 100644 --- a/pype/hosts/resolve/lib.py +++ b/pype/hosts/resolve/lib.py @@ -1,5 +1,4 @@ import sys -from .utils import get_resolve_module from pype.api import Logger log = Logger().get_logger(__name__, "resolve") @@ -9,12 +8,26 @@ self.pm = None def get_project_manager(): + from . import bmd if not self.pm: - resolve = get_resolve_module() - self.pm = resolve.GetProjectManager() + self.pm = bmd.GetProjectManager() return self.pm +def get_current_project(): + # initialize project manager + get_project_manager() + + return self.pm.GetCurrentProject() + + +def get_current_sequence(): + # get current project + project = get_current_project() + + return project.GetCurrentTimeline() + + def set_project_manager_to_folder_name(folder_name): """ Sets context of Project manager to given folder by name. diff --git a/pype/hosts/resolve/pipeline.py b/pype/hosts/resolve/pipeline.py index 8dfb94486b..91d06da274 100644 --- a/pype/hosts/resolve/pipeline.py +++ b/pype/hosts/resolve/pipeline.py @@ -2,7 +2,7 @@ Basic avalon integration """ import os -# import sys +import contextlib from avalon.tools import workfiles from avalon import api as avalon from pyblish import api as pyblish @@ -11,8 +11,6 @@ from pype.api import Logger log = Logger().get_logger(__name__, "resolve") -# self = sys.modules[__name__] - AVALON_CONFIG = os.environ["AVALON_CONFIG"] LOAD_PATH = os.path.join(pype.PLUGINS_DIR, "resolve", "load") @@ -38,11 +36,13 @@ def install(): See the Maya equivalent for inspiration on how to implement this. """ + from . import get_resolve_module # Disable all families except for the ones we explicitly want to see family_states = [ "imagesequence", - "mov" + "mov", + "clip" ] avalon.data["familiesStateDefault"] = False avalon.data["familiesStateToggled"] = family_states @@ -57,6 +57,8 @@ def install(): avalon.register_plugin_path(avalon.Creator, CREATE_PATH) avalon.register_plugin_path(avalon.InventoryAction, INVENTORY_PATH) + get_resolve_module() + def uninstall(): """Uninstall all tha was installed @@ -138,3 +140,44 @@ def publish(parent): """Shorthand to publish from within host""" from avalon.tools import publish return publish.show(parent) + + +@contextlib.contextmanager +def maintained_selection(): + """Maintain selection during context + + Example: + >>> with maintained_selection(): + ... node['selected'].setValue(True) + >>> print(node['selected'].value()) + False + """ + from . import get_current_project + project = get_current_project() + nodes = [] + previous_selection = None + + # deselect all nodes + reset_selection() + + try: + # do the operation + yield + finally: + # unselect all selection in case there is some + reset_selection() + # and select all previously selected nodes + if previous_selection: + try: + for n in nodes: + if n not in previous_selection: + continue + n['selected'].setValue(True) + except ValueError as e: + log.warning(e) + + +def reset_selection(): + """Deselect all selected nodes + """ + pass diff --git a/pype/hosts/resolve/plugin.py b/pype/hosts/resolve/plugin.py index 628d4bdb26..513b9984f4 100644 --- a/pype/hosts/resolve/plugin.py +++ b/pype/hosts/resolve/plugin.py @@ -1,6 +1,7 @@ from avalon import api -# from pype.hosts.resolve import lib as drlib +from pype.hosts import resolve from avalon.vendor import qargparse +from pype.api import config def get_reference_node_parents(ref): @@ -73,3 +74,21 @@ class SequenceLoader(api.Loader): """Remove an existing `container` """ pass + + +class Creator(api.Creator): + """Creator class wrapper + """ + marker_color = "Purple" + + def __init__(self, *args, **kwargs): + super(Creator, self).__init__(*args, **kwargs) + self.presets = config.get_presets()['plugins']["resolve"][ + "create"].get(self.__class__.__name__, {}) + + # adding basic current context resolve objects + self.project = resolve.get_current_project() + self.sequence = resolve.get_current_sequence() + + # TODO: make sure no duplicity of subsets are in workfile + return diff --git a/pype/hosts/resolve/utility_scripts/test.py b/pype/hosts/resolve/utility_scripts/test.py new file mode 100644 index 0000000000..4c43507e62 --- /dev/null +++ b/pype/hosts/resolve/utility_scripts/test.py @@ -0,0 +1,19 @@ +#! python3 +import sys +from pype.api import Logger + +log = Logger().get_logger(__name__) + + +def main(): + import pype.hosts.resolve as bmdvr + bm = bmdvr.utils.get_resolve_module() + log.info(f"blackmagicmodule: {bm}") + +import DaVinciResolveScript as bmd +print(f"_>> bmd.scriptapp(Resolve): {bmd.scriptapp('Resolve')}") + + +if __name__ == "__main__": + result = main() + sys.exit(not bool(result)) diff --git a/pype/hosts/resolve/utils.py b/pype/hosts/resolve/utils.py index f5add53a6b..74ce2dc98f 100644 --- a/pype/hosts/resolve/utils.py +++ b/pype/hosts/resolve/utils.py @@ -9,18 +9,16 @@ import os import shutil from pypeapp import Logger - log = Logger().get_logger(__name__, "resolve") -self = sys.modules[__name__] -self.bmd = None - def get_resolve_module(): + from pype.hosts import resolve # dont run if already loaded - if self.bmd: - return self.bmd - + if resolve.bmd: + log.info(("resolve module is assigned to " + f"`pype.hosts.resolve.bmd`: {resolve.bmd}")) + return resolve.bmd try: """ The PYTHONPATH needs to be set correctly for this import @@ -71,8 +69,10 @@ def get_resolve_module(): ) sys.exit() # assign global var and return - self.bmd = bmd.scriptapp("Resolve") - return self.bmd + bmd = bmd.scriptapp("Resolve") + resolve.bmd = bmd + log.info(("Assigning resolve module to " + f"`pype.hosts.resolve.bmd`: {resolve.bmd}")) def _sync_utility_scripts(env=None): diff --git a/pype/hosts/resolve/workio.py b/pype/hosts/resolve/workio.py index e1e30a8734..9d8d320a3c 100644 --- a/pype/hosts/resolve/workio.py +++ b/pype/hosts/resolve/workio.py @@ -2,8 +2,9 @@ import os from pypeapp import Logger -from .lib import ( +from . import ( get_project_manager, + get_current_project, set_project_manager_to_folder_name ) @@ -26,7 +27,7 @@ def save_file(filepath): pm = get_project_manager() file = os.path.basename(filepath) fname, _ = os.path.splitext(file) - project = pm.GetCurrentProject() + project = get_current_project() name = project.GetName() if "Untitled Project" not in name: diff --git a/pype/plugins/resolve/create/create_shot_clip.py b/pype/plugins/resolve/create/create_shot_clip.py index 97d5663922..43a8ab0cbd 100644 --- a/pype/plugins/resolve/create/create_shot_clip.py +++ b/pype/plugins/resolve/create/create_shot_clip.py @@ -1,8 +1,7 @@ -import avalon.api from pype.hosts import resolve -class CreateShotClip(avalon.api.Creator): +class CreateShotClip(resolve.Creator): """Publishable clip""" label = "Shot" @@ -10,6 +9,9 @@ class CreateShotClip(avalon.api.Creator): icon = "film" defaults = ["Main"] + presets = None + def process(self): - project = resolve.get_current_project() - self.log.info(f"Project name: {project.GetName()}") + print(f"Project name: {self.project.GetName()}") + print(f"Sequence name: {self.sequence.GetName()}") + print(self.presets) From 48c163a3319429ba63eb068c928fc77509c4f88c Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 22 Jun 2020 19:30:17 +0300 Subject: [PATCH 005/813] feat(resolve): updating resolve integration wip --- pype/hosts/resolve/__init__.py | 12 +- pype/hosts/resolve/lib.py | 205 +++++++++++++++++- pype/hosts/resolve/pipeline.py | 20 +- pype/hosts/resolve/plugin.py | 8 +- pype/hosts/resolve/utility_scripts/test.py | 4 +- pype/hosts/resolve/utils.py | 16 +- .../resolve/create/create_shot_clip.py | 27 ++- 7 files changed, 255 insertions(+), 37 deletions(-) diff --git a/pype/hosts/resolve/__init__.py b/pype/hosts/resolve/__init__.py index b7e6c7dee3..5f651f4b29 100644 --- a/pype/hosts/resolve/__init__.py +++ b/pype/hosts/resolve/__init__.py @@ -17,6 +17,9 @@ from .lib import ( get_project_manager, get_current_project, get_current_sequence, + get_current_track_items, + create_current_sequence_media_bin, + create_compound_clip, set_project_manager_to_folder_name ) @@ -33,7 +36,8 @@ from .workio import ( work_root ) -bmd = None +bmdvr = None +bmdvf = None __all__ = [ # pipeline @@ -54,6 +58,9 @@ __all__ = [ "get_project_manager", "get_current_project", "get_current_sequence", + "get_current_track_items", + "create_current_sequence_media_bin", + "create_compound_clip", "set_project_manager_to_folder_name", # menu @@ -71,5 +78,6 @@ __all__ = [ "work_root", # singleton with black magic resolve module - "bmd" + "bmdvr", + "bmdvf" ] diff --git a/pype/hosts/resolve/lib.py b/pype/hosts/resolve/lib.py index 25e177eb1c..9232f8ec68 100644 --- a/pype/hosts/resolve/lib.py +++ b/pype/hosts/resolve/lib.py @@ -8,9 +8,9 @@ self.pm = None def get_project_manager(): - from . import bmd + from . import bmdvr if not self.pm: - self.pm = bmd.GetProjectManager() + self.pm = bmdvr.GetProjectManager() return self.pm @@ -28,6 +28,207 @@ def get_current_sequence(): return project.GetCurrentTimeline() +def get_current_track_items( + filter=False, + track_type=None, + selecting_color=None): + """ Gets all available current timeline track items + """ + from pprint import pformat + track_type = track_type or "video" + selecting_color = selecting_color or "Chocolate" + project = get_current_project() + sequence = get_current_sequence() + selected_clips = list() + + # get all tracks count filtered by track type + sequence_video_count = sequence.GetTrackCount(track_type) + + # loop all tracks and get items + _clips = dict() + for track_index in range(1, (int(sequence_video_count) + 1)): + track_name = sequence.GetTrackName(track_type, track_index) + track_track_items = sequence.GetItemListInTrack( + track_type, track_index) + _clips[track_index] = track_track_items + + _data = { + "project": project, + "sequence": sequence, + "track": { + "name": track_name, + "index": track_index, + "type": track_type} + } + # get track item object and its color + for clip_index, ti in enumerate(_clips[track_index]): + data = _data.copy() + data["clip"] = { + "item": ti, + "index": clip_index + } + ti_color = ti.GetClipColor() + if filter is True: + if selecting_color in ti_color: + selected_clips.append(data) + ti.ClearClipColor() + else: + selected_clips.append(data) + + return selected_clips + + +def create_current_sequence_media_bin(sequence): + seq_name = sequence.GetName() + media_pool = get_current_project().GetMediaPool() + root_folder = media_pool.GetRootFolder() + sub_folders = root_folder.GetSubFolderList() + testing_names = list() + + print(f"_ sub_folders: {sub_folders}") + for subfolder in sub_folders: + subf_name = subfolder.GetName() + if seq_name in subf_name: + testing_names.append(subfolder) + else: + testing_names.append(False) + + matching = next((f for f in testing_names if f is not False), None) + + if not matching: + new_folder = media_pool.AddSubFolder(root_folder, seq_name) + media_pool.SetCurrentFolder(new_folder) + else: + media_pool.SetCurrentFolder(matching) + + return media_pool.GetCurrentFolder() + + +def create_compound_clip(clip_data, folder, presets): + """ + Convert timeline object into nested timeline object + + Args: + clip_data (dict): timeline item object packed into dict + with project, timeline (sequence) + folder (resolve.MediaPool.Folder): media pool folder object, + presets (dict): pype config plugin presets + + Returns: + resolve.MediaPoolItem: media pool item with compound clip timeline(cct) + """ + from pprint import pformat + + # get basic objects form data + project = clip_data["project"] + sequence = clip_data["sequence"] + clip = clip_data["clip"] + + # get details of objects + clip_item = clip["item"] + track = clip_data["track"] + + # build name + clip_name_split = clip_item.GetName().split(".") + name = "_".join([ + track["name"], + str(track["index"]), + clip_name_split[0], + str(clip["index"])] + ) + + # get metadata + mp_item = clip_item.GetMediaPoolItem() + mp_props = mp_item.GetClipProperty() + metadata = get_metadata_from_clip(clip_item) + mp = project.GetMediaPool() + + # keep original sequence + sq_origin = sequence + + # print(f"_ sequence: {sequence}") + # print(f"_ metadata: {pformat(metadata)}") + + # Set current folder to input media_pool_folder: + mp.SetCurrentFolder(folder) + + # check if clip doesnt exist already: + clips = folder.GetClipList() + cct = next((c for c in clips + if c.GetName() in name), None) + + if cct: + print(f"_ cct exists: {cct}") + return cct + + # Create empty timeline in current folder and give name: + cct = mp.CreateEmptyTimeline(name) + print(f"_ cct: {cct}") + + # Set current timeline to created timeline: + project.SetCurrentTimeline(cct) + + # Add input clip to the current timeline: + # TODO: set offsets if handles + done = mp.AppendToTimeline([{ + "mediaPoolItem": mp_item, + "startFrame": int(mp_props["Start"]), + "endFrame": int(mp_props["End"]) + }]) + print(f"_ done1: {done}") + + # Set current timeline to the working timeline: + project.SetCurrentTimeline(sq_origin) + + # Add collected metadata to the comound clip: + done = mp_item.SetClipProperty("pypeMetadata", metadata) + print(f"_ done2: {done}") + + return cct + + +def validate_tc(x): + # Validate and reformat timecode string + + if len(x) != 11: + print('Invalid timecode. Try again.') + + c = ':' + colonized = x[:2] + c + x[3:5] + c + x[6:8] + c + x[9:] + + if colonized.replace(':', '').isdigit(): + print(f"_ colonized: {colonized}") + return colonized + else: + print('Invalid timecode. Try again.') + + +def get_metadata_from_clip(clip): + """ + Collect all metadata from resolve timeline item + + Args: + clip (resolve.TimelineItem): timeline item object + + Returns: + dict: all collected metadata as key: values + """ + mp_item = clip.GetMediaPoolItem() + + data = { + "clipIn": clip.GetStart(), + "clipOut": clip.GetEnd(), + "clipLeftOffset": clip.GetLeftOffset(), + "clipRightOffset": clip.GetRightOffset(), + "clipMarkers": clip.GetMarkers(), + "clipFlags": clip.GetFlagList(), + "sourceMetadata": mp_item.GetMetadata(), + "sourceId": mp_item.GetMediaId(), + "sourceProperties": mp_item.GetClipProperty() + } + return data + + def set_project_manager_to_folder_name(folder_name): """ Sets context of Project manager to given folder by name. diff --git a/pype/hosts/resolve/pipeline.py b/pype/hosts/resolve/pipeline.py index 91d06da274..92bef2e13b 100644 --- a/pype/hosts/resolve/pipeline.py +++ b/pype/hosts/resolve/pipeline.py @@ -152,29 +152,11 @@ def maintained_selection(): >>> print(node['selected'].value()) False """ - from . import get_current_project - project = get_current_project() - nodes = [] - previous_selection = None - - # deselect all nodes - reset_selection() - try: # do the operation yield finally: - # unselect all selection in case there is some - reset_selection() - # and select all previously selected nodes - if previous_selection: - try: - for n in nodes: - if n not in previous_selection: - continue - n['selected'].setValue(True) - except ValueError as e: - log.warning(e) + pass def reset_selection(): diff --git a/pype/hosts/resolve/plugin.py b/pype/hosts/resolve/plugin.py index 513b9984f4..002d12106d 100644 --- a/pype/hosts/resolve/plugin.py +++ b/pype/hosts/resolve/plugin.py @@ -89,6 +89,10 @@ class Creator(api.Creator): # adding basic current context resolve objects self.project = resolve.get_current_project() self.sequence = resolve.get_current_sequence() - - # TODO: make sure no duplicity of subsets are in workfile + + if (self.options or {}).get("useSelection"): + self.selected = resolve.get_current_track_items(filter=True) + else: + self.selected = resolve.get_current_track_items(filter=False) + return diff --git a/pype/hosts/resolve/utility_scripts/test.py b/pype/hosts/resolve/utility_scripts/test.py index 4c43507e62..cf7db3b7e5 100644 --- a/pype/hosts/resolve/utility_scripts/test.py +++ b/pype/hosts/resolve/utility_scripts/test.py @@ -10,8 +10,8 @@ def main(): bm = bmdvr.utils.get_resolve_module() log.info(f"blackmagicmodule: {bm}") -import DaVinciResolveScript as bmd -print(f"_>> bmd.scriptapp(Resolve): {bmd.scriptapp('Resolve')}") +import DaVinciResolveScript as bmdvr +print(f"_>> bmdvr.scriptapp(Resolve): {bmdvr.scriptapp('Resolve')}") if __name__ == "__main__": diff --git a/pype/hosts/resolve/utils.py b/pype/hosts/resolve/utils.py index 74ce2dc98f..dcc92c5b8d 100644 --- a/pype/hosts/resolve/utils.py +++ b/pype/hosts/resolve/utils.py @@ -15,10 +15,10 @@ log = Logger().get_logger(__name__, "resolve") def get_resolve_module(): from pype.hosts import resolve # dont run if already loaded - if resolve.bmd: + if resolve.bmdvr: log.info(("resolve module is assigned to " - f"`pype.hosts.resolve.bmd`: {resolve.bmd}")) - return resolve.bmd + f"`pype.hosts.resolve.bmdvr`: {resolve.bmdvr}")) + return resolve.bmdvr try: """ The PYTHONPATH needs to be set correctly for this import @@ -69,10 +69,14 @@ def get_resolve_module(): ) sys.exit() # assign global var and return - bmd = bmd.scriptapp("Resolve") - resolve.bmd = bmd + bmdvr = bmd.scriptapp("Resolve") + bmdvf = bmd.scriptapp("Fusion") + resolve.bmdvr = bmdvr + resolve.bmdvf = bmdvf log.info(("Assigning resolve module to " - f"`pype.hosts.resolve.bmd`: {resolve.bmd}")) + f"`pype.hosts.resolve.bmdvr`: {resolve.bmdvr}")) + log.info(("Assigning resolve module to " + f"`pype.hosts.resolve.bmdvf`: {resolve.bmdvf}")) def _sync_utility_scripts(env=None): diff --git a/pype/plugins/resolve/create/create_shot_clip.py b/pype/plugins/resolve/create/create_shot_clip.py index 43a8ab0cbd..c48ca3a5a6 100644 --- a/pype/plugins/resolve/create/create_shot_clip.py +++ b/pype/plugins/resolve/create/create_shot_clip.py @@ -1,6 +1,6 @@ +from pprint import pformat from pype.hosts import resolve - class CreateShotClip(resolve.Creator): """Publishable clip""" @@ -12,6 +12,25 @@ class CreateShotClip(resolve.Creator): presets = None def process(self): - print(f"Project name: {self.project.GetName()}") - print(f"Sequence name: {self.sequence.GetName()}") - print(self.presets) + project = self.project + sequence = self.sequence + presets = self.presets + print(f"__ selected_clips: {self.selected}") + + # sequence attrs + sq_frame_start = self.sequence.GetStartFrame() + sq_markers = self.sequence.GetMarkers() + print(f"__ sq_frame_start: {pformat(sq_frame_start)}") + print(f"__ seq_markers: {pformat(sq_markers)}") + + # create media bin for compound clips (trackItems) + mp_folder = resolve.create_current_sequence_media_bin(self.sequence) + print(f"_ mp_folder: {mp_folder.GetName()}") + + for t_data in self.selected: + print(t_data) + # convert track item to timeline media pool item + c_clip = resolve.create_compound_clip( + t_data, mp_folder, presets) + + # replace orig clip with compound_clip From 8e91764c4c06aff57d158e853e3e51d191dcd3dd Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 23 Jun 2020 12:47:06 +0300 Subject: [PATCH 006/813] feat(resolve): create compound clip wip --- pype/hosts/resolve/lib.py | 34 ++++++++++++++++++++++++---------- pype/hosts/resolve/utils.py | 4 ++-- 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/pype/hosts/resolve/lib.py b/pype/hosts/resolve/lib.py index 9232f8ec68..5a5980a462 100644 --- a/pype/hosts/resolve/lib.py +++ b/pype/hosts/resolve/lib.py @@ -1,4 +1,5 @@ import sys +import json from pype.api import Logger log = Logger().get_logger(__name__, "resolve") @@ -140,14 +141,14 @@ def create_compound_clip(clip_data, folder, presets): # get metadata mp_item = clip_item.GetMediaPoolItem() mp_props = mp_item.GetClipProperty() - metadata = get_metadata_from_clip(clip_item) + clip_attributes = get_clip_attributes(clip_item) mp = project.GetMediaPool() # keep original sequence sq_origin = sequence - # print(f"_ sequence: {sequence}") - # print(f"_ metadata: {pformat(metadata)}") + print(f"_ sequence: {sequence}") + print(f"_ metadata: {pformat(clip_attributes)}") # Set current folder to input media_pool_folder: mp.SetCurrentFolder(folder) @@ -163,7 +164,12 @@ def create_compound_clip(clip_data, folder, presets): # Create empty timeline in current folder and give name: cct = mp.CreateEmptyTimeline(name) - print(f"_ cct: {cct}") + + # check if clip doesnt exist already: + clips = folder.GetClipList() + cct = next((c for c in clips + if c.GetName() in name), None) + print(f"_ cct created: {cct}") # Set current timeline to created timeline: project.SetCurrentTimeline(cct) @@ -180,10 +186,19 @@ def create_compound_clip(clip_data, folder, presets): # Set current timeline to the working timeline: project.SetCurrentTimeline(sq_origin) - # Add collected metadata to the comound clip: - done = mp_item.SetClipProperty("pypeMetadata", metadata) + # Add collected metadata and attributes to the comound clip: + clip_attributes["VFX Notes"] = mp_item.GetMetadata( + "VFX Notes")["VFX Notes"] + clip_attributes = json.dumps(clip_attributes) + + for k, v in mp_item.GetMetadata().items(): + done = cct.SetMetadata(k, v) + + done = cct.SetMetadata("VFX Notes", clip_attributes) print(f"_ done2: {done}") + # # add clip item as take to timeline + # AddTake(cct, startFrame, endFrame) return cct @@ -203,15 +218,15 @@ def validate_tc(x): print('Invalid timecode. Try again.') -def get_metadata_from_clip(clip): +def get_clip_attributes(clip): """ - Collect all metadata from resolve timeline item + Collect basic atrributes from resolve timeline item Args: clip (resolve.TimelineItem): timeline item object Returns: - dict: all collected metadata as key: values + dict: all collected attributres as key: values """ mp_item = clip.GetMediaPoolItem() @@ -222,7 +237,6 @@ def get_metadata_from_clip(clip): "clipRightOffset": clip.GetRightOffset(), "clipMarkers": clip.GetMarkers(), "clipFlags": clip.GetFlagList(), - "sourceMetadata": mp_item.GetMetadata(), "sourceId": mp_item.GetMediaId(), "sourceProperties": mp_item.GetClipProperty() } diff --git a/pype/hosts/resolve/utils.py b/pype/hosts/resolve/utils.py index dcc92c5b8d..e11cc64b3b 100644 --- a/pype/hosts/resolve/utils.py +++ b/pype/hosts/resolve/utils.py @@ -70,9 +70,9 @@ def get_resolve_module(): sys.exit() # assign global var and return bmdvr = bmd.scriptapp("Resolve") - bmdvf = bmd.scriptapp("Fusion") + # bmdvf = bmd.scriptapp("Fusion") resolve.bmdvr = bmdvr - resolve.bmdvf = bmdvf + resolve.bmdvf = bmdvr.Fusion() log.info(("Assigning resolve module to " f"`pype.hosts.resolve.bmdvr`: {resolve.bmdvr}")) log.info(("Assigning resolve module to " From 34c27c3fbf7aac183017540f73e15e8335416c2d Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 23 Jun 2020 21:02:41 +0300 Subject: [PATCH 007/813] feat(resolve): compound clip create with sequencial rename --- pype/hosts/resolve/lib.py | 228 ++++++++++++++---- .../resolve/create/create_shot_clip.py | 16 +- 2 files changed, 192 insertions(+), 52 deletions(-) diff --git a/pype/hosts/resolve/lib.py b/pype/hosts/resolve/lib.py index 5a5980a462..50b70241c0 100644 --- a/pype/hosts/resolve/lib.py +++ b/pype/hosts/resolve/lib.py @@ -1,11 +1,15 @@ import sys import json +from opentimelineio import opentime + from pype.api import Logger log = Logger().get_logger(__name__, "resolve") self = sys.modules[__name__] self.pm = None +self.rename_index = 0 +self.rename_add = 0 def get_project_manager(): @@ -35,7 +39,6 @@ def get_current_track_items( selecting_color=None): """ Gets all available current timeline track items """ - from pprint import pformat track_type = track_type or "video" selecting_color = selecting_color or "Chocolate" project = get_current_project() @@ -105,7 +108,75 @@ def create_current_sequence_media_bin(sequence): return media_pool.GetCurrentFolder() -def create_compound_clip(clip_data, folder, presets): +def get_name_with_data(clip_data, presets): + """ + Take hierarchy data from presets and build name with parents data + + Args: + clip_data (dict): clip data from `get_current_track_items()` + presets (dict): data from create plugin + + Returns: + list: name, data + + """ + def _replace_hash_to_expression(name, text): + _spl = text.split("#") + _len = (len(_spl) - 1) + _repl = f"{{{name}:0>{_len}}}" + new_text = text.replace(("#" * _len), _repl) + return new_text + + # presets data + clip_name = presets["clipName"] + hierarchy = presets["hierarchy"] + hierarchy_data = presets["hierarchyData"].copy() + count_from = presets["countFrom"] + steps = presets["steps"] + + # reset rename_add + if self.rename_add < count_from: + self.rename_add = count_from + + # shot num calculate + if self.rename_index == 0: + shot_num = self.rename_add + else: + shot_num = self.rename_add + steps + + print(f"shot_num: {shot_num}") + + # clip data + _data = { + "sequence": clip_data["sequence"].GetName(), + "track": clip_data["track"]["name"].replace(" ", "_"), + "shot": shot_num + } + + # solve # in test to pythonic explression + for k, v in hierarchy_data.items(): + if "#" not in v: + continue + hierarchy_data[k] = _replace_hash_to_expression(k, v) + + # fill up pythonic expresisons + for k, v in hierarchy_data.items(): + hierarchy_data[k] = v.format(**_data) + + # fill up clip name and hierarchy keys + hierarchy = hierarchy.format(**hierarchy_data) + clip_name = clip_name.format(**hierarchy_data) + + self.rename_add = shot_num + print(f"shot_num: {shot_num}") + + return (clip_name, { + "hierarchy": hierarchy, + "hierarchyData": hierarchy_data + }) + + +def create_compound_clip(clip_data, folder, rename=False, **kwargs): """ Convert timeline object into nested timeline object @@ -113,12 +184,12 @@ def create_compound_clip(clip_data, folder, presets): clip_data (dict): timeline item object packed into dict with project, timeline (sequence) folder (resolve.MediaPool.Folder): media pool folder object, - presets (dict): pype config plugin presets + rename (bool)[optional]: renaming in sequence or not + kwargs (optional): additional data needed for rename=True (presets) Returns: resolve.MediaPoolItem: media pool item with compound clip timeline(cct) """ - from pprint import pformat # get basic objects form data project = clip_data["project"] @@ -129,27 +200,53 @@ def create_compound_clip(clip_data, folder, presets): clip_item = clip["item"] track = clip_data["track"] - # build name - clip_name_split = clip_item.GetName().split(".") - name = "_".join([ - track["name"], - str(track["index"]), - clip_name_split[0], - str(clip["index"])] - ) + mp = project.GetMediaPool() + + # get clip attributes + clip_attributes = get_clip_attributes(clip_item) + + if rename: + presets = kwargs.get("presets") + if presets: + name, data = get_name_with_data(clip_data, presets) + # add hirarchy data to clip attributes + clip_attributes.update(data) + else: + name = "{:0>3}_{:0>4}".format( + int(track["index"]), int(clip["index"])) + else: + # build name + clip_name_split = clip_item.GetName().split(".") + name = "_".join([ + track["name"], + str(track["index"]), + clip_name_split[0], + str(clip["index"])] + ) # get metadata mp_item = clip_item.GetMediaPoolItem() mp_props = mp_item.GetClipProperty() - clip_attributes = get_clip_attributes(clip_item) - mp = project.GetMediaPool() + + mp_first_frame = int(mp_props["Start"]) + mp_last_frame = int(mp_props["End"]) + + # initialize basic source timing for otio + ci_l_offset = clip_item.GetLeftOffset() + ci_duration = clip_item.GetDuration() + rate = float(mp_props["FPS"]) + + # source rational times + mp_in_rc = opentime.RationalTime((ci_l_offset), rate) + mp_out_rc = opentime.RationalTime((ci_l_offset + ci_duration - 1), rate) + + # get frame in and out for clip swaping + in_frame = opentime.to_frames(mp_in_rc) + out_frame = opentime.to_frames(mp_out_rc) # keep original sequence sq_origin = sequence - print(f"_ sequence: {sequence}") - print(f"_ metadata: {pformat(clip_attributes)}") - # Set current folder to input media_pool_folder: mp.SetCurrentFolder(folder) @@ -160,48 +257,89 @@ def create_compound_clip(clip_data, folder, presets): if cct: print(f"_ cct exists: {cct}") - return cct + else: + # Create empty timeline in current folder and give name: + cct = mp.CreateEmptyTimeline(name) - # Create empty timeline in current folder and give name: - cct = mp.CreateEmptyTimeline(name) + # check if clip doesnt exist already: + clips = folder.GetClipList() + cct = next((c for c in clips + if c.GetName() in name), None) + print(f"_ cct created: {cct}") - # check if clip doesnt exist already: - clips = folder.GetClipList() - cct = next((c for c in clips - if c.GetName() in name), None) - print(f"_ cct created: {cct}") + # Set current timeline to created timeline: + project.SetCurrentTimeline(cct) - # Set current timeline to created timeline: - project.SetCurrentTimeline(cct) + # Add input clip to the current timeline: + mp.AppendToTimeline([{ + "mediaPoolItem": mp_item, + "startFrame": mp_first_frame, + "endFrame": mp_last_frame + }]) - # Add input clip to the current timeline: - # TODO: set offsets if handles - done = mp.AppendToTimeline([{ - "mediaPoolItem": mp_item, - "startFrame": int(mp_props["Start"]), - "endFrame": int(mp_props["End"]) - }]) - print(f"_ done1: {done}") - - # Set current timeline to the working timeline: - project.SetCurrentTimeline(sq_origin) + # Set current timeline to the working timeline: + project.SetCurrentTimeline(sq_origin) # Add collected metadata and attributes to the comound clip: - clip_attributes["VFX Notes"] = mp_item.GetMetadata( - "VFX Notes")["VFX Notes"] + if clip_attributes.get("VFX Notes"): + clip_attributes["VFX Notes"] = mp_item.GetMetadata( + "VFX Notes")["VFX Notes"] clip_attributes = json.dumps(clip_attributes) + # add attributes to metadata for k, v in mp_item.GetMetadata().items(): - done = cct.SetMetadata(k, v) + cct.SetMetadata(k, v) - done = cct.SetMetadata("VFX Notes", clip_attributes) - print(f"_ done2: {done}") + # add metadata to cct + cct.SetMetadata("VFX Notes", clip_attributes) - # # add clip item as take to timeline - # AddTake(cct, startFrame, endFrame) + # reset start timecode of the compound clip + cct.SetClipProperty("Start TC", mp_props["Start TC"]) + + # swap clips on timeline + swap_clips(clip_item, cct, name, in_frame, out_frame) + + cct.SetClipColor("Pink") return cct +def swap_clips(from_clip, to_clip, to_clip_name, to_in_frame, to_out_frame): + """ + Swaping clips on timeline in timelineItem + + It will add take and activate it to the frame range which is inputted + + Args: + from_clip (resolve.mediaPoolItem) + to_clip (resolve.mediaPoolItem) + to_clip_name (str): name of to_clip + to_in_frame (float): cut in frame, usually `GetLeftOffset()` + to_out_frame (float): cut out frame, usually left offset plus duration + + Returns: + bool: True if successfully replaced + + """ + # add clip item as take to timeline + take = from_clip.AddTake( + to_clip, + float(to_in_frame), + float(to_out_frame) + ) + + if not take: + return False + + for take_index in range(1, (int(from_clip.GetTakesCount()) + 1)): + take_item = from_clip.GetTakeByIndex(take_index) + take_mp_item = take_item["mediaPoolItem"] + if to_clip_name in take_mp_item.GetName(): + from_clip.SelectTakeByIndex(take_index) + from_clip.FinalizeTake() + return True + return False + + def validate_tc(x): # Validate and reformat timecode string diff --git a/pype/plugins/resolve/create/create_shot_clip.py b/pype/plugins/resolve/create/create_shot_clip.py index c48ca3a5a6..c12d5541d4 100644 --- a/pype/plugins/resolve/create/create_shot_clip.py +++ b/pype/plugins/resolve/create/create_shot_clip.py @@ -1,5 +1,7 @@ from pprint import pformat from pype.hosts import resolve +from avalon.vendor import Qt + class CreateShotClip(resolve.Creator): """Publishable clip""" @@ -12,9 +14,8 @@ class CreateShotClip(resolve.Creator): presets = None def process(self): - project = self.project - sequence = self.sequence - presets = self.presets + from pype.hosts.resolve import lib + print(f"__ selected_clips: {self.selected}") # sequence attrs @@ -27,10 +28,11 @@ class CreateShotClip(resolve.Creator): mp_folder = resolve.create_current_sequence_media_bin(self.sequence) print(f"_ mp_folder: {mp_folder.GetName()}") - for t_data in self.selected: + lib.rename_add = 0 + for i, t_data in enumerate(self.selected): + lib.rename_index = i print(t_data) # convert track item to timeline media pool item c_clip = resolve.create_compound_clip( - t_data, mp_folder, presets) - - # replace orig clip with compound_clip + t_data, mp_folder, rename=True, **dict( + {"presets": self.presets})) From 4f3565d2cf85d5ff4171f5247176603ce56642b2 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 24 Jun 2020 12:55:17 +0300 Subject: [PATCH 008/813] feat(resolve): basic publish collecting of clips --- pype/hosts/resolve/__init__.py | 4 + pype/hosts/resolve/lib.py | 34 +++- pype/plugins/resolve/publish/collect_clips.py | 159 ++++++++++++++++++ 3 files changed, 190 insertions(+), 7 deletions(-) create mode 100644 pype/plugins/resolve/publish/collect_clips.py diff --git a/pype/hosts/resolve/__init__.py b/pype/hosts/resolve/__init__.py index 5f651f4b29..c8f45259ff 100644 --- a/pype/hosts/resolve/__init__.py +++ b/pype/hosts/resolve/__init__.py @@ -20,6 +20,8 @@ from .lib import ( get_current_track_items, create_current_sequence_media_bin, create_compound_clip, + swap_clips, + get_pype_clip_metadata, set_project_manager_to_folder_name ) @@ -61,6 +63,8 @@ __all__ = [ "get_current_track_items", "create_current_sequence_media_bin", "create_compound_clip", + "swap_clips", + "get_pype_clip_metadata", "set_project_manager_to_folder_name", # menu diff --git a/pype/hosts/resolve/lib.py b/pype/hosts/resolve/lib.py index 50b70241c0..db3bd989bf 100644 --- a/pype/hosts/resolve/lib.py +++ b/pype/hosts/resolve/lib.py @@ -1,6 +1,7 @@ import sys import json from opentimelineio import opentime +from pprint import pformat from pype.api import Logger @@ -10,6 +11,7 @@ self = sys.modules[__name__] self.pm = None self.rename_index = 0 self.rename_add = 0 +self.pype_metadata_key = "VFX Notes" def get_project_manager(): @@ -46,11 +48,11 @@ def get_current_track_items( selected_clips = list() # get all tracks count filtered by track type - sequence_video_count = sequence.GetTrackCount(track_type) + selected_track_count = sequence.GetTrackCount(track_type) # loop all tracks and get items _clips = dict() - for track_index in range(1, (int(sequence_video_count) + 1)): + for track_index in range(1, (int(selected_track_count) + 1)): track_name = sequence.GetTrackName(track_type, track_index) track_track_items = sequence.GetItemListInTrack( track_type, track_index) @@ -190,7 +192,6 @@ def create_compound_clip(clip_data, folder, rename=False, **kwargs): Returns: resolve.MediaPoolItem: media pool item with compound clip timeline(cct) """ - # get basic objects form data project = clip_data["project"] sequence = clip_data["sequence"] @@ -204,6 +205,7 @@ def create_compound_clip(clip_data, folder, rename=False, **kwargs): # get clip attributes clip_attributes = get_clip_attributes(clip_item) + print(f"_ clip_attributes: {pformat(clip_attributes)}") if rename: presets = kwargs.get("presets") @@ -281,9 +283,11 @@ def create_compound_clip(clip_data, folder, rename=False, **kwargs): project.SetCurrentTimeline(sq_origin) # Add collected metadata and attributes to the comound clip: - if clip_attributes.get("VFX Notes"): - clip_attributes["VFX Notes"] = mp_item.GetMetadata( - "VFX Notes")["VFX Notes"] + if mp_item.GetMetadata(self.pype_metadata_key): + clip_attributes[self.pype_metadata_key] = mp_item.GetMetadata( + self.pype_metadata_key)[self.pype_metadata_key] + + # stringify clip_attributes = json.dumps(clip_attributes) # add attributes to metadata @@ -291,7 +295,7 @@ def create_compound_clip(clip_data, folder, rename=False, **kwargs): cct.SetMetadata(k, v) # add metadata to cct - cct.SetMetadata("VFX Notes", clip_attributes) + cct.SetMetadata(self.pype_metadata_key, clip_attributes) # reset start timecode of the compound clip cct.SetClipProperty("Start TC", mp_props["Start TC"]) @@ -356,6 +360,22 @@ def validate_tc(x): print('Invalid timecode. Try again.') +def get_pype_clip_metadata(clip): + """ + Get pype metadata created by creator plugin + + Attributes: + clip (resolve.TimelineItem): resolve's object + + Returns: + dict: hierarchy, orig clip attributes + """ + mp_item = clip.GetMediaPoolItem() + metadata = mp_item.GetMetadata() + + return metadata.get(self.pype_metadata_key) + + def get_clip_attributes(clip): """ Collect basic atrributes from resolve timeline item diff --git a/pype/plugins/resolve/publish/collect_clips.py b/pype/plugins/resolve/publish/collect_clips.py new file mode 100644 index 0000000000..0f02f26f2e --- /dev/null +++ b/pype/plugins/resolve/publish/collect_clips.py @@ -0,0 +1,159 @@ +import os +from pyblish import api +from pype.hosts import resolve +import json + +class CollectClips(api.ContextPlugin): + """Collect all Track items selection.""" + + order = api.CollectorOrder + 0.01 + label = "Collect Clips" + hosts = ["resolve"] + + def process(self, context): + # create asset_names conversion table + if not context.data.get("assetsShared"): + self.log.debug("Created `assetsShared` in context") + context.data["assetsShared"] = dict() + + projectdata = context.data["projectEntity"]["data"] + selection = resolve.get_current_track_items( + filter=True, selecting_color="Pink") + + for clip_data in selection: + data = dict() + + # get basic objects form data + project = clip_data["project"] + sequence = clip_data["sequence"] + clip = clip_data["clip"] + + # sequence attrs + sq_frame_start = sequence.GetStartFrame() + self.log.debug(f"sq_frame_start: {sq_frame_start}") + + sq_markers = sequence.GetMarkers() + + # get details of objects + clip_item = clip["item"] + track = clip_data["track"] + + mp = project.GetMediaPool() + + # get clip attributes + clip_metadata = resolve.get_pype_clip_metadata(clip_item) + clip_metadata = json.loads(clip_metadata) + self.log.debug(f"clip_metadata: {clip_metadata}") + + compound_source_prop = clip_metadata["sourceProperties"] + self.log.debug(f"compound_source_prop: {compound_source_prop}") + + asset_name = clip_item.GetName() + mp_item = clip_item.GetMediaPoolItem() + mp_prop = mp_item.GetClipProperty() + source_first = int(compound_source_prop["Start"]) + source_last = int(compound_source_prop["End"]) + source_duration = compound_source_prop["Frames"] + fps = float(mp_prop["FPS"]) + self.log.debug(f"source_first: {source_first}") + self.log.debug(f"source_last: {source_last}") + self.log.debug(f"source_duration: {source_duration}") + self.log.debug(f"fps: {fps}") + + source_path = os.path.normpath( + compound_source_prop["File Path"]) + source_name = compound_source_prop["File Name"] + source_id = clip_metadata["sourceId"] + self.log.debug(f"source_path: {source_path}") + self.log.debug(f"source_name: {source_name}") + self.log.debug(f"source_id: {source_id}") + + clip_left_offset = int(clip_item.GetLeftOffset()) + clip_right_offset = int(clip_item.GetRightOffset()) + self.log.debug(f"clip_left_offset: {clip_left_offset}") + self.log.debug(f"clip_right_offset: {clip_right_offset}") + + # source in/out + source_in = int(source_first + clip_left_offset) + source_out = int(source_first + clip_right_offset) + self.log.debug(f"source_in: {source_in}") + self.log.debug(f"source_out: {source_out}") + + clip_in = int(clip_item.GetStart() - sq_frame_start) + clip_out = int(clip_item.GetEnd() - sq_frame_start) + clip_duration = int(clip_item.GetDuration()) + self.log.debug(f"clip_in: {clip_in}") + self.log.debug(f"clip_out: {clip_out}") + self.log.debug(f"clip_duration: {clip_duration}") + + is_sequence = False + + self.log.debug( + "__ assets_shared: {}".format( + context.data["assetsShared"])) + + # Check for clips with the same range + # this is for testing if any vertically neighbouring + # clips has been already processed + clip_matching_with_range = next( + (k for k, v in context.data["assetsShared"].items() + if (v.get("_clipIn", 0) == clip_in) + and (v.get("_clipOut", 0) == clip_out) + ), False) + + # check if clip name is the same in matched + # vertically neighbouring clip + # if it is then it is correct and resent variable to False + # not to be rised wrong name exception + if asset_name in str(clip_matching_with_range): + clip_matching_with_range = False + + # rise wrong name exception if found one + assert (not clip_matching_with_range), ( + "matching clip: {asset}" + " timeline range ({clip_in}:{clip_out})" + " conflicting with {clip_matching_with_range}" + " >> rename any of clips to be the same as the other <<" + ).format( + **locals()) + + if ("[" in source_name) and ("]" in source_name): + is_sequence = True + + data.update({ + "name": "_".join([ + track["name"], asset_name, source_name]), + "item": clip_item, + "source": mp_item, + # "timecodeStart": str(source.timecodeStart()), + "timelineStart": sq_frame_start, + "sourcePath": source_path, + "sourceFileHead": source_name, + "isSequence": is_sequence, + "track": track["name"], + "trackIndex": track["index"], + "sourceFirst": source_first, + + "sourceIn": source_in, + "sourceOut": source_out, + "mediaDuration": source_duration, + "clipIn": clip_in, + "clipOut": clip_out, + "clipDuration": clip_duration, + "asset": asset_name, + "subset": "plateMain", + "family": "clip", + "families": [], + "handleStart": projectdata.get("handleStart", 0), + "handleEnd": projectdata.get("handleEnd", 0)}) + + instance = context.create_instance(**data) + + self.log.info("Created instance: {}".format(instance)) + self.log.info("Created instance.data: {}".format(instance.data)) + + context.data["assetsShared"][asset_name] = { + "_clipIn": clip_in, + "_clipOut": clip_out + } + self.log.info("context.data[\"assetsShared\"]: {}".format(context.data["assetsShared"])) From 7465d8cb7394ec1c3987b16da782a8b84f66cfd2 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 24 Jun 2020 20:17:22 +0300 Subject: [PATCH 009/813] feat(resolve): creator with sequencial rename gui --- pype/hosts/resolve/menu_style.qss | 8 ++ pype/hosts/resolve/plugin.py | 76 ++++++++++++++++++- .../resolve/create/create_shot_clip.py | 53 ++++++++++++- 3 files changed, 133 insertions(+), 4 deletions(-) diff --git a/pype/hosts/resolve/menu_style.qss b/pype/hosts/resolve/menu_style.qss index df4fd7e949..c43886c7c3 100644 --- a/pype/hosts/resolve/menu_style.qss +++ b/pype/hosts/resolve/menu_style.qss @@ -20,6 +20,14 @@ QPushButton:hover { color: #e64b3d; } +QSpinBox { + background-color: #ffffff; +} + +QLineEdit { + background-color: #ffffff; +} + #PypeMenu { border: 1px solid #fef9ef; } diff --git a/pype/hosts/resolve/plugin.py b/pype/hosts/resolve/plugin.py index 002d12106d..2eff278b80 100644 --- a/pype/hosts/resolve/plugin.py +++ b/pype/hosts/resolve/plugin.py @@ -1,8 +1,82 @@ +import sys from avalon import api from pype.hosts import resolve from avalon.vendor import qargparse from pype.api import config +from Qt import QtWidgets + + +class Universal_widget(QtWidgets.QDialog): + def __init__(self, widgets, parent=None): + super(Universal_widget, self).__init__(parent) + + # Where inputs and labels are set + content_widget = QtWidgets.QWidget(self) + content_layout = QtWidgets.QFormLayout(content_widget) + + self.items = dict() + for w in widgets: + attr = getattr(QtWidgets, w["type"]) + label = QtWidgets.QLabel(w["label"]) + attr_name = w["label"].replace(" ", "").lower() + setattr( + self, + attr_name, + attr(parent=self)) + item = getattr(self, attr_name) + func = next((k for k in w if k not in ["label", "type"]), None) + if func: + if getattr(item, func): + func_attr = getattr(item, func) + func_attr(w[func]) + + content_layout.addRow(label, item) + self.items.update({ + w["label"]: item + }) + + # Confirmation buttons + btns_widget = QtWidgets.QWidget(self) + btns_layout = QtWidgets.QHBoxLayout(btns_widget) + + cancel_btn = QtWidgets.QPushButton("Cancel") + btns_layout.addWidget(cancel_btn) + + ok_btn = QtWidgets.QPushButton("Ok") + btns_layout.addWidget(ok_btn) + + # Main layout of the dialog + main_layout = QtWidgets.QVBoxLayout(self) + main_layout.setContentsMargins(0, 0, 0, 0) + main_layout.setSpacing(0) + + main_layout.addWidget(content_widget) + main_layout.addWidget(btns_widget) + + ok_btn.clicked.connect(self._on_ok_clicked) + cancel_btn.clicked.connect(self._on_cancel_clicked) + + stylesheet = resolve.menu.load_stylesheet() + self.setStyleSheet(stylesheet) + + def _on_ok_clicked(self): + self.value() + self.close() + + def _on_cancel_clicked(self): + self.result = None + self.close() + + def value(self): + for k, v in self.items.items(): + if getattr(v, "value", None): + result = getattr(v, "value") + else: + result = getattr(v, "text") + self.items[k] = result() + self.result = self.items + def get_reference_node_parents(ref): """Return all parent reference nodes of reference node @@ -95,4 +169,4 @@ class Creator(api.Creator): else: self.selected = resolve.get_current_track_items(filter=False) - return + self.widget = Universal_widget diff --git a/pype/plugins/resolve/create/create_shot_clip.py b/pype/plugins/resolve/create/create_shot_clip.py index c12d5541d4..bba9851c0f 100644 --- a/pype/plugins/resolve/create/create_shot_clip.py +++ b/pype/plugins/resolve/create/create_shot_clip.py @@ -1,6 +1,13 @@ from pprint import pformat from pype.hosts import resolve -from avalon.vendor import Qt +from pype.hosts.resolve import lib +import re + + +def camel_case_split(text): + matches = re.finditer( + '.+?(?:(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])|$)', text) + return " ".join([str(m.group(0)).capitalize() for m in matches]) class CreateShotClip(resolve.Creator): @@ -13,11 +20,51 @@ class CreateShotClip(resolve.Creator): presets = None - def process(self): - from pype.hosts.resolve import lib + # widget + layout = [{ + "type": "QLabel", + "label": "Define sequencial rename" + }] + def add_presets_to_layout(self, data): + for k, v in data.items(): + if isinstance(v, dict): + self.layout.append({ + "type": "QLabel", + "label": camel_case_split(k) + }) + self.add_presets_to_layout(v) + elif isinstance(v, str): + self.layout.append({ + "type": "QLineEdit", + "label": camel_case_split(k), + "setText": v + }) + elif isinstance(v, int): + self.layout.append({ + "type": "QSpinBox", + "label": camel_case_split(k), + "setValue": v + }) + + def process(self): print(f"__ selected_clips: {self.selected}") + if len(self.selected) < 1: + return + + self.add_presets_to_layout(self.presets) + + widget = self.widget(self.layout) + widget.exec_() + + print(widget.result) + if widget.result: + print("success") + return + else: + return + # sequence attrs sq_frame_start = self.sequence.GetStartFrame() sq_markers = self.sequence.GetMarkers() From ccadf98c501bc4ee5ae447b846bcc93bb8a6e156 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 25 Jun 2020 09:17:35 +0300 Subject: [PATCH 010/813] feat(resolve): update creator input widget --- pype/hosts/resolve/menu_style.qss | 6 ++++++ pype/hosts/resolve/plugin.py | 15 +++++++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/pype/hosts/resolve/menu_style.qss b/pype/hosts/resolve/menu_style.qss index c43886c7c3..516c9af72b 100644 --- a/pype/hosts/resolve/menu_style.qss +++ b/pype/hosts/resolve/menu_style.qss @@ -22,10 +22,16 @@ QPushButton:hover { QSpinBox { background-color: #ffffff; + padding: 5; } QLineEdit { background-color: #ffffff; + padding: 5; +} + +QLabel { + color: #ffffff; } #PypeMenu { diff --git a/pype/hosts/resolve/plugin.py b/pype/hosts/resolve/plugin.py index 2eff278b80..c1aa05dc7d 100644 --- a/pype/hosts/resolve/plugin.py +++ b/pype/hosts/resolve/plugin.py @@ -4,13 +4,24 @@ from pype.hosts import resolve from avalon.vendor import qargparse from pype.api import config -from Qt import QtWidgets +from Qt import QtWidgets, QtCore class Universal_widget(QtWidgets.QDialog): def __init__(self, widgets, parent=None): super(Universal_widget, self).__init__(parent) + self.setObjectName("PypeCreatorInput") + + self.setWindowFlags( + QtCore.Qt.Window + | QtCore.Qt.CustomizeWindowHint + | QtCore.Qt.WindowTitleHint + | QtCore.Qt.WindowCloseButtonHint + | QtCore.Qt.WindowStaysOnTopHint + ) + self.setWindowTitle("CreatorInput") + # Where inputs and labels are set content_widget = QtWidgets.QWidget(self) content_layout = QtWidgets.QFormLayout(content_widget) @@ -48,7 +59,7 @@ class Universal_widget(QtWidgets.QDialog): # Main layout of the dialog main_layout = QtWidgets.QVBoxLayout(self) - main_layout.setContentsMargins(0, 0, 0, 0) + main_layout.setContentsMargins(10, 20, 10, 20) main_layout.setSpacing(0) main_layout.addWidget(content_widget) From 3fb4460b7b412bddff727e438d706c1c567a987f Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 26 Jun 2020 14:13:54 +0300 Subject: [PATCH 011/813] feat(resolve): creator plugin with dynamic widget --- pype/hosts/resolve/menu_style.qss | 4 + pype/hosts/resolve/plugin.py | 84 ++++++++++++++----- .../resolve/create/create_shot_clip.py | 39 +-------- 3 files changed, 69 insertions(+), 58 deletions(-) diff --git a/pype/hosts/resolve/menu_style.qss b/pype/hosts/resolve/menu_style.qss index 516c9af72b..f3bfa7a30c 100644 --- a/pype/hosts/resolve/menu_style.qss +++ b/pype/hosts/resolve/menu_style.qss @@ -41,3 +41,7 @@ QLabel { #Spacer { background-color: #282828; } + +#ContentLayout { + background-color: #585858; +} diff --git a/pype/hosts/resolve/plugin.py b/pype/hosts/resolve/plugin.py index c1aa05dc7d..8c5fd321d1 100644 --- a/pype/hosts/resolve/plugin.py +++ b/pype/hosts/resolve/plugin.py @@ -1,4 +1,5 @@ import sys +import re from avalon import api from pype.hosts import resolve from avalon.vendor import qargparse @@ -8,7 +9,11 @@ from Qt import QtWidgets, QtCore class Universal_widget(QtWidgets.QDialog): - def __init__(self, widgets, parent=None): + + # output items + items = dict() + + def __init__(self, name, presets, parent=None): super(Universal_widget, self).__init__(parent) self.setObjectName("PypeCreatorInput") @@ -24,28 +29,14 @@ class Universal_widget(QtWidgets.QDialog): # Where inputs and labels are set content_widget = QtWidgets.QWidget(self) - content_layout = QtWidgets.QFormLayout(content_widget) + self.content_layout = QtWidgets.QFormLayout(content_widget) + self.content_layout.setObjectName("ContentLayout") - self.items = dict() - for w in widgets: - attr = getattr(QtWidgets, w["type"]) - label = QtWidgets.QLabel(w["label"]) - attr_name = w["label"].replace(" ", "").lower() - setattr( - self, - attr_name, - attr(parent=self)) - item = getattr(self, attr_name) - func = next((k for k in w if k not in ["label", "type"]), None) - if func: - if getattr(item, func): - func_attr = getattr(item, func) - func_attr(w[func]) + # first add widget tag line + self.create_row("QLabel", name) - content_layout.addRow(label, item) - self.items.update({ - w["label"]: item - }) + # add preset data into input widget layout + self.add_presets_to_layout(presets) # Confirmation buttons btns_widget = QtWidgets.QWidget(self) @@ -88,6 +79,57 @@ class Universal_widget(QtWidgets.QDialog): self.items[k] = result() self.result = self.items + def camel_case_split(self, text): + matches = re.finditer( + '.+?(?:(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])|$)', text) + return " ".join([str(m.group(0)).capitalize() for m in matches]) + + def create_row(self, type, text, **kwargs): + # get type attribute from qwidgets + attr = getattr(QtWidgets, type) + + # convert label text to normal capitalized text with spaces + label_text = self.camel_case_split(text) + + # assign the new text to lable widget + label = QtWidgets.QLabel(label_text) + + # create attribute name text strip of spaces + attr_name = text.replace(" ", "") + + # create attribute and assign default values + setattr( + self, + attr_name, + attr(parent=self)) + + # assign the created attribute to variable + item = getattr(self, attr_name) + for func, val in kwargs.items(): + if getattr(item, func): + func_attr = getattr(item, func) + func_attr(val) + + self.content_layout.addRow(label, item) + return item + + def add_presets_to_layout(self, data): + for k, v in data.items(): + if isinstance(v, dict): + # if nested dict then create label + # TODO: create also new layout + self.create_row("QLabel", k) + self.add_presets_to_layout(v) + elif isinstance(v, str): + item = self.create_row("QLineEdit", k, setText=v) + elif isinstance(v, int): + item = self.create_row("QSpinBox", k, setValue=v) + + # add it to items for later requests + self.items.update({ + k: item + }) + def get_reference_node_parents(ref): """Return all parent reference nodes of reference node diff --git a/pype/plugins/resolve/create/create_shot_clip.py b/pype/plugins/resolve/create/create_shot_clip.py index bba9851c0f..18759a2f98 100644 --- a/pype/plugins/resolve/create/create_shot_clip.py +++ b/pype/plugins/resolve/create/create_shot_clip.py @@ -1,13 +1,6 @@ from pprint import pformat from pype.hosts import resolve from pype.hosts.resolve import lib -import re - - -def camel_case_split(text): - matches = re.finditer( - '.+?(?:(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])|$)', text) - return " ".join([str(m.group(0)).capitalize() for m in matches]) class CreateShotClip(resolve.Creator): @@ -18,44 +11,16 @@ class CreateShotClip(resolve.Creator): icon = "film" defaults = ["Main"] + gui_name = "Define sequencial rename" presets = None - # widget - layout = [{ - "type": "QLabel", - "label": "Define sequencial rename" - }] - - def add_presets_to_layout(self, data): - for k, v in data.items(): - if isinstance(v, dict): - self.layout.append({ - "type": "QLabel", - "label": camel_case_split(k) - }) - self.add_presets_to_layout(v) - elif isinstance(v, str): - self.layout.append({ - "type": "QLineEdit", - "label": camel_case_split(k), - "setText": v - }) - elif isinstance(v, int): - self.layout.append({ - "type": "QSpinBox", - "label": camel_case_split(k), - "setValue": v - }) - def process(self): print(f"__ selected_clips: {self.selected}") if len(self.selected) < 1: return - self.add_presets_to_layout(self.presets) - - widget = self.widget(self.layout) + widget = self.widget(self.gui_name, self.presets) widget.exec_() print(widget.result) From 4391a14da2a2f4063b3692a089b2ae9a1c948040 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 26 Jun 2020 20:28:15 +0300 Subject: [PATCH 012/813] feat(resolve): adding Create widget with style --- pype/hosts/resolve/lib.py | 2 +- pype/hosts/resolve/menu_style.qss | 29 +++-- pype/hosts/resolve/plugin.py | 112 +++++++++++++----- .../resolve/create/create_shot_clip.py | 25 ++-- 4 files changed, 116 insertions(+), 52 deletions(-) diff --git a/pype/hosts/resolve/lib.py b/pype/hosts/resolve/lib.py index db3bd989bf..deb4fa6339 100644 --- a/pype/hosts/resolve/lib.py +++ b/pype/hosts/resolve/lib.py @@ -77,7 +77,7 @@ def get_current_track_items( if filter is True: if selecting_color in ti_color: selected_clips.append(data) - ti.ClearClipColor() + # ti.ClearClipColor() else: selected_clips.append(data) diff --git a/pype/hosts/resolve/menu_style.qss b/pype/hosts/resolve/menu_style.qss index f3bfa7a30c..ea11c4ca2e 100644 --- a/pype/hosts/resolve/menu_style.qss +++ b/pype/hosts/resolve/menu_style.qss @@ -1,6 +1,7 @@ QWidget { background-color: #282828; border-radius: 3; + font-size: 13px; } QPushButton { @@ -21,27 +22,37 @@ QPushButton:hover { } QSpinBox { - background-color: #ffffff; - padding: 5; + border: 1px solid #090909; + background-color: #201f1f; + color: #ffffff; + padding: 2; + max-width: 8em; + qproperty-alignment: AlignCenter; } QLineEdit { - background-color: #ffffff; - padding: 5; -} - -QLabel { + border: 1px solid #090909; + border-radius: 3px; + background-color: #201f1f; color: #ffffff; + padding: 2; + min-width: 10em; + qproperty-alignment: AlignCenter; } #PypeMenu { border: 1px solid #fef9ef; } -#Spacer { +QVBoxLayout { background-color: #282828; } -#ContentLayout { +#Devider { + border: 1px solid #090909; background-color: #585858; } + +QLabel { + color: #77776b; +} diff --git a/pype/hosts/resolve/plugin.py b/pype/hosts/resolve/plugin.py index 8c5fd321d1..4e7ac80add 100644 --- a/pype/hosts/resolve/plugin.py +++ b/pype/hosts/resolve/plugin.py @@ -1,4 +1,3 @@ -import sys import re from avalon import api from pype.hosts import resolve @@ -13,10 +12,10 @@ class Universal_widget(QtWidgets.QDialog): # output items items = dict() - def __init__(self, name, presets, parent=None): + def __init__(self, name, info, presets, parent=None): super(Universal_widget, self).__init__(parent) - self.setObjectName("PypeCreatorInput") + self.setObjectName(name) self.setWindowFlags( QtCore.Qt.Window @@ -25,18 +24,25 @@ class Universal_widget(QtWidgets.QDialog): | QtCore.Qt.WindowCloseButtonHint | QtCore.Qt.WindowStaysOnTopHint ) - self.setWindowTitle("CreatorInput") + self.setWindowTitle(name or "Pype Creator Input") # Where inputs and labels are set - content_widget = QtWidgets.QWidget(self) - self.content_layout = QtWidgets.QFormLayout(content_widget) - self.content_layout.setObjectName("ContentLayout") + self.content_widget = [QtWidgets.QWidget(self)] + top_layout = QtWidgets.QFormLayout(self.content_widget[0]) + top_layout.setObjectName("ContentLayout") + top_layout.addWidget(Spacer(5, self)) # first add widget tag line - self.create_row("QLabel", name) + top_layout.addWidget(QtWidgets.QLabel(info)) + + top_layout.addWidget(Spacer(5, self)) + + # main dynamic layout + self.content_widget.append(QtWidgets.QWidget(self)) + content_layout = QtWidgets.QFormLayout(self.content_widget[-1]) # add preset data into input widget layout - self.add_presets_to_layout(presets) + self.items = self.add_presets_to_layout(content_layout, presets) # Confirmation buttons btns_widget = QtWidgets.QWidget(self) @@ -50,10 +56,13 @@ class Universal_widget(QtWidgets.QDialog): # Main layout of the dialog main_layout = QtWidgets.QVBoxLayout(self) - main_layout.setContentsMargins(10, 20, 10, 20) + main_layout.setContentsMargins(10, 10, 10, 10) main_layout.setSpacing(0) - main_layout.addWidget(content_widget) + # adding content widget + for w in self.content_widget: + main_layout.addWidget(w) + main_layout.addWidget(btns_widget) ok_btn.clicked.connect(self._on_ok_clicked) @@ -63,28 +72,34 @@ class Universal_widget(QtWidgets.QDialog): self.setStyleSheet(stylesheet) def _on_ok_clicked(self): - self.value() + self.result = self.value(self.items) self.close() def _on_cancel_clicked(self): self.result = None self.close() - def value(self): - for k, v in self.items.items(): - if getattr(v, "value", None): + def value(self, data): + for k, v in data.items(): + if isinstance(v, dict): + print(f"nested: {k}") + data[k] = self.value(v) + elif getattr(v, "value", None): + print(f"normal int: {k}") result = getattr(v, "value") + data[k] = result() else: + print(f"normal text: {k}") result = getattr(v, "text") - self.items[k] = result() - self.result = self.items + data[k] = result() + return data def camel_case_split(self, text): matches = re.finditer( '.+?(?:(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])|$)', text) return " ".join([str(m.group(0)).capitalize() for m in matches]) - def create_row(self, type, text, **kwargs): + def create_row(self, layout, type, text, **kwargs): # get type attribute from qwidgets attr = getattr(QtWidgets, type) @@ -93,6 +108,7 @@ class Universal_widget(QtWidgets.QDialog): # assign the new text to lable widget label = QtWidgets.QLabel(label_text) + label.setObjectName("LineLabel") # create attribute name text strip of spaces attr_name = text.replace(" ", "") @@ -110,25 +126,57 @@ class Universal_widget(QtWidgets.QDialog): func_attr = getattr(item, func) func_attr(val) - self.content_layout.addRow(label, item) + # add to layout + layout.addRow(label, item) + return item - def add_presets_to_layout(self, data): + def add_presets_to_layout(self, content_layout, data): for k, v in data.items(): if isinstance(v, dict): - # if nested dict then create label - # TODO: create also new layout - self.create_row("QLabel", k) - self.add_presets_to_layout(v) - elif isinstance(v, str): - item = self.create_row("QLineEdit", k, setText=v) - elif isinstance(v, int): - item = self.create_row("QSpinBox", k, setValue=v) + # adding spacer between sections + self.content_widget.append(QtWidgets.QWidget(self)) + devider = QtWidgets.QVBoxLayout(self.content_widget[-1]) + devider.addWidget(Spacer(5, self)) + devider.setObjectName("Devider") - # add it to items for later requests - self.items.update({ - k: item - }) + # adding nested layout with label + self.content_widget.append(QtWidgets.QWidget(self)) + nested_content_layout = QtWidgets.QFormLayout( + self.content_widget[-1]) + nested_content_layout.setObjectName("NestedContentLayout") + + # add nested key as label + self.create_row(nested_content_layout, "QLabel", k) + data[k] = self.add_presets_to_layout(nested_content_layout, v) + elif isinstance(v, str): + print(f"layout.str: {k}") + print(f"content_layout: {content_layout}") + data[k] = self.create_row( + content_layout, "QLineEdit", k, setText=v) + elif isinstance(v, int): + print(f"layout.int: {k}") + print(f"content_layout: {content_layout}") + data[k] = self.create_row( + content_layout, "QSpinBox", k, setValue=v) + return data + + +class Spacer(QtWidgets.QWidget): + def __init__(self, height, *args, **kwargs): + super(self.__class__, self).__init__(*args, **kwargs) + + self.setFixedHeight(height) + + real_spacer = QtWidgets.QWidget(self) + real_spacer.setObjectName("Spacer") + real_spacer.setFixedHeight(height) + + layout = QtWidgets.QVBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.addWidget(real_spacer) + + self.setLayout(layout) def get_reference_node_parents(ref): diff --git a/pype/plugins/resolve/create/create_shot_clip.py b/pype/plugins/resolve/create/create_shot_clip.py index 18759a2f98..43207743e2 100644 --- a/pype/plugins/resolve/create/create_shot_clip.py +++ b/pype/plugins/resolve/create/create_shot_clip.py @@ -11,7 +11,8 @@ class CreateShotClip(resolve.Creator): icon = "film" defaults = ["Main"] - gui_name = "Define sequencial rename" + gui_name = "Pype sequencial rename with hirerarchy" + gui_info = "Define sequencial rename and fill hierarchy data." presets = None def process(self): @@ -20,14 +21,11 @@ class CreateShotClip(resolve.Creator): if len(self.selected) < 1: return - widget = self.widget(self.gui_name, self.presets) + widget = self.widget(self.gui_name, self.gui_info, self.presets) widget.exec_() - print(widget.result) - if widget.result: - print("success") - return - else: + if not widget.result: + print("Operation aborted") return # sequence attrs @@ -43,8 +41,15 @@ class CreateShotClip(resolve.Creator): lib.rename_add = 0 for i, t_data in enumerate(self.selected): lib.rename_index = i - print(t_data) + + # clear color after it is done + t_data["clip"]["item"].ClearClipColor() + # convert track item to timeline media pool item c_clip = resolve.create_compound_clip( - t_data, mp_folder, rename=True, **dict( - {"presets": self.presets})) + t_data, + mp_folder, + rename=True, + **dict( + {"presets": widget.result}) + ) From 5df2c326142b5434339e3b56d9887cc8816bee2f Mon Sep 17 00:00:00 2001 From: "petr.kalis" Date: Fri, 3 Jul 2020 17:16:26 +0200 Subject: [PATCH 013/813] WIP POC imlementation of Websocket Json RPC server Includes two testing clients Requires: - adding wsrpc-aiohttp==3.0.1 to pypeapp/requirements.txt - adding code to \pype-config\presets\tray\menu_items.json , { "title": "Websocket Server", "type": "module", "import_path": "pype.modules.websocket_server", "fromlist": ["pype","modules"] } --- pype/modules/websocket_server/__init__.py | 5 + .../websocket_server/external_app_1.py | 46 +++++ .../test_client/wsrpc_client.html | 179 ++++++++++++++++++ .../test_client/wsrpc_client.py | 34 ++++ .../websocket_server/websocket_server.py | 129 +++++++++++++ 5 files changed, 393 insertions(+) create mode 100644 pype/modules/websocket_server/__init__.py create mode 100644 pype/modules/websocket_server/external_app_1.py create mode 100644 pype/modules/websocket_server/test_client/wsrpc_client.html create mode 100644 pype/modules/websocket_server/test_client/wsrpc_client.py create mode 100644 pype/modules/websocket_server/websocket_server.py diff --git a/pype/modules/websocket_server/__init__.py b/pype/modules/websocket_server/__init__.py new file mode 100644 index 0000000000..eb5a0d9f27 --- /dev/null +++ b/pype/modules/websocket_server/__init__.py @@ -0,0 +1,5 @@ +from .websocket_server import WebSocketServer + + +def tray_init(tray_widget, main_widget): + return WebSocketServer() diff --git a/pype/modules/websocket_server/external_app_1.py b/pype/modules/websocket_server/external_app_1.py new file mode 100644 index 0000000000..34a43a4d23 --- /dev/null +++ b/pype/modules/websocket_server/external_app_1.py @@ -0,0 +1,46 @@ +import asyncio + +from pype.api import Logger +from wsrpc_aiohttp import WebSocketRoute + +log = Logger().get_logger("WebsocketServer") + +class ExternalApp1(WebSocketRoute): + """ + One route, mimicking external application (like Harmony, etc). + All functions could be called from client. + 'do_notify' function calls function on the client - mimicking + notification after long running job on the server or similar + """ + + def init(self, **kwargs): + # Python __init__ must be return "self". + # This method might return anything. + log.debug("someone called ExternalApp1 route") + return kwargs + + async def server_function_one(self): + log.info('In function one') + + async def server_function_two(self): + log.info('In function two') + return 'function two' + + async def server_function_three(self): + log.info('In function three') + asyncio.ensure_future(self.do_notify()) + return '{"message":"function tree"}' + + async def server_function_four(self, *args, **kwargs): + log.info('In function four args {} kwargs {}'.format(args, kwargs)) + ret = dict(**kwargs) + ret["message"] = "function four received arguments" + return str(ret) + + # This method calls function on the client side + async def do_notify(self): + import time + time.sleep(5) + log.info('Calling function on server after delay') + awesome = 'Somebody server_function_three method!' + await self.socket.call('notify', result=awesome) diff --git a/pype/modules/websocket_server/test_client/wsrpc_client.html b/pype/modules/websocket_server/test_client/wsrpc_client.html new file mode 100644 index 0000000000..9c3f469aca --- /dev/null +++ b/pype/modules/websocket_server/test_client/wsrpc_client.html @@ -0,0 +1,179 @@ + + + + + Title + + + + + + + + + + + + + +
+
Test of wsrpc javascript client
+ +
+ +
+
+
+
+

No return value

+
+
+
    +
  • Calls server_function_one
  • +
  • Function only logs on server
  • +
  • No return value
  • +
  •  
  • +
  •  
  • +
  •  
  • +
+ +
+
+
+
+

Return value

+
+
+
    +
  • Calls server_function_two
  • +
  • Function logs on server
  • +
  • Returns simple text value
  • +
  •  
  • +
  •  
  • +
  •  
  • +
+ +
+
+
+
+

Notify

+
+
+
    +
  • Calls server_function_three
  • +
  • Function logs on server
  • +
  • Returns json payload
  • +
  • Server then calls function ON the client after delay
  • +
  •  
  • +
+ +
+
+
+
+

Send value

+
+
+
    +
  • Calls server_function_four
  • +
  • Function logs on server
  • +
  • Returns modified sent values
  • +
  •  
  • +
  •  
  • +
  •  
  • +
+ +
+
+
+
+ + + \ No newline at end of file diff --git a/pype/modules/websocket_server/test_client/wsrpc_client.py b/pype/modules/websocket_server/test_client/wsrpc_client.py new file mode 100644 index 0000000000..ef861513ae --- /dev/null +++ b/pype/modules/websocket_server/test_client/wsrpc_client.py @@ -0,0 +1,34 @@ +import asyncio + +from wsrpc_aiohttp import WSRPCClient + +""" + Simple testing Python client for wsrpc_aiohttp + Calls sequentially multiple methods on server +""" + +loop = asyncio.get_event_loop() + + +async def main(): + print("main") + client = WSRPCClient("ws://127.0.0.1:8099/ws/", + loop=asyncio.get_event_loop()) + + client.add_route('notify', notify) + await client.connect() + print("connected") + print(await client.proxy.ExternalApp1.server_function_one()) + print(await client.proxy.ExternalApp1.server_function_two()) + print(await client.proxy.ExternalApp1.server_function_three()) + print(await client.proxy.ExternalApp1.server_function_four(foo="one")) + await client.close() + + +def notify(socket, *args, **kwargs): + print("called from server") + + +if __name__ == "__main__": + # loop.run_until_complete(main()) + asyncio.run(main()) diff --git a/pype/modules/websocket_server/websocket_server.py b/pype/modules/websocket_server/websocket_server.py new file mode 100644 index 0000000000..ce5f23180a --- /dev/null +++ b/pype/modules/websocket_server/websocket_server.py @@ -0,0 +1,129 @@ +from pype.api import config, Logger +from Qt import QtCore + +from aiohttp import web, WSCloseCode +import asyncio +import weakref +from wsrpc_aiohttp import STATIC_DIR, WebSocketAsync + +from . import external_app_1 + +log = Logger().get_logger("WebsocketServer") + +class WebSocketServer(): + """ + Basic POC implementation of asychronic websocket RPC server. + Uses class in external_app_1.py to mimic implementation for single + external application. + 'test_client' folder contains two test implementations of client + + WIP + """ + + def __init__(self): + self.qaction = None + self.failed_icon = None + self._is_running = False + default_port = 8099 + + try: + self.presets = config.get_presets()["services"]["websocket_server"] + except Exception: + self.presets = {"default_port": default_port, "exclude_ports": []} + log.debug(( + "There are not set presets for WebsocketServer." + " Using defaults \"{}\"" + ).format(str(self.presets))) + + self.app = web.Application() + self.app["websockets"] = weakref.WeakSet() + + self.app.router.add_route("*", "/ws/", WebSocketAsync) + self.app.router.add_static("/js", STATIC_DIR) + self.app.router.add_static("/", ".") + + # add route with multiple methods for single "external app" + WebSocketAsync.add_route('ExternalApp1', external_app_1.ExternalApp1) + + self.app.on_shutdown.append(self.on_shutdown) + + self.websocket_thread = WebsocketServerThread(self, default_port) + + + def add_routes_for_class(self, cls): + ''' Probably obsolete, use classes inheriting from WebSocketRoute ''' + methods = [method for method in dir(cls) if '__' not in method] + log.info("added routes for {}".format(methods)) + for method in methods: + WebSocketAsync.add_route(method, getattr(cls, method)) + + def tray_start(self): + self.websocket_thread.start() + + # log.info("Starting websocket server") + # loop = asyncio.get_event_loop() + # self.runner = web.AppRunner(self.app) + # loop.run_until_complete(self.runner.setup()) + # self.site = web.TCPSite(self.runner, 'localhost', 8044) + # loop.run_until_complete(self.site.start()) + # log.info('site {}'.format(self.site._server)) + # asyncio.ensure_future() + # #loop.run_forever() + # #web.run_app(self.app, port=8044) + # log.info("Started websocket server") + + @property + def is_running(self): + return self.websocket_thread.is_running + + def stop(self): + self.websocket_thread.is_running = False + + def thread_stopped(self): + self._is_running = False + + async def on_shutdown(self): + """ + Gracefully remove all connected websocket connections + :return: None + """ + log.info('Shutting down websocket server') + for ws in set(self.app['websockets']): + await ws.close(code=WSCloseCode.GOING_AWAY, + message='Server shutdown') + +class WebsocketServerThread(QtCore.QThread): + """ Listener for websocket rpc requests. + + It would be probably better to "attach" this to main thread (as for + example Harmony needs to run something on main thread), but currently + it creates separate thread and separate asyncio event loop + """ + def __init__(self, module, port): + super(WebsocketServerThread, self).__init__() + self.is_running = False + self.port = port + self.module = module + + def run(self): + self.is_running = True + + try: + log.debug( + "Running Websocket server on URL:" + " \"ws://localhost:{}\"".format(self.port) + ) + + log.info("Starting websocket server") + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + web.run_app(self.module.app, port=self.port) # blocking + log.info("Started websocket server") + + except Exception: + log.warning( + "Websocket Server service has failed", exc_info=True + ) + + self.is_running = False + self.module.thread_stopped() From d6485030b5d3f1f19f1c7535fffd00ff2bf5d934 Mon Sep 17 00:00:00 2001 From: "petr.kalis" Date: Fri, 3 Jul 2020 17:31:53 +0200 Subject: [PATCH 014/813] Hound --- pype/modules/websocket_server/external_app_1.py | 1 + pype/modules/websocket_server/websocket_server.py | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/pype/modules/websocket_server/external_app_1.py b/pype/modules/websocket_server/external_app_1.py index 34a43a4d23..9352787175 100644 --- a/pype/modules/websocket_server/external_app_1.py +++ b/pype/modules/websocket_server/external_app_1.py @@ -5,6 +5,7 @@ from wsrpc_aiohttp import WebSocketRoute log = Logger().get_logger("WebsocketServer") + class ExternalApp1(WebSocketRoute): """ One route, mimicking external application (like Harmony, etc). diff --git a/pype/modules/websocket_server/websocket_server.py b/pype/modules/websocket_server/websocket_server.py index ce5f23180a..29b08a1c6d 100644 --- a/pype/modules/websocket_server/websocket_server.py +++ b/pype/modules/websocket_server/websocket_server.py @@ -10,6 +10,7 @@ from . import external_app_1 log = Logger().get_logger("WebsocketServer") + class WebSocketServer(): """ Basic POC implementation of asychronic websocket RPC server. @@ -31,8 +32,8 @@ class WebSocketServer(): except Exception: self.presets = {"default_port": default_port, "exclude_ports": []} log.debug(( - "There are not set presets for WebsocketServer." - " Using defaults \"{}\"" + "There are not set presets for WebsocketServer." + " Using defaults \"{}\"" ).format(str(self.presets))) self.app = web.Application() @@ -49,9 +50,8 @@ class WebSocketServer(): self.websocket_thread = WebsocketServerThread(self, default_port) - def add_routes_for_class(self, cls): - ''' Probably obsolete, use classes inheriting from WebSocketRoute ''' + """ Probably obsolete, use classes inheriting from WebSocketRoute """ methods = [method for method in dir(cls) if '__' not in method] log.info("added routes for {}".format(methods)) for method in methods: @@ -92,6 +92,7 @@ class WebSocketServer(): await ws.close(code=WSCloseCode.GOING_AWAY, message='Server shutdown') + class WebsocketServerThread(QtCore.QThread): """ Listener for websocket rpc requests. From a29c84e9af7180df2a2b12b23bf1d79a91d13315 Mon Sep 17 00:00:00 2001 From: "petr.kalis" Date: Wed, 8 Jul 2020 16:14:07 +0200 Subject: [PATCH 015/813] Fix - changed usage of QThread to threading.Thread Apparently QThread is making problems on some non-gui linux distros --- pype/modules/websocket_server/websocket_server.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/modules/websocket_server/websocket_server.py b/pype/modules/websocket_server/websocket_server.py index 29b08a1c6d..de16891ee1 100644 --- a/pype/modules/websocket_server/websocket_server.py +++ b/pype/modules/websocket_server/websocket_server.py @@ -1,6 +1,6 @@ from pype.api import config, Logger -from Qt import QtCore +import threading from aiohttp import web, WSCloseCode import asyncio import weakref @@ -93,7 +93,7 @@ class WebSocketServer(): message='Server shutdown') -class WebsocketServerThread(QtCore.QThread): +class WebsocketServerThread(threading.Thread): """ Listener for websocket rpc requests. It would be probably better to "attach" this to main thread (as for From d02e9596a665188249e5f096af21c644f71edd5d Mon Sep 17 00:00:00 2001 From: "petr.kalis" Date: Thu, 9 Jul 2020 12:30:55 +0200 Subject: [PATCH 016/813] Added separate 'hosts' folder Added auto-routing of classes from selected folders --- .../websocket_server/hosts/__init__.py | 0 .../{ => hosts}/external_app_1.py | 0 .../websocket_server/websocket_server.py | 45 ++++++++++++++----- 3 files changed, 35 insertions(+), 10 deletions(-) create mode 100644 pype/modules/websocket_server/hosts/__init__.py rename pype/modules/websocket_server/{ => hosts}/external_app_1.py (100%) diff --git a/pype/modules/websocket_server/hosts/__init__.py b/pype/modules/websocket_server/hosts/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/pype/modules/websocket_server/external_app_1.py b/pype/modules/websocket_server/hosts/external_app_1.py similarity index 100% rename from pype/modules/websocket_server/external_app_1.py rename to pype/modules/websocket_server/hosts/external_app_1.py diff --git a/pype/modules/websocket_server/websocket_server.py b/pype/modules/websocket_server/websocket_server.py index de16891ee1..8c8cb7be67 100644 --- a/pype/modules/websocket_server/websocket_server.py +++ b/pype/modules/websocket_server/websocket_server.py @@ -6,7 +6,10 @@ import asyncio import weakref from wsrpc_aiohttp import STATIC_DIR, WebSocketAsync -from . import external_app_1 +import os +import sys +import pyclbr +import importlib log = Logger().get_logger("WebsocketServer") @@ -32,8 +35,8 @@ class WebSocketServer(): except Exception: self.presets = {"default_port": default_port, "exclude_ports": []} log.debug(( - "There are not set presets for WebsocketServer." - " Using defaults \"{}\"" + "There are not set presets for WebsocketServer." + " Using defaults \"{}\"" ).format(str(self.presets))) self.app = web.Application() @@ -44,18 +47,40 @@ class WebSocketServer(): self.app.router.add_static("/", ".") # add route with multiple methods for single "external app" - WebSocketAsync.add_route('ExternalApp1', external_app_1.ExternalApp1) + directories_with_routes = ['hosts'] + self.add_routes_for_directories(directories_with_routes) self.app.on_shutdown.append(self.on_shutdown) self.websocket_thread = WebsocketServerThread(self, default_port) - def add_routes_for_class(self, cls): - """ Probably obsolete, use classes inheriting from WebSocketRoute """ - methods = [method for method in dir(cls) if '__' not in method] - log.info("added routes for {}".format(methods)) - for method in methods: - WebSocketAsync.add_route(method, getattr(cls, method)) + def add_routes_for_directories(self, directories_with_routes): + """ Loops through selected directories to find all modules and + in them all classes implementing 'WebSocketRoute' that could be + used as route. + All methods in these classes are registered automatically. + """ + for dir_name in directories_with_routes: + dir_name = os.path.join(os.path.dirname(__file__), dir_name) + for file_name in os.listdir(dir_name): + if '.py' in file_name and '__' not in file_name: + self.add_routes_for_module(file_name, dir_name) + + def add_routes_for_module(self, file_name, dir_name): + """ Auto routes for all classes implementing 'WebSocketRoute' + in 'file_name' in 'dir_name' + """ + module_name = file_name.replace('.py', '') + module_info = pyclbr.readmodule(module_name, [dir_name]) + + for class_name, cls_object in module_info.items(): + sys.path.append(dir_name) + if 'WebSocketRoute' in cls_object.super: + log.debug('Adding route for {}'.format(class_name)) + module = importlib.import_module(module_name) + cls = getattr(module, class_name) + WebSocketAsync.add_route(class_name, cls) + sys.path.pop() def tray_start(self): self.websocket_thread.start() From dc7a1042145b9eb3ee5c20424b8c352a3e13affb Mon Sep 17 00:00:00 2001 From: "petr.kalis" Date: Fri, 10 Jul 2020 11:15:46 +0200 Subject: [PATCH 017/813] Added proper shutdown --- .../websocket_server/websocket_server.py | 98 ++++++++++++------- 1 file changed, 65 insertions(+), 33 deletions(-) diff --git a/pype/modules/websocket_server/websocket_server.py b/pype/modules/websocket_server/websocket_server.py index 8c8cb7be67..87f9bde8e0 100644 --- a/pype/modules/websocket_server/websocket_server.py +++ b/pype/modules/websocket_server/websocket_server.py @@ -1,9 +1,8 @@ from pype.api import config, Logger import threading -from aiohttp import web, WSCloseCode +from aiohttp import web import asyncio -import weakref from wsrpc_aiohttp import STATIC_DIR, WebSocketAsync import os @@ -40,7 +39,6 @@ class WebSocketServer(): ).format(str(self.presets))) self.app = web.Application() - self.app["websockets"] = weakref.WeakSet() self.app.router.add_route("*", "/ws/", WebSocketAsync) self.app.router.add_static("/js", STATIC_DIR) @@ -50,8 +48,6 @@ class WebSocketServer(): directories_with_routes = ['hosts'] self.add_routes_for_directories(directories_with_routes) - self.app.on_shutdown.append(self.on_shutdown) - self.websocket_thread = WebsocketServerThread(self, default_port) def add_routes_for_directories(self, directories_with_routes): @@ -85,38 +81,33 @@ class WebSocketServer(): def tray_start(self): self.websocket_thread.start() - # log.info("Starting websocket server") - # loop = asyncio.get_event_loop() - # self.runner = web.AppRunner(self.app) - # loop.run_until_complete(self.runner.setup()) - # self.site = web.TCPSite(self.runner, 'localhost', 8044) - # loop.run_until_complete(self.site.start()) - # log.info('site {}'.format(self.site._server)) - # asyncio.ensure_future() - # #loop.run_forever() - # #web.run_app(self.app, port=8044) - # log.info("Started websocket server") + def tray_exit(self): + self.stop() + + def stop_websocket_server(self): + + self.stop() @property def is_running(self): return self.websocket_thread.is_running def stop(self): - self.websocket_thread.is_running = False + if not self.is_running: + return + try: + log.debug("Stopping websocket server") + self.websocket_thread.is_running = False + self.websocket_thread.stop() + except Exception: + log.warning( + "Error has happened during Killing websocket server", + exc_info=True + ) def thread_stopped(self): self._is_running = False - async def on_shutdown(self): - """ - Gracefully remove all connected websocket connections - :return: None - """ - log.info('Shutting down websocket server') - for ws in set(self.app['websockets']): - await ws.close(code=WSCloseCode.GOING_AWAY, - message='Server shutdown') - class WebsocketServerThread(threading.Thread): """ Listener for websocket rpc requests. @@ -130,26 +121,67 @@ class WebsocketServerThread(threading.Thread): self.is_running = False self.port = port self.module = module + self.loop = None + self.runner = None + self.site = None def run(self): self.is_running = True try: + log.info("Starting websocket server") + self.loop = asyncio.new_event_loop() # create new loop for thread + asyncio.set_event_loop(self.loop) + + self.loop.run_until_complete(self.start_server()) + log.debug( "Running Websocket server on URL:" " \"ws://localhost:{}\"".format(self.port) ) - log.info("Starting websocket server") - loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) - web.run_app(self.module.app, port=self.port) # blocking - log.info("Started websocket server") - + asyncio.ensure_future(self.check_shutdown(), loop=self.loop) + self.loop.run_forever() except Exception: log.warning( "Websocket Server service has failed", exc_info=True ) + finally: + self.loop.close() # optional self.is_running = False self.module.thread_stopped() + log.info("Websocket server stopped") + + async def start_server(self): + """ Starts runner and TCPsite """ + self.runner = web.AppRunner(self.module.app) + await self.runner.setup() + self.site = web.TCPSite(self.runner, 'localhost', self.port) + await self.site.start() + + def stop(self): + """ Sets is_running flag to false, 'check_shutdown' shuts server down""" + self.is_running = False + + async def check_shutdown(self): + """ Future that is running and checks if server should be running + periodically. + """ + while self.is_running: + await asyncio.sleep(0.5) + + log.debug("Starting shutdown") + await self.site.stop() + log.debug("Site stopped") + await self.runner.cleanup() + log.debug("Runner stopped") + tasks = [task for task in asyncio.all_tasks() if + task is not asyncio.current_task()] + list(map(lambda task: task.cancel(), tasks)) # cancel all the tasks + results = await asyncio.gather(*tasks, return_exceptions=True) + log.debug(f'Finished awaiting cancelled tasks, results: {results}...') + await self.loop.shutdown_asyncgens() + # to really make sure everything else has time to stop + await asyncio.sleep(0.07) + self.loop.stop() From 75ecb02262ef11e43b5ef5164cffc4c14cad08e5 Mon Sep 17 00:00:00 2001 From: "petr.kalis" Date: Fri, 10 Jul 2020 11:25:22 +0200 Subject: [PATCH 018/813] Hound --- 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 87f9bde8e0..56e71ea895 100644 --- a/pype/modules/websocket_server/websocket_server.py +++ b/pype/modules/websocket_server/websocket_server.py @@ -161,7 +161,7 @@ class WebsocketServerThread(threading.Thread): await self.site.start() def stop(self): - """ Sets is_running flag to false, 'check_shutdown' shuts server down""" + """Sets is_running flag to false, 'check_shutdown' shuts server down""" self.is_running = False async def check_shutdown(self): From 76fe88fbccab959ff8c333dcb88e06e950919a5c Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 13 Jul 2020 10:19:58 +0200 Subject: [PATCH 019/813] feat(resolve): config moved to plugins and widget rename --- pype/hosts/resolve/plugin.py | 4 +-- .../resolve/create/create_shot_clip.py | 32 ++++++++++++++++--- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/pype/hosts/resolve/plugin.py b/pype/hosts/resolve/plugin.py index 4e7ac80add..29e544cb47 100644 --- a/pype/hosts/resolve/plugin.py +++ b/pype/hosts/resolve/plugin.py @@ -7,7 +7,7 @@ from pype.api import config from Qt import QtWidgets, QtCore -class Universal_widget(QtWidgets.QDialog): +class Creator_widget(QtWidgets.QDialog): # output items items = dict() @@ -270,4 +270,4 @@ class Creator(api.Creator): else: self.selected = resolve.get_current_track_items(filter=False) - self.widget = Universal_widget + self.widget = Creator_widget diff --git a/pype/plugins/resolve/create/create_shot_clip.py b/pype/plugins/resolve/create/create_shot_clip.py index 43207743e2..e4b3e8fe2a 100644 --- a/pype/plugins/resolve/create/create_shot_clip.py +++ b/pype/plugins/resolve/create/create_shot_clip.py @@ -13,17 +13,41 @@ class CreateShotClip(resolve.Creator): gui_name = "Pype sequencial rename with hirerarchy" gui_info = "Define sequencial rename and fill hierarchy data." + gui_inputs = { + "clipName": "{episode}{sequence}{shot}", + "hierarchy": "{folder}/{sequence}/{shot}", + "countFrom": 10, + "steps": 10, + "hierarchyData": { + "folder": "shots", + "shot": "sh####", + "track": "{track}", + "sequence": "sc010", + "episode": "ep01" + } + } presets = None def process(self): - print(f"__ selected_clips: {self.selected}") + # solve gui inputs overwrites from presets + # overwrite gui inputs from presets + for k, v in self.gui_inputs.items(): + if isinstance(v, dict): + # nested dictionary (only one level allowed) + for _k, _v in v.items(): + if self.presets.get(_k): + self.gui_inputs[k][_k] = self.presets[_k] + if self.presets.get(k): + self.gui_inputs[k] = self.presets[k] + # open widget for plugins inputs + widget = self.widget(self.gui_name, self.gui_info, self.gui_inputs) + widget.exec_() + + print(f"__ selected_clips: {self.selected}") if len(self.selected) < 1: return - widget = self.widget(self.gui_name, self.gui_info, self.presets) - widget.exec_() - if not widget.result: print("Operation aborted") return From 309515461aeee31f8d2df4276f7b5942fe240627 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 13 Jul 2020 10:31:11 +0200 Subject: [PATCH 020/813] feat(resolve): hound fixes --- pype/hosts/resolve/plugin.py | 4 ++-- pype/plugins/resolve/publish/collect_clips.py | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/pype/hosts/resolve/plugin.py b/pype/hosts/resolve/plugin.py index 29e544cb47..67f2820990 100644 --- a/pype/hosts/resolve/plugin.py +++ b/pype/hosts/resolve/plugin.py @@ -7,7 +7,7 @@ from pype.api import config from Qt import QtWidgets, QtCore -class Creator_widget(QtWidgets.QDialog): +class CreatorWidget(QtWidgets.QDialog): # output items items = dict() @@ -270,4 +270,4 @@ class Creator(api.Creator): else: self.selected = resolve.get_current_track_items(filter=False) - self.widget = Creator_widget + self.widget = CreatorWidget diff --git a/pype/plugins/resolve/publish/collect_clips.py b/pype/plugins/resolve/publish/collect_clips.py index 0f02f26f2e..f86e5c8384 100644 --- a/pype/plugins/resolve/publish/collect_clips.py +++ b/pype/plugins/resolve/publish/collect_clips.py @@ -3,6 +3,7 @@ from pyblish import api from pype.hosts import resolve import json + class CollectClips(api.ContextPlugin): """Collect all Track items selection.""" @@ -156,4 +157,6 @@ class CollectClips(api.ContextPlugin): "_clipIn": clip_in, "_clipOut": clip_out } - self.log.info("context.data[\"assetsShared\"]: {}".format(context.data["assetsShared"])) + self.log.info( + "context.data[\"assetsShared\"]: {}".format( + context.data["assetsShared"])) From e259a55af9e1efe648842cc3a94bfca6d34c0f62 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 13 Jul 2020 10:42:26 +0200 Subject: [PATCH 021/813] feat(resolve): hound fixes --- pype/hosts/resolve/plugin.py | 6 +++--- pype/hosts/resolve/utility_scripts/test.py | 4 +++- pype/plugins/resolve/create/create_shot_clip.py | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/pype/hosts/resolve/plugin.py b/pype/hosts/resolve/plugin.py index 67f2820990..72eec04896 100644 --- a/pype/hosts/resolve/plugin.py +++ b/pype/hosts/resolve/plugin.py @@ -13,7 +13,7 @@ class CreatorWidget(QtWidgets.QDialog): items = dict() def __init__(self, name, info, presets, parent=None): - super(Universal_widget, self).__init__(parent) + super(CreatorWidget, self).__init__(parent) self.setObjectName(name) @@ -86,11 +86,11 @@ class CreatorWidget(QtWidgets.QDialog): data[k] = self.value(v) elif getattr(v, "value", None): print(f"normal int: {k}") - result = getattr(v, "value") + result = v.value() data[k] = result() else: print(f"normal text: {k}") - result = getattr(v, "text") + result = v.text() data[k] = result() return data diff --git a/pype/hosts/resolve/utility_scripts/test.py b/pype/hosts/resolve/utility_scripts/test.py index cf7db3b7e5..69dc4768bd 100644 --- a/pype/hosts/resolve/utility_scripts/test.py +++ b/pype/hosts/resolve/utility_scripts/test.py @@ -1,6 +1,8 @@ #! python3 import sys from pype.api import Logger +import DaVinciResolveScript as bmdvr + log = Logger().get_logger(__name__) @@ -10,7 +12,7 @@ def main(): bm = bmdvr.utils.get_resolve_module() log.info(f"blackmagicmodule: {bm}") -import DaVinciResolveScript as bmdvr + print(f"_>> bmdvr.scriptapp(Resolve): {bmdvr.scriptapp('Resolve')}") diff --git a/pype/plugins/resolve/create/create_shot_clip.py b/pype/plugins/resolve/create/create_shot_clip.py index e4b3e8fe2a..bd2e013fac 100644 --- a/pype/plugins/resolve/create/create_shot_clip.py +++ b/pype/plugins/resolve/create/create_shot_clip.py @@ -70,10 +70,10 @@ class CreateShotClip(resolve.Creator): t_data["clip"]["item"].ClearClipColor() # convert track item to timeline media pool item - c_clip = resolve.create_compound_clip( + resolve.create_compound_clip( t_data, mp_folder, rename=True, **dict( {"presets": widget.result}) - ) + ) From 2909b7453e2117282d7181a84fb850824bf86b5f Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 23 Jul 2020 11:03:44 +0200 Subject: [PATCH 022/813] initial commit --- .../kuba_each_case/global/creator.json | 8 + .../kuba_each_case/global/intents.json | 3 + .../project_presets/ftrack/ftrack_config.json | 11 + .../project_presets/global/creator.json | 8 + .../global/slates/example_HD.json | 212 +++ .../config/project_presets/maya/capture.json | 108 ++ .../project_presets/plugins/config.json | 1 + .../plugins/ftrack/publish.json | 6 + .../plugins/global/create.json | 1 + .../plugins/global/filter.json | 1 + .../project_presets/plugins/global/load.json | 1 + .../plugins/global/publish.json | 73 + .../project_presets/plugins/maya/create.json | 1 + .../project_presets/plugins/maya/filter.json | 9 + .../project_presets/plugins/maya/load.json | 18 + .../project_presets/plugins/maya/publish.json | 17 + .../plugins/maya/workfile_build.json | 54 + .../project_presets/plugins/nuke/create.json | 8 + .../project_presets/plugins/nuke/load.json | 1 + .../project_presets/plugins/nuke/publish.json | 48 + .../plugins/nuke/workfile_build.json | 11 + .../plugins/nukestudio/filter.json | 10 + .../plugins/nukestudio/publish.json | 8 + .../plugins/standalonepublisher/publish.json | 17 + .../project_presets/plugins/test/create.json | 8 + .../project_presets/plugins/test/publish.json | 10 + .../premiere/asset_default.json | 5 + .../project_presets/premiere/rules_tasks.json | 21 + .../project_presets/unreal/project_setup.json | 4 + .../studio_presets/ftrack/server_plugins.json | 1 + .../studio_presets/ftrack/user_plugins.json | 5 + .../studio_presets/global/applications.json | 39 + .../global/es/applications.json | 39 + .../config/studio_presets/global/intents.json | 9 + .../studio_presets/global/tray_items.json | 25 + .../muster/templates_mapping.json | 19 + .../applications_gui_schema.json | 153 ++ .../ftrack_projects_gui_schema.json | 30 + .../config_gui_schema/project_gui_schema.json | 13 + .../config_gui_schema/studio_gui_schema.json | 23 + .../config_gui_schema/tools_gui_schema.json | 29 + pype/tools/config_setting/interface.py | 49 + pype/tools/config_setting/style/__init__.py | 12 + pype/tools/config_setting/style/pype_icon.png | Bin 0 -> 3793 bytes pype/tools/config_setting/style/style.css | 90 ++ pype/tools/config_setting/widgets/__init__.py | 6 + pype/tools/config_setting/widgets/base.py | 282 ++++ pype/tools/config_setting/widgets/config.py | 236 +++ pype/tools/config_setting/widgets/inputs.py | 1346 +++++++++++++++++ pype/tools/config_setting/widgets/lib.py | 44 + pype/tools/config_setting/widgets/main.py | 26 + pype/tools/config_setting/widgets/tests.py | 127 ++ 52 files changed, 3286 insertions(+) create mode 100644 pype/tools/config_setting/config/project_overrides/kuba_each_case/global/creator.json create mode 100644 pype/tools/config_setting/config/project_overrides/kuba_each_case/global/intents.json create mode 100644 pype/tools/config_setting/config/project_presets/ftrack/ftrack_config.json create mode 100644 pype/tools/config_setting/config/project_presets/global/creator.json create mode 100644 pype/tools/config_setting/config/project_presets/global/slates/example_HD.json create mode 100644 pype/tools/config_setting/config/project_presets/maya/capture.json create mode 100644 pype/tools/config_setting/config/project_presets/plugins/config.json create mode 100644 pype/tools/config_setting/config/project_presets/plugins/ftrack/publish.json create mode 100644 pype/tools/config_setting/config/project_presets/plugins/global/create.json create mode 100644 pype/tools/config_setting/config/project_presets/plugins/global/filter.json create mode 100644 pype/tools/config_setting/config/project_presets/plugins/global/load.json create mode 100644 pype/tools/config_setting/config/project_presets/plugins/global/publish.json create mode 100644 pype/tools/config_setting/config/project_presets/plugins/maya/create.json create mode 100644 pype/tools/config_setting/config/project_presets/plugins/maya/filter.json create mode 100644 pype/tools/config_setting/config/project_presets/plugins/maya/load.json create mode 100644 pype/tools/config_setting/config/project_presets/plugins/maya/publish.json create mode 100644 pype/tools/config_setting/config/project_presets/plugins/maya/workfile_build.json create mode 100644 pype/tools/config_setting/config/project_presets/plugins/nuke/create.json create mode 100644 pype/tools/config_setting/config/project_presets/plugins/nuke/load.json create mode 100644 pype/tools/config_setting/config/project_presets/plugins/nuke/publish.json create mode 100644 pype/tools/config_setting/config/project_presets/plugins/nuke/workfile_build.json create mode 100644 pype/tools/config_setting/config/project_presets/plugins/nukestudio/filter.json create mode 100644 pype/tools/config_setting/config/project_presets/plugins/nukestudio/publish.json create mode 100644 pype/tools/config_setting/config/project_presets/plugins/standalonepublisher/publish.json create mode 100644 pype/tools/config_setting/config/project_presets/plugins/test/create.json create mode 100644 pype/tools/config_setting/config/project_presets/plugins/test/publish.json create mode 100644 pype/tools/config_setting/config/project_presets/premiere/asset_default.json create mode 100644 pype/tools/config_setting/config/project_presets/premiere/rules_tasks.json create mode 100644 pype/tools/config_setting/config/project_presets/unreal/project_setup.json create mode 100644 pype/tools/config_setting/config/studio_presets/ftrack/server_plugins.json create mode 100644 pype/tools/config_setting/config/studio_presets/ftrack/user_plugins.json create mode 100644 pype/tools/config_setting/config/studio_presets/global/applications.json create mode 100644 pype/tools/config_setting/config/studio_presets/global/es/applications.json create mode 100644 pype/tools/config_setting/config/studio_presets/global/intents.json create mode 100644 pype/tools/config_setting/config/studio_presets/global/tray_items.json create mode 100644 pype/tools/config_setting/config/studio_presets/muster/templates_mapping.json create mode 100644 pype/tools/config_setting/config_gui_schema/applications_gui_schema.json create mode 100644 pype/tools/config_setting/config_gui_schema/ftrack_projects_gui_schema.json create mode 100644 pype/tools/config_setting/config_gui_schema/project_gui_schema.json create mode 100644 pype/tools/config_setting/config_gui_schema/studio_gui_schema.json create mode 100644 pype/tools/config_setting/config_gui_schema/tools_gui_schema.json create mode 100644 pype/tools/config_setting/interface.py create mode 100644 pype/tools/config_setting/style/__init__.py create mode 100644 pype/tools/config_setting/style/pype_icon.png create mode 100644 pype/tools/config_setting/style/style.css create mode 100644 pype/tools/config_setting/widgets/__init__.py create mode 100644 pype/tools/config_setting/widgets/base.py create mode 100644 pype/tools/config_setting/widgets/config.py create mode 100644 pype/tools/config_setting/widgets/inputs.py create mode 100644 pype/tools/config_setting/widgets/lib.py create mode 100644 pype/tools/config_setting/widgets/main.py create mode 100644 pype/tools/config_setting/widgets/tests.py diff --git a/pype/tools/config_setting/config/project_overrides/kuba_each_case/global/creator.json b/pype/tools/config_setting/config/project_overrides/kuba_each_case/global/creator.json new file mode 100644 index 0000000000..d14e779f01 --- /dev/null +++ b/pype/tools/config_setting/config/project_overrides/kuba_each_case/global/creator.json @@ -0,0 +1,8 @@ +{ + "Model": ["model"], + "Render Globals": ["light", "render"], + "Layout": ["layout"], + "Set Dress": ["setdress"], + "Look": ["look"], + "Rig": ["rigging"] +} diff --git a/pype/tools/config_setting/config/project_overrides/kuba_each_case/global/intents.json b/pype/tools/config_setting/config/project_overrides/kuba_each_case/global/intents.json new file mode 100644 index 0000000000..bf147c7a19 --- /dev/null +++ b/pype/tools/config_setting/config/project_overrides/kuba_each_case/global/intents.json @@ -0,0 +1,3 @@ +{ + "default": "test" +} diff --git a/pype/tools/config_setting/config/project_presets/ftrack/ftrack_config.json b/pype/tools/config_setting/config/project_presets/ftrack/ftrack_config.json new file mode 100644 index 0000000000..c9dbde4596 --- /dev/null +++ b/pype/tools/config_setting/config/project_presets/ftrack/ftrack_config.json @@ -0,0 +1,11 @@ +{ + "status_update": { + "_ignore_": ["in progress", "ommited", "on hold"], + "Ready": ["not ready"], + "In Progress" : ["_any_"] + }, + "status_version_to_task": { + "in progress": "in progress", + "approved": "approved" + } +} diff --git a/pype/tools/config_setting/config/project_presets/global/creator.json b/pype/tools/config_setting/config/project_presets/global/creator.json new file mode 100644 index 0000000000..d14e779f01 --- /dev/null +++ b/pype/tools/config_setting/config/project_presets/global/creator.json @@ -0,0 +1,8 @@ +{ + "Model": ["model"], + "Render Globals": ["light", "render"], + "Layout": ["layout"], + "Set Dress": ["setdress"], + "Look": ["look"], + "Rig": ["rigging"] +} diff --git a/pype/tools/config_setting/config/project_presets/global/slates/example_HD.json b/pype/tools/config_setting/config/project_presets/global/slates/example_HD.json new file mode 100644 index 0000000000..b06391fb63 --- /dev/null +++ b/pype/tools/config_setting/config/project_presets/global/slates/example_HD.json @@ -0,0 +1,212 @@ +{ + "width": 1920, + "height": 1080, + "destination_path": "{destination_path}", + "style": { + "*": { + "font-family": "arial", + "font-color": "#ffffff", + "font-bold": false, + "font-italic": false, + "bg-color": "#0077ff", + "alignment-horizontal": "left", + "alignment-vertical": "top" + }, + "layer": { + "padding": 0, + "margin": 0 + }, + "rectangle": { + "padding": 0, + "margin": 0, + "bg-color": "#E9324B", + "fill": true + }, + "main_frame": { + "padding": 0, + "margin": 0, + "bg-color": "#252525" + }, + "table": { + "padding": 0, + "margin": 0, + "bg-color": "transparent" + }, + "table-item": { + "padding": 5, + "padding-bottom": 10, + "margin": 0, + "bg-color": "#212121", + "bg-alter-color": "#272727", + "font-color": "#dcdcdc", + "font-bold": false, + "font-italic": false, + "alignment-horizontal": "left", + "alignment-vertical": "top", + "word-wrap": false, + "ellide": true, + "max-lines": 1 + }, + "table-item-col[0]": { + "font-size": 20, + "font-color": "#898989", + "font-bold": true, + "ellide": false, + "word-wrap": true, + "max-lines": null + }, + "table-item-col[1]": { + "font-size": 40, + "padding-left": 10 + }, + "#colorbar": { + "bg-color": "#9932CC" + } + }, + "items": [{ + "type": "layer", + "direction": 1, + "name": "MainLayer", + "style": { + "#MainLayer": { + "width": 1094, + "height": 1000, + "margin": 25, + "padding": 0 + }, + "#LeftSide": { + "margin-right": 25 + } + }, + "items": [{ + "type": "layer", + "name": "LeftSide", + "items": [{ + "type": "layer", + "direction": 1, + "style": { + "table-item": { + "bg-color": "transparent", + "padding-bottom": 20 + }, + "table-item-col[0]": { + "font-size": 20, + "font-color": "#898989", + "alignment-horizontal": "right" + }, + "table-item-col[1]": { + "alignment-horizontal": "left", + "font-bold": true, + "font-size": 40 + } + }, + "items": [{ + "type": "table", + "values": [ + ["Show:", "{project[name]}"] + ], + "style": { + "table-item-field[0:0]": { + "width": 150 + }, + "table-item-field[0:1]": { + "width": 580 + } + } + }, { + "type": "table", + "values": [ + ["Submitting For:", "{intent}"] + ], + "style": { + "table-item-field[0:0]": { + "width": 160 + }, + "table-item-field[0:1]": { + "width": 218, + "alignment-horizontal": "right" + } + } + }] + }, { + "type": "rectangle", + "style": { + "bg-color": "#bc1015", + "width": 1108, + "height": 5, + "fill": true + } + }, { + "type": "table", + "use_alternate_color": true, + "values": [ + ["Version name:", "{version_name}"], + ["Date:", "{date}"], + ["Shot Types:", "{shot_type}"], + ["Submission Note:", "{submission_note}"] + ], + "style": { + "table-item": { + "padding-bottom": 20 + }, + "table-item-field[0:1]": { + "font-bold": true + }, + "table-item-field[3:0]": { + "word-wrap": true, + "ellide": true, + "max-lines": 4 + }, + "table-item-col[0]": { + "alignment-horizontal": "right", + "width": 150 + }, + "table-item-col[1]": { + "alignment-horizontal": "left", + "width": 958 + } + } + }] + }, { + "type": "layer", + "name": "RightSide", + "items": [{ + "type": "placeholder", + "name": "thumbnail", + "path": "{thumbnail_path}", + "style": { + "width": 730, + "height": 412 + } + }, { + "type": "placeholder", + "name": "colorbar", + "path": "{color_bar_path}", + "return_data": true, + "style": { + "width": 730, + "height": 55 + } + }, { + "type": "table", + "use_alternate_color": true, + "values": [ + ["Vendor:", "{vendor}"], + ["Shot Name:", "{shot_name}"], + ["Frames:", "{frame_start} - {frame_end} ({duration})"] + ], + "style": { + "table-item-col[0]": { + "alignment-horizontal": "left", + "width": 200 + }, + "table-item-col[1]": { + "alignment-horizontal": "right", + "width": 530, + "font-size": 30 + } + } + }] + }] + }] +} diff --git a/pype/tools/config_setting/config/project_presets/maya/capture.json b/pype/tools/config_setting/config/project_presets/maya/capture.json new file mode 100644 index 0000000000..b6c4893034 --- /dev/null +++ b/pype/tools/config_setting/config/project_presets/maya/capture.json @@ -0,0 +1,108 @@ +{ + "Codec": { + "compression": "jpg", + "format": "image", + "quality": 95 + }, + "Display Options": { + "background": [ + 0.7137254901960784, + 0.7137254901960784, + 0.7137254901960784 + ], + "backgroundBottom": [ + 0.7137254901960784, + 0.7137254901960784, + 0.7137254901960784 + ], + "backgroundTop": [ + 0.7137254901960784, + 0.7137254901960784, + 0.7137254901960784 + ], + "override_display": true + }, + "Generic": { + "isolate_view": true, + "off_screen": true + }, + "IO": { + "name": "", + "open_finished": false, + "raw_frame_numbers": false, + "recent_playblasts": [], + "save_file": false + }, + "PanZoom": { + "pan_zoom": true + }, + "Renderer": { + "rendererName": "vp2Renderer" + }, + "Resolution": { + "height": 1080, + "mode": "Custom", + "percent": 1.0, + "width": 1920 + }, + "Time Range": { + "end_frame": 25, + "frame": "", + "start_frame": 0, + "time": "Time Slider" + }, + "Viewport Options": { + "cameras": false, + "clipGhosts": false, + "controlVertices": false, + "deformers": false, + "dimensions": false, + "displayLights": 0, + "dynamicConstraints": false, + "dynamics": false, + "fluids": false, + "follicles": false, + "gpuCacheDisplayFilter": false, + "greasePencils": false, + "grid": false, + "hairSystems": false, + "handles": false, + "high_quality": true, + "hud": false, + "hulls": false, + "ikHandles": false, + "imagePlane": false, + "joints": false, + "lights": false, + "locators": false, + "manipulators": false, + "motionTrails": false, + "nCloths": false, + "nParticles": false, + "nRigids": false, + "nurbsCurves": false, + "nurbsSurfaces": false, + "override_viewport_options": true, + "particleInstancers": false, + "pivots": false, + "planes": false, + "pluginShapes": false, + "polymeshes": true, + "shadows": false, + "strokes": false, + "subdivSurfaces": false, + "textures": false, + "twoSidedLighting": true + }, + "Camera Options": { + "displayGateMask": false, + "displayResolution": false, + "displayFilmGate": false, + "displayFieldChart": false, + "displaySafeAction": false, + "displaySafeTitle": false, + "displayFilmPivot": false, + "displayFilmOrigin": false, + "overscan": 1.0 + } +} diff --git a/pype/tools/config_setting/config/project_presets/plugins/config.json b/pype/tools/config_setting/config/project_presets/plugins/config.json new file mode 100644 index 0000000000..9e26dfeeb6 --- /dev/null +++ b/pype/tools/config_setting/config/project_presets/plugins/config.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/pype/tools/config_setting/config/project_presets/plugins/ftrack/publish.json b/pype/tools/config_setting/config/project_presets/plugins/ftrack/publish.json new file mode 100644 index 0000000000..d0469ae4f7 --- /dev/null +++ b/pype/tools/config_setting/config/project_presets/plugins/ftrack/publish.json @@ -0,0 +1,6 @@ +{ + "IntegrateFtrackNote": { + "note_with_intent_template": "{intent}: {comment}", + "note_labels": [] + } +} diff --git a/pype/tools/config_setting/config/project_presets/plugins/global/create.json b/pype/tools/config_setting/config/project_presets/plugins/global/create.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/pype/tools/config_setting/config/project_presets/plugins/global/create.json @@ -0,0 +1 @@ +{} diff --git a/pype/tools/config_setting/config/project_presets/plugins/global/filter.json b/pype/tools/config_setting/config/project_presets/plugins/global/filter.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/pype/tools/config_setting/config/project_presets/plugins/global/filter.json @@ -0,0 +1 @@ +{} diff --git a/pype/tools/config_setting/config/project_presets/plugins/global/load.json b/pype/tools/config_setting/config/project_presets/plugins/global/load.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/pype/tools/config_setting/config/project_presets/plugins/global/load.json @@ -0,0 +1 @@ +{} diff --git a/pype/tools/config_setting/config/project_presets/plugins/global/publish.json b/pype/tools/config_setting/config/project_presets/plugins/global/publish.json new file mode 100644 index 0000000000..6e51e00497 --- /dev/null +++ b/pype/tools/config_setting/config/project_presets/plugins/global/publish.json @@ -0,0 +1,73 @@ +{ + "IntegrateMasterVersion": { + "enabled": false + }, + "ExtractReview": { + "__documentation__": "http://pype.club/docs/admin_presets_plugins", + "profiles": [ + { + "families": [], + "hosts": [], + "outputs": { + "h264": { + "filter": { + "families": ["render", "review", "ftrack"] + }, + "ext": "mp4", + "ffmpeg_args": { + "input": [ + "-gamma 2.2" + ], + "video_filters": [], + "audio_filters": [], + "output": [ + "-pix_fmt yuv420p", + "-crf 18", + "-intra" + ] + }, + "tags": ["burnin", "ftrackreview"] + } + } + } + ] + }, + "ExtractBurnin": { + "options": { + "opacity": 1, + "x_offset": 5, + "y_offset": 5, + "bg_padding": 5, + "bg_opacity": 0.5, + "font_size": 42 + }, + "fields": { + + }, + "profiles": [ + { + "burnins": { + "burnin": { + "TOP_LEFT": "{yy}-{mm}-{dd}", + "TOP_RIGHT": "{anatomy[version]}", + "TOP_CENTERED": "", + "BOTTOM_RIGHT": "{frame_start}-{current_frame}-{frame_end}", + "BOTTOM_CENTERED": "{asset}", + "BOTTOM_LEFT": "{username}" + } + } + } + ] + }, + "IntegrateAssetNew": { + "template_name_profiles": { + "publish": { + "families": [], + "tasks": [] + }, + "render": { + "families": ["review", "render", "prerender"] + } + } + } +} diff --git a/pype/tools/config_setting/config/project_presets/plugins/maya/create.json b/pype/tools/config_setting/config/project_presets/plugins/maya/create.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/pype/tools/config_setting/config/project_presets/plugins/maya/create.json @@ -0,0 +1 @@ +{} diff --git a/pype/tools/config_setting/config/project_presets/plugins/maya/filter.json b/pype/tools/config_setting/config/project_presets/plugins/maya/filter.json new file mode 100644 index 0000000000..83d6f05f31 --- /dev/null +++ b/pype/tools/config_setting/config/project_presets/plugins/maya/filter.json @@ -0,0 +1,9 @@ +{ + "Preset n1": { + "ValidateNoAnimation": false, + "ValidateShapeDefaultNames": false + }, + "Preset n2": { + "ValidateNoAnimation": false + } +} diff --git a/pype/tools/config_setting/config/project_presets/plugins/maya/load.json b/pype/tools/config_setting/config/project_presets/plugins/maya/load.json new file mode 100644 index 0000000000..260fbb35ee --- /dev/null +++ b/pype/tools/config_setting/config/project_presets/plugins/maya/load.json @@ -0,0 +1,18 @@ +{ + "colors": { + "model": [0.821, 0.518, 0.117], + "rig": [0.144, 0.443, 0.463], + "pointcache": [0.368, 0.821, 0.117], + "animation": [0.368, 0.821, 0.117], + "ass": [1.0, 0.332, 0.312], + "camera": [0.447, 0.312, 1.0], + "fbx": [1.0, 0.931, 0.312], + "mayaAscii": [0.312, 1.0, 0.747], + "setdress": [0.312, 1.0, 0.747], + "layout": [0.312, 1.0, 0.747], + "vdbcache": [0.312, 1.0, 0.428], + "vrayproxy": [0.258, 0.95, 0.541], + "yeticache": [0.2, 0.8, 0.3], + "yetiRig": [0, 0.8, 0.5] + } +} diff --git a/pype/tools/config_setting/config/project_presets/plugins/maya/publish.json b/pype/tools/config_setting/config/project_presets/plugins/maya/publish.json new file mode 100644 index 0000000000..2e2b3164f3 --- /dev/null +++ b/pype/tools/config_setting/config/project_presets/plugins/maya/publish.json @@ -0,0 +1,17 @@ +{ + "ValidateModelName": { + "enabled": false, + "material_file": "/path/to/shader_name_definition.txt", + "regex": "(.*)_(\\d)*_(?P.*)_(GEO)" + }, + "ValidateAssemblyName": { + "enabled": false + }, + "ValidateShaderName": { + "enabled": false, + "regex": "(?P.*)_(.*)_SHD" + }, + "ValidateMeshHasOverlappingUVs": { + "enabled": false + } +} diff --git a/pype/tools/config_setting/config/project_presets/plugins/maya/workfile_build.json b/pype/tools/config_setting/config/project_presets/plugins/maya/workfile_build.json new file mode 100644 index 0000000000..2872b783cb --- /dev/null +++ b/pype/tools/config_setting/config/project_presets/plugins/maya/workfile_build.json @@ -0,0 +1,54 @@ +[{ + "tasks": ["lighting"], + + "current_context": [{ + "subset_name_filters": [".+[Mm]ain"], + "families": ["model"], + "repre_names": ["abc", "ma"], + "loaders": ["ReferenceLoader"] + }, { + "families": ["animation", "pointcache"], + "repre_names": ["abc"], + "loaders": ["ReferenceLoader"] + },{ + "families": ["rendersetup"], + "repre_names": ["json"], + "loaders": ["RenderSetupLoader"] + }, { + "families": ["camera"], + "repre_names": ["abc"], + "loaders": ["ReferenceLoader"] + }], + + "linked_assets": [{ + "families": ["setdress"], + "repre_names": ["ma"], + "loaders": ["ReferenceLoader"] + }, { + "families": ["ass"], + "repre_names": ["ass"], + "loaders":["assLoader"] + }] +}, { + "tasks": ["animation"], + + "current_context": [{ + "families": ["camera"], + "repre_names": ["abc", "ma"], + "loaders": ["ReferenceLoader"] + }, { + "families": ["audio"], + "repre_names": ["wav"], + "loaders": ["RenderSetupLoader"] + }], + + "linked_assets": [{ + "families": ["setdress"], + "repre_names": ["proxy"], + "loaders": ["ReferenceLoader"] + }, { + "families": ["rig"], + "repre_names": ["ass"], + "loaders": ["rigLoader"] + }] +}] diff --git a/pype/tools/config_setting/config/project_presets/plugins/nuke/create.json b/pype/tools/config_setting/config/project_presets/plugins/nuke/create.json new file mode 100644 index 0000000000..4deb0b4ad5 --- /dev/null +++ b/pype/tools/config_setting/config/project_presets/plugins/nuke/create.json @@ -0,0 +1,8 @@ +{ + "CreateWriteRender": { + "fpath_template": "{work}/renders/nuke/{subset}/{subset}.{frame}.{ext}" + }, + "CreateWritePrerender": { + "fpath_template": "{work}/prerenders/nuke/{subset}/{subset}.{frame}.{ext}" + } +} diff --git a/pype/tools/config_setting/config/project_presets/plugins/nuke/load.json b/pype/tools/config_setting/config/project_presets/plugins/nuke/load.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/pype/tools/config_setting/config/project_presets/plugins/nuke/load.json @@ -0,0 +1 @@ +{} diff --git a/pype/tools/config_setting/config/project_presets/plugins/nuke/publish.json b/pype/tools/config_setting/config/project_presets/plugins/nuke/publish.json new file mode 100644 index 0000000000..ab0d0e76a5 --- /dev/null +++ b/pype/tools/config_setting/config/project_presets/plugins/nuke/publish.json @@ -0,0 +1,48 @@ +{ + "ExtractThumbnail": { + "nodes": { + "Reformat": [ + ["type", "to format"], + ["format", "HD_1080"], + ["filter", "Lanczos6"], + ["black_outside", true], + ["pbb", false] + ] + } + }, + "ValidateNukeWriteKnobs": { + "enabled": false, + "knobs": { + "render": { + "review": true + } + } + }, + "ExtractReviewDataLut": { + "__documentation__": { + "viewer_lut_raw": "set to `true` if you Input_process node on viewer is used with baked screen space, so you have to look with RAW viewer lut. Else just keep it on `false`", + "enabled": "keep in on `false` if you want Nuke baking colorspace workflow applied else FFmpeg will convert imgsequence with baked LUT" + }, + "enabled": false + }, + "ExtractReviewDataMov": { + "__documentation__": { + "viewer_lut_raw": "set to `true` if you Input_process node on viewer is used with baked screen space, so you have to look with RAW viewer lut. Else just keep it on `false`", + "enabled": "keep in on `true` if you want Nuke baking colorspace workflow applied" + }, + "enabled": true, + "viewer_lut_raw": false + }, + "ExtractSlateFrame": { + "__documentation__": { + "viewer_lut_raw": "set to `true` if you Input_process node on viewer is used with baked screen space, so you have to look with RAW viewer lut. Else just keep it on `false`" + }, + "viewer_lut_raw": false + }, + "NukeSubmitDeadline": { + "deadline_priority": 50, + "deadline_pool": "", + "deadline_pool_secondary": "", + "deadline_chunk_size": 1 + } +} diff --git a/pype/tools/config_setting/config/project_presets/plugins/nuke/workfile_build.json b/pype/tools/config_setting/config/project_presets/plugins/nuke/workfile_build.json new file mode 100644 index 0000000000..d3613c929e --- /dev/null +++ b/pype/tools/config_setting/config/project_presets/plugins/nuke/workfile_build.json @@ -0,0 +1,11 @@ +[{ + "tasks": ["compositing"], + + "current_context": [{ + "families": ["render", "plate"], + "repre_names": ["exr" ,"dpx"], + "loaders": ["LoadSequence"] + }], + + "linked_assets": [] +}] diff --git a/pype/tools/config_setting/config/project_presets/plugins/nukestudio/filter.json b/pype/tools/config_setting/config/project_presets/plugins/nukestudio/filter.json new file mode 100644 index 0000000000..bd6a0dc1bd --- /dev/null +++ b/pype/tools/config_setting/config/project_presets/plugins/nukestudio/filter.json @@ -0,0 +1,10 @@ +{ + "strict": { + "ValidateVersion": true, + "VersionUpWorkfile": true + }, + "benevolent": { + "ValidateVersion": false, + "VersionUpWorkfile": false + } +} \ No newline at end of file diff --git a/pype/tools/config_setting/config/project_presets/plugins/nukestudio/publish.json b/pype/tools/config_setting/config/project_presets/plugins/nukestudio/publish.json new file mode 100644 index 0000000000..8c4ad133f1 --- /dev/null +++ b/pype/tools/config_setting/config/project_presets/plugins/nukestudio/publish.json @@ -0,0 +1,8 @@ +{ + "CollectInstanceVersion": { + "enabled": false + }, + "ExtractReviewCutUpVideo": { + "tags_addition": [] + } +} diff --git a/pype/tools/config_setting/config/project_presets/plugins/standalonepublisher/publish.json b/pype/tools/config_setting/config/project_presets/plugins/standalonepublisher/publish.json new file mode 100644 index 0000000000..ecfff12db9 --- /dev/null +++ b/pype/tools/config_setting/config/project_presets/plugins/standalonepublisher/publish.json @@ -0,0 +1,17 @@ +{ + "ExtractReviewSP": { + "outputs": { + "h264": { + "input": [ + "-gamma 2.2" + ], + "output": [ + "-pix_fmt yuv420p", + "-crf 18" + ], + "tags": ["preview"], + "ext": "mov" + } + } + } +} diff --git a/pype/tools/config_setting/config/project_presets/plugins/test/create.json b/pype/tools/config_setting/config/project_presets/plugins/test/create.json new file mode 100644 index 0000000000..fa0b2fc05f --- /dev/null +++ b/pype/tools/config_setting/config/project_presets/plugins/test/create.json @@ -0,0 +1,8 @@ +{ + "MyTestCreator": { + "my_test_property": "B", + "active": false, + "new_property": "new", + "family": "new_family" + } +} diff --git a/pype/tools/config_setting/config/project_presets/plugins/test/publish.json b/pype/tools/config_setting/config/project_presets/plugins/test/publish.json new file mode 100644 index 0000000000..3180dd5d8a --- /dev/null +++ b/pype/tools/config_setting/config/project_presets/plugins/test/publish.json @@ -0,0 +1,10 @@ +{ + "MyTestPlugin": { + "label": "loaded from preset", + "optional": true, + "families": ["changed", "by", "preset"] + }, + "MyTestRemovedPlugin": { + "enabled": false + } +} diff --git a/pype/tools/config_setting/config/project_presets/premiere/asset_default.json b/pype/tools/config_setting/config/project_presets/premiere/asset_default.json new file mode 100644 index 0000000000..84d2bde3d8 --- /dev/null +++ b/pype/tools/config_setting/config/project_presets/premiere/asset_default.json @@ -0,0 +1,5 @@ +{ + "frameStart": 1001, + "handleStart": 0, + "handleEnd": 0 +} diff --git a/pype/tools/config_setting/config/project_presets/premiere/rules_tasks.json b/pype/tools/config_setting/config/project_presets/premiere/rules_tasks.json new file mode 100644 index 0000000000..333c9cd70b --- /dev/null +++ b/pype/tools/config_setting/config/project_presets/premiere/rules_tasks.json @@ -0,0 +1,21 @@ +{ + "defaultTasks": ["Layout", "Animation"], + "taskToSubsets": { + "Layout": ["reference", "audio"], + "Animation": ["audio"] + }, + "subsetToRepresentations": { + "reference": { + "preset": "h264", + "representation": "mp4" + }, + "thumbnail": { + "preset": "jpeg_thumb", + "representation": "jpg" + }, + "audio": { + "preset": "48khz", + "representation": "wav" + } + } +} diff --git a/pype/tools/config_setting/config/project_presets/unreal/project_setup.json b/pype/tools/config_setting/config/project_presets/unreal/project_setup.json new file mode 100644 index 0000000000..8a4dffc526 --- /dev/null +++ b/pype/tools/config_setting/config/project_presets/unreal/project_setup.json @@ -0,0 +1,4 @@ +{ + "dev_mode": false, + "install_unreal_python_engine": false +} diff --git a/pype/tools/config_setting/config/studio_presets/ftrack/server_plugins.json b/pype/tools/config_setting/config/studio_presets/ftrack/server_plugins.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/pype/tools/config_setting/config/studio_presets/ftrack/server_plugins.json @@ -0,0 +1 @@ +{} diff --git a/pype/tools/config_setting/config/studio_presets/ftrack/user_plugins.json b/pype/tools/config_setting/config/studio_presets/ftrack/user_plugins.json new file mode 100644 index 0000000000..1ba8e9b511 --- /dev/null +++ b/pype/tools/config_setting/config/studio_presets/ftrack/user_plugins.json @@ -0,0 +1,5 @@ +{ + "TestAction": { + "ignore_me": true + } +} diff --git a/pype/tools/config_setting/config/studio_presets/global/applications.json b/pype/tools/config_setting/config/studio_presets/global/applications.json new file mode 100644 index 0000000000..35e399444c --- /dev/null +++ b/pype/tools/config_setting/config/studio_presets/global/applications.json @@ -0,0 +1,39 @@ +{ + "blender_2.80": true, + "blender_2.81": true, + "blender_2.82": true, + "blender_2.83": true, + "harmony_17": true, + "houdini_16": true, + "houdini_17": true, + "houdini_18": true, + "maya_2016": true, + "maya_2017": true, + "maya_2018": true, + "maya_2019": true, + "maya_2020": true, + "nuke_10.0": true, + "nuke_11.0": true, + "nuke_11.2": true, + "nuke_11.3": true, + "nuke_12.0": true, + "nukex_10.0": true, + "nukex_11.0": true, + "nukex_11.2": true, + "nukex_11.3": true, + "nukex_12.0": true, + "nukestudio_10.0": true, + "nukestudio_11.0": true, + "nukestudio_11.2": true, + "nukestudio_11.3": true, + "nukestudio_12.0": true, + "photoshop_2020": true, + "premiere_2019": true, + "premiere_2020": true, + "python_2": true, + "python_3": true, + "resolve_16": true, + "shell": true, + "storyboardpro_7": true, + "unreal_4.21": true +} diff --git a/pype/tools/config_setting/config/studio_presets/global/es/applications.json b/pype/tools/config_setting/config/studio_presets/global/es/applications.json new file mode 100644 index 0000000000..35e399444c --- /dev/null +++ b/pype/tools/config_setting/config/studio_presets/global/es/applications.json @@ -0,0 +1,39 @@ +{ + "blender_2.80": true, + "blender_2.81": true, + "blender_2.82": true, + "blender_2.83": true, + "harmony_17": true, + "houdini_16": true, + "houdini_17": true, + "houdini_18": true, + "maya_2016": true, + "maya_2017": true, + "maya_2018": true, + "maya_2019": true, + "maya_2020": true, + "nuke_10.0": true, + "nuke_11.0": true, + "nuke_11.2": true, + "nuke_11.3": true, + "nuke_12.0": true, + "nukex_10.0": true, + "nukex_11.0": true, + "nukex_11.2": true, + "nukex_11.3": true, + "nukex_12.0": true, + "nukestudio_10.0": true, + "nukestudio_11.0": true, + "nukestudio_11.2": true, + "nukestudio_11.3": true, + "nukestudio_12.0": true, + "photoshop_2020": true, + "premiere_2019": true, + "premiere_2020": true, + "python_2": true, + "python_3": true, + "resolve_16": true, + "shell": true, + "storyboardpro_7": true, + "unreal_4.21": true +} diff --git a/pype/tools/config_setting/config/studio_presets/global/intents.json b/pype/tools/config_setting/config/studio_presets/global/intents.json new file mode 100644 index 0000000000..c853e27b7f --- /dev/null +++ b/pype/tools/config_setting/config/studio_presets/global/intents.json @@ -0,0 +1,9 @@ +{ + "default": "wip", + "items": { + "": "", + "wip": "WIP", + "test": "TEST", + "final": "FINAL" + } +} diff --git a/pype/tools/config_setting/config/studio_presets/global/tray_items.json b/pype/tools/config_setting/config/studio_presets/global/tray_items.json new file mode 100644 index 0000000000..a42bf67c38 --- /dev/null +++ b/pype/tools/config_setting/config/studio_presets/global/tray_items.json @@ -0,0 +1,25 @@ +{ + "usage": { + "User settings": false, + "Ftrack": false, + "Muster": false, + "Avalon": true, + "Clockify": false, + "Standalone Publish": true, + "Logging": true, + "Idle Manager": true, + "Timers Manager": true, + "Rest Api": true, + "Premiere Communicator": true + }, + "attributes": { + "Rest Api": { + "default_port": 8021, + "exclude_ports": [] + }, + "Timers Manager": { + "full_time": 15, + "message_time": 0.5 + } + } +} diff --git a/pype/tools/config_setting/config/studio_presets/muster/templates_mapping.json b/pype/tools/config_setting/config/studio_presets/muster/templates_mapping.json new file mode 100644 index 0000000000..4edab9077d --- /dev/null +++ b/pype/tools/config_setting/config/studio_presets/muster/templates_mapping.json @@ -0,0 +1,19 @@ +{ + "3delight": 41, + "arnold": 46, + "arnold_sf": 57, + "gelato": 30, + "harware": 3, + "krakatoa": 51, + "file_layers": 7, + "mentalray": 2, + "mentalray_sf": 6, + "redshift": 55, + "renderman": 29, + "software": 1, + "software_sf": 5, + "turtle": 10, + "vector": 4, + "vray": 37, + "ffmpeg": 48 +} diff --git a/pype/tools/config_setting/config_gui_schema/applications_gui_schema.json b/pype/tools/config_setting/config_gui_schema/applications_gui_schema.json new file mode 100644 index 0000000000..096964b5b8 --- /dev/null +++ b/pype/tools/config_setting/config_gui_schema/applications_gui_schema.json @@ -0,0 +1,153 @@ +{ + "key": "applications", + "type": "dict-expanding", + "label": "Applications", + "children": [ + { + "type": "dict-form", + "children": [ + { + "key": "blender_2.80", + "type": "boolean", + "label": "Blender 2.80" + }, { + "key": "blender_2.81", + "type": "boolean", + "label": "Blender 2.81" + }, { + "key": "blender_2.82", + "type": "boolean", + "label": "Blender 2.82" + }, { + "key": "blender_2.83", + "type": "boolean", + "label": "Blender 2.83" + }, { + "key": "celaction_local", + "type": "boolean", + "label": "Celaction Local" + }, { + "key": "celaction_remote", + "type": "boolean", + "label": "Celaction Remote" + }, { + "key": "harmony_17", + "type": "boolean", + "label": "Harmony 17" + }, { + "key": "houdini_16", + "type": "boolean", + "label": "Houdini 16" + }, { + "key": "houdini_17", + "type": "boolean", + "label": "Houdini 17" + }, { + "key": "houdini_18", + "type": "boolean", + "label": "Houdini 18" + }, { + "key": "maya_2017", + "type": "boolean", + "label": "Autodest Maya 2017" + }, { + "key": "maya_2018", + "type": "boolean", + "label": "Autodest Maya 2018" + }, { + "key": "maya_2019", + "type": "boolean", + "label": "Autodest Maya 2019" + }, { + "key": "maya_2020", + "type": "boolean", + "label": "Autodest Maya 2020" + }, { + "key": "nuke_10.0", + "type": "boolean", + "label": "Nuke 10.0" + }, { + "key": "nuke_11.2", + "type": "boolean", + "label": "Nuke 11.2" + }, { + "key": "nuke_11.3", + "type": "boolean", + "label": "Nuke 11.3" + }, { + "key": "nuke_12.0", + "type": "boolean", + "label": "Nuke 12.0" + }, { + "key": "nukex_10.0", + "type": "boolean", + "label": "NukeX 10.0" + }, { + "key": "nukex_11.2", + "type": "boolean", + "label": "NukeX 11.2" + }, { + "key": "nukex_11.3", + "type": "boolean", + "label": "NukeX 11.3" + }, { + "key": "nukex_12.0", + "type": "boolean", + "label": "NukeX 12.0" + }, { + "key": "nukestudio_10.0", + "type": "boolean", + "label": "NukeStudio 10.0" + }, { + "key": "nukestudio_11.2", + "type": "boolean", + "label": "NukeStudio 11.2" + }, { + "key": "nukestudio_11.3", + "type": "boolean", + "label": "NukeStudio 11.3" + }, { + "key": "nukestudio_12.0", + "type": "boolean", + "label": "NukeStudio 12.0" + }, { + "key": "houdini_16.5", + "type": "boolean", + "label": "Houdini 16.5" + }, { + "key": "houdini_17", + "type": "boolean", + "label": "Houdini 17" + }, { + "key": "houdini_18", + "type": "boolean", + "label": "Houdini 18" + }, { + "key": "premiere_2019", + "type": "boolean", + "label": "Premiere 2019" + }, { + "key": "premiere_2020", + "type": "boolean", + "label": "Premiere 2020" + }, { + "key": "premiere_2020", + "type": "boolean", + "label": "Premiere 2020" + }, { + "key": "resolve_16", + "type": "boolean", + "label": "BM DaVinci Resolve 16" + }, { + "key": "storyboardpro_7", + "type": "boolean", + "label": "Storyboard Pro 7" + }, { + "key": "unreal_4.24", + "type": "boolean", + "label": "Unreal Editor 4.24" + } + ] + } + ] +} diff --git a/pype/tools/config_setting/config_gui_schema/ftrack_projects_gui_schema.json b/pype/tools/config_setting/config_gui_schema/ftrack_projects_gui_schema.json new file mode 100644 index 0000000000..febf84eb4a --- /dev/null +++ b/pype/tools/config_setting/config_gui_schema/ftrack_projects_gui_schema.json @@ -0,0 +1,30 @@ +{ + "key": "ftrack", + "type": "dict-expanding", + "label": "Ftrack", + "children": [ + { + "key": "status_update", + "type": "dict-expanding", + "label": "Status updates", + "children": [ + { + "key": "Ready", + "type": "text-singleline", + "label": "Ready" + } + ] + }, { + "key": "status_version_to_task", + "type": "dict-expanding", + "label": "Version status to Task status", + "children": [ + { + "key": "in progress", + "type": "text-singleline", + "label": "In Progress" + } + ] + } + ] +} diff --git a/pype/tools/config_setting/config_gui_schema/project_gui_schema.json b/pype/tools/config_setting/config_gui_schema/project_gui_schema.json new file mode 100644 index 0000000000..38c07ec33d --- /dev/null +++ b/pype/tools/config_setting/config_gui_schema/project_gui_schema.json @@ -0,0 +1,13 @@ +{ + "key": "studio", + "type": "dict-invisible", + "label": "Studio", + "children": [ + { + "type": "schema", + "children": [ + "ftrack_projects_gui_schema" + ] + } + ] +} diff --git a/pype/tools/config_setting/config_gui_schema/studio_gui_schema.json b/pype/tools/config_setting/config_gui_schema/studio_gui_schema.json new file mode 100644 index 0000000000..7d902bb8db --- /dev/null +++ b/pype/tools/config_setting/config_gui_schema/studio_gui_schema.json @@ -0,0 +1,23 @@ +{ + "key": "studio", + "type": "dict-invisible", + "label": "Studio", + "children": [ + { + "type": "schema", + "children": [ + "applications_gui_schema", + "tools_gui_schema" + ] + }, { + "key": "muster", + "type": "dict-invisible", + "children": [{ + "key": "templates_mapping", + "label": "Muster", + "type": "dict-modifiable", + "object_type": "int" + }] + } + ] +} diff --git a/pype/tools/config_setting/config_gui_schema/tools_gui_schema.json b/pype/tools/config_setting/config_gui_schema/tools_gui_schema.json new file mode 100644 index 0000000000..2f46ef0ec5 --- /dev/null +++ b/pype/tools/config_setting/config_gui_schema/tools_gui_schema.json @@ -0,0 +1,29 @@ +{ + "key": "tools", + "type": "dict-expanding", + "label": "Tools", + "children": [ + { + "type": "dict-form", + "children": [ + { + "key": "mtoa_3.0.1", + "type": "boolean", + "label": "Arnold Maya 3.0.1" + }, { + "key": "mtoa_3.1.1", + "type": "boolean", + "label": "Arnold Maya 3.1.1" + }, { + "key": "mtoa_3.2.0", + "type": "boolean", + "label": "Arnold Maya 3.2.0" + }, { + "key": "yeti_2.1.2", + "type": "boolean", + "label": "Yeti 2.1.2" + } + ] + } + ] +} diff --git a/pype/tools/config_setting/interface.py b/pype/tools/config_setting/interface.py new file mode 100644 index 0000000000..e95f3f5fe3 --- /dev/null +++ b/pype/tools/config_setting/interface.py @@ -0,0 +1,49 @@ +import os +import sys +os.environ["PYPE_CONFIG"] = ( + "C:/Users/jakub.trllo/Desktop/pype/pype-setup/repos/pype-config" +) +os.environ["AVALON_MONGO"] = "mongodb://localhost:2707" +sys_paths = ( + "C:/Users/Public/pype_env2/Lib/site-packages", + "C:/Users/jakub.trllo/Desktop/pype/pype-setup", + "C:/Users/jakub.trllo/Desktop/pype/pype-setup/repos/pype", + "C:/Users/jakub.trllo/Desktop/pype/pype-setup/repos/avalon-core", + "C:/Users/jakub.trllo/Desktop/pype/pype-setup/repos/pyblish-base", + "C:/Users/jakub.trllo/Desktop/pype/pype-setup/repos/pyblish-lite", + "C:/Users/jakub.trllo/Desktop/pype/pype-setup/repos/pype-config" +) +for path in sys_paths: + sys.path.append(path) + +from widgets import main +import style +from Qt import QtWidgets, QtGui + + +class MyApp(QtWidgets.QApplication): + def __init__(self, *args, **kwargs): + super(MyApp, self).__init__(*args, **kwargs) + stylesheet = style.load_stylesheet() + self.setStyleSheet(stylesheet) + self.setWindowIcon(QtGui.QIcon(style.app_icon_path())) + + +if __name__ == "__main__": + app = MyApp(sys.argv) + + # main_widget = QtWidgets.QWidget() + # main_widget.setWindowIcon(QtGui.QIcon(style.app_icon_path())) + # + # layout = QtWidgets.QVBoxLayout(main_widget) + # + # widget = main.MainWidget(main_widget) + + # layout.addWidget(widget) + # main_widget.setLayout(layout) + # main_widget.show() + + widget = main.MainWidget() + widget.show() + + sys.exit(app.exec_()) diff --git a/pype/tools/config_setting/style/__init__.py b/pype/tools/config_setting/style/__init__.py new file mode 100644 index 0000000000..a8f202d97b --- /dev/null +++ b/pype/tools/config_setting/style/__init__.py @@ -0,0 +1,12 @@ +import os + + +def load_stylesheet(): + style_path = os.path.join(os.path.dirname(__file__), "style.css") + with open(style_path, "r") as style_file: + stylesheet = style_file.read() + return stylesheet + + +def app_icon_path(): + return os.path.join(os.path.dirname(__file__), "pype_icon.png") diff --git a/pype/tools/config_setting/style/pype_icon.png b/pype/tools/config_setting/style/pype_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..bfacf6eeedc753db0d77e661843a78b0eb13ce38 GIT binary patch literal 3793 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 b;3u>qRjE!lxug$XMKJrL$8D;P_+R@UX<7IA literal 0 HcmV?d00001 diff --git a/pype/tools/config_setting/style/style.css b/pype/tools/config_setting/style/style.css new file mode 100644 index 0000000000..f559a4c3b3 --- /dev/null +++ b/pype/tools/config_setting/style/style.css @@ -0,0 +1,90 @@ +QWidget { + color: #bfccd6; + background-color: #293742; + font-size: 12px; +} + +QCheckBox::indicator { +} +QCheckBox::indicator:focus { + color: #ff0000; +} + +QLineEdit, QSpinBox, QDoubleSpinBox, QPlainTextEdit { + border: 1px solid #aaaaaa; + border-radius: 3px; +} + +QLineEdit:focus, QSpinBox:focus, QDoubleSpinBox:focus, QPlainTextEdit:focus { + border: 1px solid #ffffff; +} + +QLabel[state="original"] {} + +QLabel[state="modified"] { + color: #137cbd; +} + +QLabel[state="overriden-modified"] { + color: #00b386; +} + +QLabel[state="overriden"] { + color: #ff8c1a; +} + +QWidget[input-state="original"] {} +QWidget[input-state="modified"] { + border-color: #137cbd; +} + +QWidget[input-state="overriden-modified"] { + border-color: #00b386; +} + +QWidget[input-state="overriden"] { + border-color: #ff8c1a; +} + +QPushButton[btn-type="text-list"] { + border: 1px solid #bfccd6; + border-radius: 10px; +} + +QPushButton[btn-type="text-list"]:hover { + border-color: #137cbd; + color: #137cbd; +} + +QPushButton[btn-type="expand-toggle"] { + background: transparent; +} + +#DictKey[state="original"] {} + +#DictKey[state="modified"] { + border-color: #137cbd; +} + +#DictKey[state="overriden"] { + border-color: #00f; +} +#DictKey[state="overriden-modified"] { + border-color: #0f0; +} + +#ExpandLabel { + background: transparent; +} + +#DictExpandWidget, #ModifiableDict, #ExpandingWidget { + border: 1px solid #455c6e; + border-radius: 3px; + background: #1d272f; +} + +#TextListSubWidget { + border: 1px solid #455c6e; + border-radius: 3px; + background: #1d272f; +} diff --git a/pype/tools/config_setting/widgets/__init__.py b/pype/tools/config_setting/widgets/__init__.py new file mode 100644 index 0000000000..b295759a36 --- /dev/null +++ b/pype/tools/config_setting/widgets/__init__.py @@ -0,0 +1,6 @@ +from .lib import CustomNone, NOT_SET + + +from .base import * +from .main import * +from .inputs import * diff --git a/pype/tools/config_setting/widgets/base.py b/pype/tools/config_setting/widgets/base.py new file mode 100644 index 0000000000..0cc64a66de --- /dev/null +++ b/pype/tools/config_setting/widgets/base.py @@ -0,0 +1,282 @@ +import os +import json +from Qt import QtWidgets, QtCore, QtGui +from . import config +from .lib import NOT_SET +from avalon import io + + +class TypeToKlass: + types = {} + + +class ClickableWidget(QtWidgets.QLabel): + clicked = QtCore.Signal() + + def __init__(self, *args, **kwargs): + super(ClickableWidget, self).__init__(*args, **kwargs) + self.setObjectName("ExpandLabel") + + def mouseReleaseEvent(self, event): + if event.button() == QtCore.Qt.LeftButton: + self.clicked.emit() + super(ClickableWidget, self).mouseReleaseEvent(event) + + +class PypeConfigurationWidget: + is_category = False + is_overriden = False + is_modified = False + + def config_value(self): + raise NotImplementedError( + "Method `config_value` is not implemented for `{}`.".format( + self.__class__.__name__ + ) + ) + + def value_from_values(self, values, keys=None): + if not values: + return NOT_SET + + if keys is None: + keys = self.keys + + value = values + for key in keys: + if not isinstance(value, dict): + raise TypeError( + "Expected dictionary got {}.".format(str(type(value))) + ) + + if key not in value: + return NOT_SET + value = value[key] + return value + + def add_children_gui(self, child_configuration, values): + raise NotImplementedError(( + "Method `add_children_gui` is not implemented for `{}`." + ).format(self.__class__.__name__)) + + +class StudioWidget(QtWidgets.QWidget, PypeConfigurationWidget): + config_dir = os.path.join( + os.path.dirname(os.path.dirname(__file__)), + "config_gui_schema" + ) + is_overidable = False + + def __init__(self, parent=None): + super(StudioWidget, self).__init__(parent) + + self.input_fields = [] + + scroll_widget = QtWidgets.QScrollArea(self) + content_widget = QtWidgets.QWidget(scroll_widget) + content_layout = QtWidgets.QVBoxLayout(content_widget) + content_layout.setContentsMargins(0, 0, 0, 0) + content_layout.setSpacing(0) + content_layout.setAlignment(QtCore.Qt.AlignTop) + content_widget.setLayout(content_layout) + + # scroll_widget.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) + # scroll_widget.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) + scroll_widget.setWidgetResizable(True) + scroll_widget.setWidget(content_widget) + + self.scroll_widget = scroll_widget + self.content_layout = content_layout + self.content_widget = content_widget + + values = {"studio": config.studio_presets()} + schema = config.gui_schema("studio_gui_schema") + self.keys = schema.get("keys", []) + self.add_children_gui(schema, values) + + footer_widget = QtWidgets.QWidget() + footer_layout = QtWidgets.QHBoxLayout(footer_widget) + + btn = QtWidgets.QPushButton("Finish") + spacer_widget = QtWidgets.QWidget() + footer_layout.addWidget(spacer_widget, 1) + footer_layout.addWidget(btn, 0) + + layout = QtWidgets.QVBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(0) + self.setLayout(layout) + + layout.addWidget(scroll_widget, 1) + layout.addWidget(footer_widget, 0) + + btn.clicked.connect(self.___finish) + + def ___finish(self): + output = {} + for item in self.input_fields: + output.update(item.config_value()) + + for key in reversed(self.keys): + _output = {key: output} + output = _output + + print(json.dumps(output, indent=4)) + + def add_children_gui(self, child_configuration, values): + item_type = child_configuration["type"] + klass = TypeToKlass.types.get(item_type) + item = klass( + child_configuration, values, self.keys, self + ) + self.input_fields.append(item) + self.content_layout.addWidget(item) + + +class ProjectListWidget(QtWidgets.QWidget): + default = "< Default >" + + def __init__(self, parent=None): + super(ProjectListWidget, self).__init__(parent) + + label = QtWidgets.QLabel("Project") + project_list = QtWidgets.QListView(self) + project_list.setModel(QtGui.QStandardItemModel()) + + layout = QtWidgets.QVBoxLayout(self) + # content_margin = 5 + # layout.setContentsMargins( + # content_margin, + # content_margin, + # content_margin, + # content_margin + # ) + # layout.setSpacing(3) + layout.addWidget(label, 0) + layout.addWidget(project_list, 1) + + self.project_list = project_list + + self.refresh() + + def project_name(self): + current_selection = self.project_list.currentText() + if current_selection == self.default: + return None + return current_selection + + def refresh(self): + selected_project = None + for index in self.project_list.selectedIndexes(): + selected_project = index.data(QtCore.Qt.DisplayRole) + break + + model = self.project_list.model() + model.clear() + items = [self.default] + io.install() + for project_doc in tuple(io.projects()): + print(project_doc["name"]) + items.append(project_doc["name"]) + + for item in items: + model.appendRow(QtGui.QStandardItem(item)) + + if not selected_project: + selected_project = self.default + + found_items = model.findItems(selected_project) + if found_items: + index = model.indexFromItem(found_items[0]) + c = QtCore.QItemSelectionModel.SelectionFlag.SelectCurrent + self.project_list.selectionModel().select( + index, c + ) + # self.project_list.selectionModel().setCurrentIndex( + # index, c + # ) + + + +class ProjectWidget(QtWidgets.QWidget, PypeConfigurationWidget): + config_dir = os.path.join( + os.path.dirname(os.path.dirname(__file__)), + "config_gui_schema" + ) + is_overidable = True + + def __init__(self, parent=None): + super(ProjectWidget, self).__init__(parent) + + self.input_fields = [] + + scroll_widget = QtWidgets.QScrollArea(self) + content_widget = QtWidgets.QWidget(scroll_widget) + content_layout = QtWidgets.QVBoxLayout(content_widget) + content_layout.setContentsMargins(0, 0, 0, 0) + content_layout.setSpacing(0) + content_layout.setAlignment(QtCore.Qt.AlignTop) + content_widget.setLayout(content_layout) + + scroll_widget.setWidgetResizable(True) + scroll_widget.setWidget(content_widget) + + project_list_widget = ProjectListWidget() + content_layout.addWidget(project_list_widget) + + self.project_list_widget = project_list_widget + self.scroll_widget = scroll_widget + self.content_layout = content_layout + self.content_widget = content_widget + + values = config.project_presets() + schema = config.gui_schema("project_gui_schema") + self.keys = schema.get("keys", []) + self.add_children_gui(schema, values) + + footer_widget = QtWidgets.QWidget() + footer_layout = QtWidgets.QHBoxLayout(footer_widget) + + btn = QtWidgets.QPushButton("Finish") + spacer_widget = QtWidgets.QWidget() + footer_layout.addWidget(spacer_widget, 1) + footer_layout.addWidget(btn, 0) + + presets_widget = QtWidgets.QWidget() + presets_layout = QtWidgets.QVBoxLayout(presets_widget) + presets_layout.setContentsMargins(0, 0, 0, 0) + presets_layout.setSpacing(0) + + presets_layout.addWidget(scroll_widget, 1) + presets_layout.addWidget(footer_widget, 0) + + layout = QtWidgets.QHBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(0) + self.setLayout(layout) + + layout.addWidget(project_list_widget, 0) + layout.addWidget(presets_widget, 1) + + btn.clicked.connect(self.___finish) + + def ___finish(self): + output = {} + for item in self.input_fields: + output.update(item.config_value()) + + for key in reversed(self.keys): + _output = {key: output} + output = _output + + print(json.dumps(output, indent=4)) + + def add_children_gui(self, child_configuration, values): + item_type = child_configuration["type"] + klass = TypeToKlass.types.get(item_type) + + item = klass( + child_configuration, values, self.keys, self + ) + self.input_fields.append(item) + self.content_layout.addWidget(item) diff --git a/pype/tools/config_setting/widgets/config.py b/pype/tools/config_setting/widgets/config.py new file mode 100644 index 0000000000..335299cb2f --- /dev/null +++ b/pype/tools/config_setting/widgets/config.py @@ -0,0 +1,236 @@ +import os +import json +import logging +import copy + +# DEBUG SETUP +os.environ["AVALON_PROJECT"] = "kuba_each_case" +os.environ["PYPE_PROJECT_CONFIGS"] = os.path.join( + os.path.dirname(os.path.dirname(__file__)), + "config", + "project_overrides" +) +# + +log = logging.getLogger(__name__) + +config_path = os.path.dirname(os.path.dirname(__file__)) +studio_presets_path = os.path.normpath( + os.path.join(config_path, "config", "studio_presets") +) +project_presets_path = os.path.normpath( + os.path.join(config_path, "config", "project_presets") +) +first_run = False + +OVERRIDE_KEY = "__overriden__" +POP_KEY = "__popkey__" + + +def load_json(fpath): + # Load json data + with open(fpath, "r") as opened_file: + lines = opened_file.read().splitlines() + + # prepare json string + standard_json = "" + for line in lines: + # Remove all whitespace on both sides + line = line.strip() + + # Skip blank lines + if len(line) == 0: + continue + + standard_json += line + + # Check if has extra commas + extra_comma = False + if ",]" in standard_json or ",}" in standard_json: + extra_comma = True + standard_json = standard_json.replace(",]", "]") + standard_json = standard_json.replace(",}", "}") + + global first_run + if extra_comma and first_run: + log.error("Extra comma in json file: \"{}\"".format(fpath)) + + # return empty dict if file is empty + if standard_json == "": + if first_run: + log.error("Empty json file: \"{}\"".format(fpath)) + return {} + + # Try to parse string + try: + return json.loads(standard_json) + + except json.decoder.JSONDecodeError: + # Return empty dict if it is first time that decode error happened + if not first_run: + return {} + + # Repreduce the exact same exception but traceback contains better + # information about position of error in the loaded json + try: + with open(fpath, "r") as opened_file: + json.load(opened_file) + + except json.decoder.JSONDecodeError: + log.warning( + "File has invalid json format \"{}\"".format(fpath), + exc_info=True + ) + + return {} + + +def subkey_merge(_dict, value, keys, with_metadata=False): + key = keys.pop(0) + if not keys: + if with_metadata: + _dict[key] = {"type": "file", "value": value} + else: + _dict[key] = value + return _dict + + if key not in _dict: + if with_metadata: + _dict[key] = {"type": "folder", "value": {}} + else: + _dict[key] = {} + + if with_metadata: + sub_dict = _dict[key]["value"] + else: + sub_dict = _dict[key] + + _value = subkey_merge(sub_dict, value, keys, with_metadata) + if with_metadata: + _dict[key]["value"] = _value + else: + _dict[key] = _value + return _dict + + +def load_jsons_from_dir(path, *args, **kwargs): + output = {} + + path = os.path.normpath(path) + if not os.path.exists(path): + # TODO warning + return output + + with_metadata = kwargs.get("with_metadata") + sub_keys = list(kwargs.pop("subkeys", args)) + for sub_key in tuple(sub_keys): + _path = os.path.join(path, sub_key) + if not os.path.exists(_path): + break + + path = _path + sub_keys.pop(0) + + base_len = len(path) + 1 + ext_len = len(".json") + + for base, _directories, filenames in os.walk(path): + for filename in filenames: + basename, ext = os.path.splitext(filename) + if ext == ".json": + full_path = os.path.join(base, filename) + value = load_json(full_path) + + # dict_path = os.path.join(base[base_len:], basename) + # dict_keys = dict_path.split(os.path.sep) + dict_keys = base[base_len:].split(os.path.sep) + [basename] + output = subkey_merge(output, value, dict_keys, with_metadata) + + for sub_key in sub_keys: + output = output[sub_key] + return output + + +def studio_presets(*args, **kwargs): + return load_jsons_from_dir(studio_presets_path, *args, **kwargs) + + +def global_project_presets(**kwargs): + return load_jsons_from_dir(project_presets_path, **kwargs) + + +def studio_presets_with_metadata(*args, **kwargs): + return load_jsons_from_dir(studio_presets_path, *args, **kwargs) + + +def global_project_presets_with_metadata(**kwargs): + return load_jsons_from_dir(project_presets_path, **kwargs) + + +def project_preset_overrides(project_name, **kwargs): + project_configs_path = os.environ.get("PYPE_PROJECT_CONFIGS") + if project_name and project_configs_path: + return load_jsons_from_dir( + os.path.join(project_configs_path, project_name), + **kwargs + ) + return {} + + +def merge_overrides(global_dict, override_dict): + if OVERRIDE_KEY in override_dict: + _override = override_dict.pop(OVERRIDE_KEY) + if _override: + return override_dict + + for key, value in override_dict.items(): + if value == POP_KEY: + global_dict.pop(key) + + elif key == OVERRIDE_KEY: + continue + + elif key not in global_dict: + global_dict[key] = value + + elif isinstance(value, dict) and isinstance(global_dict[key], dict): + global_dict[key] = merge_overrides(global_dict[key], value) + + else: + global_dict[key] = value + return global_dict + + +def apply_overrides(global_presets, project_overrides): + global_presets = copy.deepcopy(global_presets) + if not project_overrides: + return global_presets + return merge_overrides(global_presets, project_overrides) + + +def project_presets(project_name=None, **kwargs): + global_presets = global_project_presets(**kwargs) + + if not project_name: + project_name = os.environ.get("AVALON_PROJECT") + project_overrides = project_preset_overrides(project_name, **kwargs) + + return apply_overrides(global_presets, project_overrides) + + +def gui_schema(schema_name): + filename = schema_name + ".json" + schema_folder = os.path.join( + os.path.dirname(os.path.dirname(__file__)), + "config_gui_schema", + filename + ) + with open(schema_folder, "r") as json_stream: + schema = json.load(json_stream) + return schema + + +p1 = studio_presets(with_metadata=True) +p2 = studio_presets(with_metadata=False) +print(json.dumps(p1, indent=4)) +print(json.dumps(p2, indent=4)) diff --git a/pype/tools/config_setting/widgets/inputs.py b/pype/tools/config_setting/widgets/inputs.py new file mode 100644 index 0000000000..1ddc27278d --- /dev/null +++ b/pype/tools/config_setting/widgets/inputs.py @@ -0,0 +1,1346 @@ +from Qt import QtWidgets, QtCore, QtGui +from . import config +from .base import PypeConfigurationWidget, TypeToKlass, ClickableWidget +from .lib import NOT_SET, AS_WIDGET + +import json + + +class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): + value_changed = QtCore.Signal() + + def __init__( + self, input_data, values, parent_keys, parent, label_widget=None + ): + self._as_widget = values is AS_WIDGET + self._parent = parent + + super(BooleanWidget, self).__init__(parent) + + layout = QtWidgets.QVBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(0) + + self.checkbox = QtWidgets.QCheckBox() + self.checkbox.setAttribute(QtCore.Qt.WA_StyledBackground) + if not self._as_widget and not label_widget: + label = input_data["label"] + label_widget = QtWidgets.QLabel(label) + label_widget.setAttribute(QtCore.Qt.WA_StyledBackground) + layout.addWidget(label_widget) + + layout.addWidget(self.checkbox) + + if not self._as_widget: + self.label_widget = label_widget + self.key = input_data["key"] + keys = list(parent_keys) + keys.append(self.key) + self.keys = keys + + value = self.value_from_values(values) + if value is not NOT_SET: + self.checkbox.setChecked(value) + + self.origin_value = self.item_value() + + self.checkbox.stateChanged.connect(self._on_value_change) + + def set_value(self, value, origin_value=False): + self.checkbox.setChecked(value) + if origin_value: + self.origin_value = self.item_value() + self._on_value_change() + + def reset_value(self): + self.set_value(self.origin_value) + + def clear_value(self): + self.reset_value() + + @property + def is_overidable(self): + return self._parent.is_overidable + + def _on_value_change(self, value=None): + self.is_modified = self.item_value() != self.origin_value + self.is_overriden = True + + self._update_style() + + self.value_changed.emit() + + def _update_style(self): + if self.is_overidable and self.is_overriden: + if self.is_modified: + state = "overriden-modified" + else: + state = "overriden" + elif self.is_modified: + state = "modified" + else: + state = "original" + + if not self._as_widget: + property_name = "input-state" + else: + property_name = "state" + + self.label_widget.setProperty(property_name, state) + self.label_widget.style().polish(self.label_widget) + + def item_value(self): + return self.checkbox.isChecked() + + def config_value(self): + return {self.key: self.item_value()} + + +class ModifiedIntSpinBox(QtWidgets.QSpinBox): + def __init__(self, *args, **kwargs): + super(ModifiedIntSpinBox, self).__init__(*args, **kwargs) + self.setFocusPolicy(QtCore.Qt.StrongFocus) + + def wheelEvent(self, event): + if self.hasFocus(): + super(ModifiedIntSpinBox, self).wheelEvent(event) + else: + event.ignore() + + +class ModifiedFloatSpinBox(QtWidgets.QDoubleSpinBox): + def __init__(self, *args, **kwargs): + super(ModifiedFloatSpinBox, self).__init__(*args, **kwargs) + self.setFocusPolicy(QtCore.Qt.StrongFocus) + + def wheelEvent(self, event): + if self.hasFocus(): + super(ModifiedFloatSpinBox, self).wheelEvent(event) + else: + event.ignore() + + +class IntegerWidget(QtWidgets.QWidget, PypeConfigurationWidget): + value_changed = QtCore.Signal() + + def __init__( + self, input_data, values, parent_keys, parent, label_widget=None + ): + self._parent = parent + self._as_widget = values is AS_WIDGET + + super(IntegerWidget, self).__init__(parent) + + layout = QtWidgets.QVBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(0) + + self.int_input = ModifiedIntSpinBox() + + if not self._as_widget and not label_widget: + label = input_data["label"] + label_widget = QtWidgets.QLabel(label) + layout.addWidget(label_widget) + layout.addWidget(self.int_input) + + if not self._as_widget: + self.label_widget = label_widget + + self.key = input_data["key"] + keys = list(parent_keys) + keys.append(self.key) + self.keys = keys + + value = self.value_from_values(values) + if value is not NOT_SET: + self.int_input.setValue(value) + + self.origin_value = self.item_value() + + self.int_input.valueChanged.connect(self._on_value_change) + + @property + def is_overidable(self): + return self._parent.is_overidable + + def set_value(self, value, origin_value=False): + self.int_input.setValue(value) + if origin_value: + self.origin_value = self.item_value() + self._on_value_change() + + def clear_value(self): + self.set_value(0) + + def reset_value(self): + self.set_value(self.origin_value) + + def _on_value_change(self, value=None): + self.is_modified = self.item_value() != self.origin_value + self.is_overriden = True + + self._update_style() + + self.value_changed.emit() + + def _update_style(self): + if self.is_overidable and self.is_overriden: + if self.is_modified: + state = "overriden-modified" + else: + state = "overriden" + elif self.is_modified: + state = "modified" + else: + state = "original" + + if self._as_widget: + property_name = "input-state" + widget = self.int_input + else: + property_name = "state" + widget = self.label_widget + + widget.setProperty(property_name, state) + widget.style().polish(widget) + + def item_value(self): + return self.int_input.value() + + def config_value(self): + return {self.key: self.item_value()} + + +class FloatWidget(QtWidgets.QWidget, PypeConfigurationWidget): + value_changed = QtCore.Signal() + + def __init__( + self, input_data, values, parent_keys, parent, label_widget=None + ): + self._parent = parent + self._as_widget = values is AS_WIDGET + + super(FloatWidget, self).__init__(parent) + + layout = QtWidgets.QVBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(0) + + self.float_input = ModifiedFloatSpinBox() + + decimals = input_data.get("decimals", 5) + maximum = input_data.get("maximum") + minimum = input_data.get("minimum") + + self.float_input.setDecimals(decimals) + if maximum is not None: + self.float_input.setMaximum(float(maximum)) + if minimum is not None: + self.float_input.setMinimum(float(minimum)) + + if not self._as_widget and not label_widget: + label = input_data["label"] + label_widget = QtWidgets.QLabel(label) + layout.addWidget(label_widget) + layout.addWidget(self.float_input) + + if not self._as_widget: + self.label_widget = label_widget + + self.key = input_data["key"] + keys = list(parent_keys) + keys.append(self.key) + self.keys = keys + + value = self.value_from_values(values) + if value is not NOT_SET: + self.float_input.setValue(value) + + self.origin_value = self.item_value() + + self.float_input.valueChanged.connect(self._on_value_change) + + @property + def is_overidable(self): + return self._parent.is_overidable + + def set_value(self, value, origin_value=False): + self.float_input.setValue(value) + if origin_value: + self.origin_value = self.item_value() + self._on_value_change() + + def reset_value(self): + self.set_value(self.origin_value) + + def clear_value(self): + self.set_value(0) + + def _on_value_change(self, value=None): + self.is_modified = self.item_value() != self.origin_value + self.is_overriden = True + + self._update_style() + + self.value_changed.emit() + + def _update_style(self): + if not self._as_widget: + if self.is_overidable and self.is_overriden: + if self.is_modified: + state = "overriden-modified" + else: + state = "overriden" + elif self.is_modified: + state = "modified" + else: + state = "original" + + self.label_widget.setProperty("state", state) + self.label_widget.style().polish(self.label_widget) + + def item_value(self): + return self.float_input.value() + + def config_value(self): + return {self.key: self.item_value()} + + +class TextSingleLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): + value_changed = QtCore.Signal() + + def __init__( + self, input_data, values, parent_keys, parent, label_widget=None + ): + self._parent = parent + self._as_widget = values is AS_WIDGET + + super(TextSingleLineWidget, self).__init__(parent) + + layout = QtWidgets.QVBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(0) + + self.text_input = QtWidgets.QLineEdit() + + if not self._as_widget and not label_widget: + label = input_data["label"] + label_widget = QtWidgets.QLabel(label) + layout.addWidget(label_widget) + layout.addWidget(self.text_input) + + if not self._as_widget: + self.label_widget = label_widget + + self.key = input_data["key"] + keys = list(parent_keys) + keys.append(self.key) + self.keys = keys + + value = self.value_from_values(values) + if value is not NOT_SET: + self.text_input.setText(value) + + self.origin_value = self.item_value() + + self.text_input.textChanged.connect(self._on_value_change) + + @property + def is_overidable(self): + return self._parent.is_overidable + + def set_value(self, value, origin_value=False): + self.text_input.setText(value) + if origin_value: + self.origin_value = self.item_value() + self._on_value_change() + + def reset_value(self): + self.set_value(self.origin_value) + + def clear_value(self): + self.set_value("") + + def _on_value_change(self, value=None): + self.is_modified = self.item_value() != self.origin_value + self.is_overriden = True + + self._update_style() + + self.value_changed.emit() + + def _update_style(self): + if self.is_overidable and self.is_overriden: + if self.is_modified: + state = "overriden-modified" + else: + state = "overriden" + elif self.is_modified: + state = "modified" + else: + state = "original" + + self.label_widget.setProperty("state", state) + self.label_widget.style().polish(self.label_widget) + + def item_value(self): + return self.text_input.text() + + def config_value(self): + return {self.key: self.item_value()} + + +class TextMultiLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): + value_changed = QtCore.Signal() + + def __init__( + self, input_data, values, parent_keys, parent, label_widget=None + ): + self._parent = parent + + super(TextMultiLineWidget, self).__init__(parent) + + layout = QtWidgets.QVBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(0) + + self.text_input = QtWidgets.QPlainTextEdit() + if not label_widget: + label = input_data["label"] + label_widget = QtWidgets.QLabel(label) + layout.addWidget(label_widget) + layout.addWidget(self.text_input) + + self.label_widget = label_widget + + self.key = input_data["key"] + keys = list(parent_keys) + keys.append(self.key) + self.keys = keys + + value = self.value_from_values(values) + if value is not NOT_SET: + self.text_input.setPlainText(value) + + self.origin_value = self.item_value() + + self.text_input.textChanged.connect(self._on_value_change) + + @property + def is_overidable(self): + return self._parent.is_overidable + + def set_value(self, value, origin_value=False): + self.text_input.setPlainText(value) + if origin_value: + self.origin_value = self.item_value() + self._on_value_change() + + def reset_value(self): + self.set_value(self.origin_value) + + def clear_value(self): + self.set_value("") + + def _on_value_change(self, value=None): + self.is_modified = self.item_value() != self.origin_value + self.is_overriden = True + + self._update_style() + + self.value_changed.emit() + + def _update_style(self): + if self.is_overidable and self.is_overriden: + if self.is_modified: + state = "overriden-modified" + else: + state = "overriden" + elif self.is_modified: + state = "modified" + else: + state = "original" + + self.label_widget.setProperty("state", state) + self.label_widget.style().polish(self.label_widget) + + def item_value(self): + return self.text_input.toPlainText() + + def config_value(self): + return {self.key: self.item_value()} + + +class TextListItem(QtWidgets.QWidget, PypeConfigurationWidget): + _btn_size = 20 + value_changed = QtCore.Signal() + + def __init__(self, parent): + super(TextListItem, self).__init__(parent) + + layout = QtWidgets.QHBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(3) + + self.text_input = QtWidgets.QLineEdit() + self.add_btn = QtWidgets.QPushButton("+") + self.remove_btn = QtWidgets.QPushButton("-") + + self.add_btn.setProperty("btn-type", "text-list") + self.remove_btn.setProperty("btn-type", "text-list") + + layout.addWidget(self.text_input, 1) + layout.addWidget(self.add_btn, 0) + layout.addWidget(self.remove_btn, 0) + + self.add_btn.setFixedSize(self._btn_size, self._btn_size) + self.remove_btn.setFixedSize(self._btn_size, self._btn_size) + self.add_btn.clicked.connect(self.on_add_clicked) + self.remove_btn.clicked.connect(self.on_remove_clicked) + + self.text_input.textChanged.connect(self._on_value_change) + + self.is_single = False + + def _on_value_change(self): + self.value_changed.emit() + + def row(self): + return self.parent().input_fields.index(self) + + def on_add_clicked(self): + self.parent().add_row(row=self.row() + 1) + + def on_remove_clicked(self): + if self.is_single: + self.text_input.setText("") + else: + self.parent().remove_row(self) + + def config_value(self): + return self.text_input.text() + + +class TextListSubWidget(QtWidgets.QWidget, PypeConfigurationWidget): + value_changed = QtCore.Signal() + + def __init__(self, input_data, values, parent_keys, parent): + super(TextListSubWidget, self).__init__(parent) + self.setObjectName("TextListSubWidget") + + layout = QtWidgets.QVBoxLayout(self) + layout.setContentsMargins(5, 5, 5, 5) + layout.setSpacing(5) + self.setLayout(layout) + + self.input_fields = [] + self.add_row() + + self.key = input_data["key"] + keys = list(parent_keys) + keys.append(self.key) + self.keys = keys + + value = self.value_from_values(values) + if value is not NOT_SET: + self.set_value(value) + + self.origin_value = self.item_value() + + def set_value(self, value, origin_value=False): + for input_field in self.input_fields: + self.remove_row(input_field) + + for item_text in value: + self.add_row(text=item_text) + + if origin_value: + self.origin_value = self.item_value() + self._on_value_change() + + def reset_value(self): + self.set_value(self.origin_value) + + def clear_value(self): + self.set_value([]) + + def _on_value_change(self): + self.value_changed.emit() + + def count(self): + return len(self.input_fields) + + def add_row(self, row=None, text=None): + # Create new item + item_widget = TextListItem(self) + + # Set/unset if new item is single item + current_count = self.count() + if current_count == 0: + item_widget.is_single = True + elif current_count == 1: + for _input_field in self.input_fields: + _input_field.is_single = False + + item_widget.value_changed.connect(self._on_value_change) + + if row is None: + self.layout().addWidget(item_widget) + self.input_fields.append(item_widget) + else: + self.layout().insertWidget(row, item_widget) + self.input_fields.insert(row, item_widget) + + # Set text if entered text is not None + # else (when add button clicked) trigger `_on_value_change` + if text is not None: + item_widget.text_input.setText(text) + else: + self._on_value_change() + self.parent().updateGeometry() + + def remove_row(self, item_widget): + item_widget.value_changed.disconnect() + + self.layout().removeWidget(item_widget) + self.input_fields.remove(item_widget) + item_widget.setParent(None) + item_widget.deleteLater() + + current_count = self.count() + if current_count == 0: + self.add_row() + elif current_count == 1: + for _input_field in self.input_fields: + _input_field.is_single = True + + self._on_value_change() + self.parent().updateGeometry() + + def item_value(self): + output = [] + for item in self.input_fields: + text = item.config_value() + if text: + output.append(text) + + return output + + def config_value(self): + return {self.key: self.item_value()} + + +class TextListWidget(QtWidgets.QWidget, PypeConfigurationWidget): + def __init__( + self, input_data, values, parent_keys, parent, label_widget=None + ): + self._parent = parent + super(TextListWidget, self).__init__(parent) + self.setObjectName("TextListWidget") + + layout = QtWidgets.QVBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(0) + + if not label_widget: + label = input_data["label"] + label_widget = QtWidgets.QLabel(label) + layout.addWidget(label_widget) + + self.label_widget = label_widget + # keys = list(parent_keys) + # keys.append(input_data["key"]) + # self.keys = keys + + self.value_widget = TextListSubWidget( + input_data, values, parent_keys, self + ) + self.value_widget.setAttribute(QtCore.Qt.WA_StyledBackground) + self.value_widget.value_changed.connect(self._on_value_change) + + # self.value_widget.se + self.key = input_data["key"] + layout.addWidget(self.value_widget) + self.setLayout(layout) + + self.origin_value = self.item_value() + + @property + def is_overidable(self): + return self._parent.is_overidable + + def _on_value_change(self, value=None): + self.is_modified = self.item_value() != self.origin_value + self.is_overriden = True + + self._update_style() + + def set_value(self, value, origin_value=False): + self.value_widget.set_value(value) + if origin_value: + self.origin_value = self.item_value() + self._on_value_change() + + def reset_value(self): + self.set_value(self.origin_value) + + def clear_value(self): + self.set_value([]) + + def _update_style(self): + if self.is_overidable and self.is_overriden: + if self.is_modified: + state = "overriden-modified" + else: + state = "overriden" + elif self.is_modified: + state = "modified" + else: + state = "original" + + self.label_widget.setProperty("state", state) + self.label_widget.style().polish(self.label_widget) + + def item_value(self): + return self.value_widget.config_value() + + def config_value(self): + return {self.key: self.item_value()} + + +class ExpandingWidget(QtWidgets.QWidget): + def __init__(self, label, parent): + super(ExpandingWidget, self).__init__(parent) + self.setObjectName("ExpandingWidget") + + top_part = ClickableWidget(parent=self) + + button_size = QtCore.QSize(5, 5) + button_toggle = QtWidgets.QToolButton(parent=top_part) + button_toggle.setProperty("btn-type", "expand-toggle") + button_toggle.setIconSize(button_size) + button_toggle.setArrowType(QtCore.Qt.RightArrow) + button_toggle.setCheckable(True) + button_toggle.setChecked(False) + + label_widget = QtWidgets.QLabel(label, parent=top_part) + label_widget.setObjectName("ExpandLabel") + + layout = QtWidgets.QHBoxLayout(top_part) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(5) + layout.addWidget(button_toggle) + layout.addWidget(label_widget) + top_part.setLayout(layout) + + self.setAttribute(QtCore.Qt.WA_StyledBackground) + + self.top_part = top_part + self.button_toggle = button_toggle + self.label_widget = label_widget + + self.top_part.clicked.connect(self._top_part_clicked) + self.button_toggle.clicked.connect(self.toggle_content) + + def set_content_widget(self, content_widget): + main_layout = QtWidgets.QVBoxLayout(self) + main_layout.setContentsMargins(9, 9, 9, 9) + + content_widget.setVisible(False) + + main_layout.addWidget(self.top_part) + main_layout.addWidget(content_widget) + self.setLayout(main_layout) + + self.content_widget = content_widget + + def _top_part_clicked(self): + self.toggle_content(not self.button_toggle.isChecked()) + + def toggle_content(self, *args): + if len(args) > 0: + checked = args[0] + else: + checked = self.button_toggle.isChecked() + arrow_type = QtCore.Qt.RightArrow + if checked: + arrow_type = QtCore.Qt.DownArrow + self.button_toggle.setChecked(checked) + self.button_toggle.setArrowType(arrow_type) + self.content_widget.setVisible(checked) + self.parent().updateGeometry() + + def resizeEvent(self, event): + super(ExpandingWidget, self).resizeEvent(event) + self.content_widget.updateGeometry() + + +class DictExpandWidget(QtWidgets.QWidget, PypeConfigurationWidget): + def __init__( + self, input_data, values, parent_keys, parent, label_widget=None + ): + if values is AS_WIDGET: + raise TypeError("Can't use \"{}\" as widget item.".format( + self.__class__.__name__ + )) + self._parent = parent + + super(DictExpandWidget, self).__init__(parent) + self.setObjectName("DictExpandWidget") + top_part = ClickableWidget(parent=self) + + button_size = QtCore.QSize(5, 5) + button_toggle = QtWidgets.QToolButton(parent=top_part) + button_toggle.setProperty("btn-type", "expand-toggle") + button_toggle.setIconSize(button_size) + button_toggle.setArrowType(QtCore.Qt.RightArrow) + button_toggle.setCheckable(True) + button_toggle.setChecked(False) + + label = input_data["label"] + button_toggle_text = QtWidgets.QLabel(label, parent=top_part) + button_toggle_text.setObjectName("ExpandLabel") + + layout = QtWidgets.QHBoxLayout(top_part) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(5) + layout.addWidget(button_toggle) + layout.addWidget(button_toggle_text) + top_part.setLayout(layout) + + main_layout = QtWidgets.QVBoxLayout(self) + main_layout.setContentsMargins(9, 9, 9, 9) + + content_widget = QtWidgets.QWidget(self) + content_widget.setVisible(False) + + content_layout = QtWidgets.QVBoxLayout(content_widget) + content_layout.setContentsMargins(3, 3, 3, 3) + + main_layout.addWidget(top_part) + main_layout.addWidget(content_widget) + self.setLayout(main_layout) + + self.setAttribute(QtCore.Qt.WA_StyledBackground) + + self.top_part = top_part + self.button_toggle = button_toggle + self.button_toggle_text = button_toggle_text + + self.content_widget = content_widget + self.content_layout = content_layout + + self.top_part.clicked.connect(self._top_part_clicked) + self.button_toggle.clicked.connect(self.toggle_content) + + self._is_category = False + self._is_overriden = False + self.input_fields = [] + + self.key = input_data["key"] + keys = list(parent_keys) + keys.append(self.key) + self.keys = keys + + for child_data in input_data.get("children", []): + self.add_children_gui(child_data, values) + + def _top_part_clicked(self): + self.toggle_content(not self.button_toggle.isChecked()) + + def toggle_content(self, *args): + if len(args) > 0: + checked = args[0] + else: + checked = self.button_toggle.isChecked() + arrow_type = QtCore.Qt.RightArrow + if checked: + arrow_type = QtCore.Qt.DownArrow + self.button_toggle.setChecked(checked) + self.button_toggle.setArrowType(arrow_type) + self.content_widget.setVisible(checked) + self.parent().updateGeometry() + + def resizeEvent(self, event): + super(DictExpandWidget, self).resizeEvent(event) + self.content_widget.updateGeometry() + + @property + def is_category(self): + return self._is_category + + @property + def is_overriden(self): + return self._is_overriden + + @property + def is_modified(self): + _is_modified = False + for input_field in self.input_fields: + if input_field.is_modified: + _is_modified = True + break + return _is_modified + + def item_value(self): + output = {} + for input_field in self.input_fields: + # TODO maybe merge instead of update should be used + # NOTE merge is custom function which merges 2 dicts + output.update(input_field.config_value()) + return output + + def config_value(self): + return {self.key: self.item_value()} + + @property + def is_overidable(self): + return self._parent.is_overidable + + def add_children_gui(self, child_configuration, values): + item_type = child_configuration["type"] + klass = TypeToKlass.types.get(item_type) + + item = klass( + child_configuration, values, self.keys, self + ) + self.content_layout.addWidget(item) + + self.input_fields.append(item) + return item + + +class DictInvisible(QtWidgets.QWidget, PypeConfigurationWidget): + def __init__( + self, input_data, values, parent_keys, parent, label_widget=None + ): + self._parent = parent + + super(DictInvisible, self).__init__(parent) + self.setObjectName("DictInvisible") + + self.setAttribute(QtCore.Qt.WA_StyledBackground) + + layout = QtWidgets.QVBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(5) + + self._is_category = False + self._is_overriden = False + self.input_fields = [] + + if "key" not in input_data: + print(json.dumps(input_data, indent=4)) + + self.key = input_data["key"] + self.keys = list(parent_keys) + self.keys.append(self.key) + + for child_data in input_data.get("children", []): + self.add_children_gui(child_data, values) + + @property + def is_overidable(self): + return self._parent.is_overidable + + @property + def is_category(self): + return self._is_category + + @property + def is_overriden(self): + return self._is_overriden + + @property + def is_modified(self): + _is_modified = False + for input_field in self.input_fields: + if input_field.is_modified: + _is_modified = True + break + return _is_modified + + def item_value(self): + output = {} + for input_field in self.input_fields: + # TODO maybe merge instead of update should be used + # NOTE merge is custom function which merges 2 dicts + output.update(input_field.config_value()) + return output + + def config_value(self): + return {self.key: self.item_value()} + + def add_children_gui(self, child_configuration, values): + item_type = child_configuration["type"] + if item_type == "schema": + for _schema in child_configuration["children"]: + children = config.gui_schema(_schema) + self.add_children_gui(children, values) + return + + klass = TypeToKlass.types.get(item_type) + item = klass( + child_configuration, values, self.keys, self + ) + self.layout().addWidget(item) + + self.input_fields.append(item) + return item + + +class DictFormWidget(QtWidgets.QWidget): + def __init__( + self, input_data, values, parent_keys, parent, label_widget=None + ): + super(DictFormWidget, self).__init__(parent) + + self.input_fields = {} + self.content_layout = QtWidgets.QFormLayout(self) + + self.keys = list(parent_keys) + + for child_data in input_data.get("children", []): + self.add_children_gui(child_data, values) + + def item_value(self): + output = {} + for input_field in self.input_fields.values(): + # TODO maybe merge instead of update should be used + # NOTE merge is custom function which merges 2 dicts + output.update(input_field.config_value()) + return output + + @property + def is_overidable(self): + return self._parent.is_overidable + + def config_value(self): + return self.item_value() + + def add_children_gui(self, child_configuration, values): + item_type = child_configuration["type"] + key = child_configuration["key"] + # Pop label to not be set in child + label = child_configuration["label"] + + klass = TypeToKlass.types.get(item_type) + + label_widget = QtWidgets.QLabel(label) + item = klass( + child_configuration, values, self.keys, self, label_widget + ) + self.content_layout.addRow(label_widget, item) + self.input_fields[key] = item + return item + + +class TextListItem(QtWidgets.QWidget, PypeConfigurationWidget): + _btn_size = 20 + value_changed = QtCore.Signal() + + def __init__(self, parent): + super(TextListItem, self).__init__(parent) + + layout = QtWidgets.QHBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(3) + + self.text_input = QtWidgets.QLineEdit() + self.add_btn = QtWidgets.QPushButton("+") + self.remove_btn = QtWidgets.QPushButton("-") + + self.add_btn.setProperty("btn-type", "text-list") + self.remove_btn.setProperty("btn-type", "text-list") + + layout.addWidget(self.text_input, 1) + layout.addWidget(self.add_btn, 0) + layout.addWidget(self.remove_btn, 0) + + self.add_btn.setFixedSize(self._btn_size, self._btn_size) + self.remove_btn.setFixedSize(self._btn_size, self._btn_size) + self.add_btn.clicked.connect(self.on_add_clicked) + self.remove_btn.clicked.connect(self.on_remove_clicked) + + self.text_input.textChanged.connect(self._on_value_change) + + self.is_single = False + + def _on_value_change(self): + self.value_changed.emit() + + def row(self): + return self.parent().input_fields.index(self) + + def on_add_clicked(self): + self.parent().add_row(row=self.row() + 1) + + def on_remove_clicked(self): + if self.is_single: + self.text_input.setText("") + else: + self.parent().remove_row(self) + + def config_value(self): + return self.text_input.text() + + +class ModifiableDictItem(QtWidgets.QWidget, PypeConfigurationWidget): + _btn_size = 20 + value_changed = QtCore.Signal() + + def __init__(self, object_type, parent): + self._parent = parent + + super(ModifiableDictItem, self).__init__(parent) + + layout = QtWidgets.QHBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(3) + + ItemKlass = TypeToKlass.types[object_type] + + self.key_input = QtWidgets.QLineEdit() + self.key_input.setObjectName("DictKey") + + self.value_input = ItemKlass( + {}, + AS_WIDGET, + [], + self, + None + ) + self.add_btn = QtWidgets.QPushButton("+") + self.remove_btn = QtWidgets.QPushButton("-") + + self.add_btn.setProperty("btn-type", "text-list") + self.remove_btn.setProperty("btn-type", "text-list") + + layout.addWidget(self.key_input, 0) + layout.addWidget(self.value_input, 1) + layout.addWidget(self.add_btn, 0) + layout.addWidget(self.remove_btn, 0) + + self.add_btn.setFixedSize(self._btn_size, self._btn_size) + self.remove_btn.setFixedSize(self._btn_size, self._btn_size) + self.add_btn.clicked.connect(self.on_add_clicked) + self.remove_btn.clicked.connect(self.on_remove_clicked) + + self.key_input.textChanged.connect(self._on_value_change) + self.value_input.value_changed.connect(self._on_value_change) + + self.origin_key = self._key() + self.origin_value = self.value_input.item_value() + + self.is_single = False + + def _key(self): + return self.key_input.text() + + def _on_value_change(self): + self._update_style() + self.value_changed.emit() + + @property + def is_overidable(self): + return self._parent.is_overidable + + def _update_style(self): + is_modified = self._key() != self.origin_key + # if self._is_overidable and self.is_overriden: + # if is_modified: + # state = "overriden-modified" + # else: + # state = "overriden" + if is_modified: + state = "modified" + else: + state = "original" + + self.key_input.setProperty("state", state) + self.key_input.style().polish(self.key_input) + + def row(self): + return self.parent().input_fields.index(self) + + def on_add_clicked(self): + self.parent().add_row(row=self.row() + 1) + + def on_remove_clicked(self): + if self.is_single: + self.value_input.clear_value() + self.key_input.setText("") + else: + self.parent().remove_row(self) + + def config_value(self): + key = self.key_input.text() + value = self.value_input.item_value() + if not key: + return {} + return {key: value} + + +class ModifiableDictSubWidget(QtWidgets.QWidget, PypeConfigurationWidget): + value_changed = QtCore.Signal() + + def __init__(self, input_data, values, parent_keys, parent): + self._parent = parent + + super(ModifiableDictSubWidget, self).__init__(parent) + self.setObjectName("ModifiableDictSubWidget") + + layout = QtWidgets.QVBoxLayout(self) + layout.setContentsMargins(5, 5, 5, 5) + layout.setSpacing(5) + self.setLayout(layout) + + self.input_fields = [] + self.object_type = input_data["object_type"] + + self.key = input_data["key"] + keys = list(parent_keys) + keys.append(self.key) + self.keys = keys + + value = self.value_from_values(values) + if value is not NOT_SET: + for item_key, item_value in value.items(): + self.add_row(key=item_key, value=item_value) + + if self.count() == 0: + self.add_row() + + self.origin_value = self.config_value() + + @property + def is_overidable(self): + return self._parent.is_overidable + + def _on_value_change(self): + self.value_changed.emit() + + def count(self): + return len(self.input_fields) + + def add_row(self, row=None, key=None, value=None): + # Create new item + item_widget = ModifiableDictItem(self.object_type, self) + + # Set/unset if new item is single item + current_count = self.count() + if current_count == 0: + item_widget.is_single = True + elif current_count == 1: + for _input_field in self.input_fields: + _input_field.is_single = False + + item_widget.value_changed.connect(self._on_value_change) + + if row is None: + self.layout().addWidget(item_widget) + self.input_fields.append(item_widget) + else: + self.layout().insertWidget(row, item_widget) + self.input_fields.insert(row, item_widget) + + # Set value if entered value is not None + # else (when add button clicked) trigger `_on_value_change` + if value is not None and key is not None: + item_widget.origin_key = key + item_widget.key_input.setText(key) + item_widget.value_input.set_value(value, origin_value=True) + else: + self._on_value_change() + self.parent().updateGeometry() + + def remove_row(self, item_widget): + item_widget.value_changed.disconnect() + + self.layout().removeWidget(item_widget) + self.input_fields.remove(item_widget) + item_widget.setParent(None) + item_widget.deleteLater() + + current_count = self.count() + if current_count == 0: + self.add_row() + elif current_count == 1: + for _input_field in self.input_fields: + _input_field.is_single = True + + self._on_value_change() + self.parent().updateGeometry() + + def config_value(self): + output = {} + for item in self.input_fields: + item_value = item.config_value() + if item_value: + output.update(item_value) + return output + + +class ModifiableDict(ExpandingWidget, PypeConfigurationWidget): + def __init__( + self, input_data, values, parent_keys, parent, + label_widget=None + ): + self._parent = parent + + super(ModifiableDict, self).__init__(input_data["label"], parent) + self.setObjectName("ModifiableDict") + + self.value_widget = ModifiableDictSubWidget( + input_data, values, parent_keys, self + ) + self.value_widget.setAttribute(QtCore.Qt.WA_StyledBackground) + self.value_widget.value_changed.connect(self._on_value_change) + + self.set_content_widget(self.value_widget) + + self.key = input_data["key"] + + self.origin_value = self.item_value() + + def _on_value_change(self, value=None): + self.is_modified = self.item_value() != self.origin_value + self.is_overriden = True + + self._update_style() + + @property + def is_overidable(self): + return self._parent.is_overidable + + def _update_style(self): + if self.is_overidable and self.is_overriden: + if self.is_modified: + state = "overriden-modified" + else: + state = "overriden" + elif self.is_modified: + state = "modified" + else: + state = "original" + + self.label_widget.setProperty("state", state) + self.label_widget.style().polish(self.label_widget) + + def item_value(self): + return self.value_widget.config_value() + + def config_value(self): + return {self.key: self.item_value()} + + +TypeToKlass.types["boolean"] = BooleanWidget +TypeToKlass.types["text-singleline"] = TextSingleLineWidget +TypeToKlass.types["text-multiline"] = TextMultiLineWidget +TypeToKlass.types["int"] = IntegerWidget +TypeToKlass.types["float"] = FloatWidget +TypeToKlass.types["dict-expanding"] = DictExpandWidget +TypeToKlass.types["dict-form"] = DictFormWidget +TypeToKlass.types["dict-invisible"] = DictInvisible +TypeToKlass.types["dict-modifiable"] = ModifiableDict +TypeToKlass.types["list-text"] = TextListWidget diff --git a/pype/tools/config_setting/widgets/lib.py b/pype/tools/config_setting/widgets/lib.py new file mode 100644 index 0000000000..ac0a353d53 --- /dev/null +++ b/pype/tools/config_setting/widgets/lib.py @@ -0,0 +1,44 @@ +import uuid + + +class CustomNone: + """Created object can be used as custom None (not equal to None). + + WARNING: Multiple created objects are not equal either. + Exmple: + >>> a = CustomNone() + >>> a == None + False + >>> b = CustomNone() + >>> a == b + False + >>> a == a + True + """ + + def __init__(self): + """Create uuid as identifier for custom None.""" + self.identifier = str(uuid.uuid4()) + + def __bool__(self): + """Return False (like default None).""" + return False + + def __eq__(self, other): + """Equality is compared by identifier value.""" + if type(other) == type(self): + if other.identifier == self.identifier: + return True + return False + + def __str__(self): + """Return value of identifier when converted to string.""" + return "".format(str(self.identifier)) + + def __repr__(self): + """Representation of custom None.""" + return "".format(str(self.identifier)) + + +NOT_SET = CustomNone() +AS_WIDGET = CustomNone() diff --git a/pype/tools/config_setting/widgets/main.py b/pype/tools/config_setting/widgets/main.py new file mode 100644 index 0000000000..af23e68f77 --- /dev/null +++ b/pype/tools/config_setting/widgets/main.py @@ -0,0 +1,26 @@ +from Qt import QtWidgets +from .base import StudioWidget, ProjectWidget + + +class MainWidget(QtWidgets.QWidget): + widget_width = 1000 + widget_height = 600 + + def __init__(self, parent=None): + super(MainWidget, self).__init__(parent) + + self.resize(self.widget_width, self.widget_height) + + header_tab_widget = QtWidgets.QTabWidget(parent=self) + + studio_widget = StudioWidget() + project_widget = ProjectWidget() + header_tab_widget.addTab(studio_widget, "Studio") + header_tab_widget.addTab(project_widget, "Project") + + layout = QtWidgets.QVBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(0) + layout.addWidget(header_tab_widget) + + self.setLayout(layout) diff --git a/pype/tools/config_setting/widgets/tests.py b/pype/tools/config_setting/widgets/tests.py new file mode 100644 index 0000000000..53b67de3a1 --- /dev/null +++ b/pype/tools/config_setting/widgets/tests.py @@ -0,0 +1,127 @@ +from Qt import QtWidgets, QtCore + + +class SelectableMenu(QtWidgets.QMenu): + + selection_changed = QtCore.Signal() + + def mouseReleaseEvent(self, event): + action = self.activeAction() + if action and action.isEnabled(): + action.trigger() + self.selection_changed.emit() + else: + super(SelectableMenu, self).mouseReleaseEvent(event) + + def event(self, event): + result = super(SelectableMenu, self).event(event) + if event.type() == QtCore.QEvent.Show: + parent = self.parent() + + move_point = parent.mapToGlobal(QtCore.QPoint(0, parent.height())) + check_point = ( + move_point + + QtCore.QPoint(self.width(), self.height()) + ) + visibility_check = ( + QtWidgets.QApplication.desktop().rect().contains(check_point) + ) + if not visibility_check: + move_point -= QtCore.QPoint(0, parent.height() + self.height()) + self.move(move_point) + + self.updateGeometry() + self.repaint() + + return result + + +class AddibleComboBox(QtWidgets.QComboBox): + """Searchable ComboBox with empty placeholder value as first value""" + + def __init__(self, placeholder="", parent=None): + super(AddibleComboBox, self).__init__(parent) + + self.setEditable(True) + # self.setInsertPolicy(self.NoInsert) + + self.lineEdit().setPlaceholderText(placeholder) + # self.lineEdit().returnPressed.connect(self.on_return_pressed) + + # Apply completer settings + completer = self.completer() + completer.setCompletionMode(completer.PopupCompletion) + completer.setCaseSensitivity(QtCore.Qt.CaseInsensitive) + + # def on_return_pressed(self): + # text = self.lineEdit().text().strip() + # if not text: + # return + # + # index = self.findText(text) + # if index < 0: + # self.addItems([text]) + # index = self.findText(text) + + + + def populate(self, items): + self.clear() + # self.addItems([""]) # ensure first item is placeholder + self.addItems(items) + + def get_valid_value(self): + """Return the current text if it's a valid value else None + + Note: The empty placeholder value is valid and returns as "" + + """ + + text = self.currentText() + lookup = set(self.itemText(i) for i in range(self.count())) + if text not in lookup: + return None + + return text or None + + +class MultiselectEnum(QtWidgets.QWidget): + + selection_changed = QtCore.Signal() + + def __init__(self, title, parent=None): + super(MultiselectEnum, self).__init__(parent) + toolbutton = QtWidgets.QToolButton(self) + toolbutton.setText(title) + + toolmenu = SelectableMenu(toolbutton) + + toolbutton.setMenu(toolmenu) + toolbutton.setPopupMode(QtWidgets.QToolButton.MenuButtonPopup) + + layout = QtWidgets.QHBoxLayout() + layout.setContentsMargins(0, 0, 0, 0) + layout.addWidget(toolbutton) + + self.setLayout(layout) + + toolmenu.selection_changed.connect(self.selection_changed) + + self.toolbutton = toolbutton + self.toolmenu = toolmenu + self.main_layout = layout + + def populate(self, items): + self.toolmenu.clear() + self.addItems(items) + + def addItems(self, items): + for item in items: + action = self.toolmenu.addAction(item) + action.setCheckable(True) + action.setChecked(True) + self.toolmenu.addAction(action) + + def items(self): + for action in self.toolmenu.actions(): + yield action From 899b654acec03e32eaac8e48df169f24a4232d24 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 23 Jul 2020 11:48:27 +0200 Subject: [PATCH 023/813] added attribute is_group to be able recognize if key will be overriden on one subvalue change --- pype/tools/config_setting/widgets/inputs.py | 24 +++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/pype/tools/config_setting/widgets/inputs.py b/pype/tools/config_setting/widgets/inputs.py index 1ddc27278d..02dd86c946 100644 --- a/pype/tools/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/widgets/inputs.py @@ -1,10 +1,9 @@ +import json from Qt import QtWidgets, QtCore, QtGui from . import config from .base import PypeConfigurationWidget, TypeToKlass, ClickableWidget from .lib import NOT_SET, AS_WIDGET -import json - class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): value_changed = QtCore.Signal() @@ -15,6 +14,8 @@ class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): self._as_widget = values is AS_WIDGET self._parent = parent + self.is_group = False + super(BooleanWidget, self).__init__(parent) layout = QtWidgets.QVBoxLayout(self) @@ -129,6 +130,8 @@ class IntegerWidget(QtWidgets.QWidget, PypeConfigurationWidget): self._parent = parent self._as_widget = values is AS_WIDGET + self.is_group = False + super(IntegerWidget, self).__init__(parent) layout = QtWidgets.QVBoxLayout(self) @@ -220,6 +223,8 @@ class FloatWidget(QtWidgets.QWidget, PypeConfigurationWidget): self._parent = parent self._as_widget = values is AS_WIDGET + self.is_group = False + super(FloatWidget, self).__init__(parent) layout = QtWidgets.QVBoxLayout(self) @@ -315,6 +320,8 @@ class TextSingleLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): self._parent = parent self._as_widget = values is AS_WIDGET + self.is_group = False + super(TextSingleLineWidget, self).__init__(parent) layout = QtWidgets.QVBoxLayout(self) @@ -398,6 +405,8 @@ class TextMultiLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): ): self._parent = parent + self.is_group = False + super(TextMultiLineWidget, self).__init__(parent) layout = QtWidgets.QVBoxLayout(self) @@ -635,6 +644,9 @@ class TextListWidget(QtWidgets.QWidget, PypeConfigurationWidget): self, input_data, values, parent_keys, parent, label_widget=None ): self._parent = parent + + self.is_group = False + super(TextListWidget, self).__init__(parent) self.setObjectName("TextListWidget") @@ -785,6 +797,8 @@ class DictExpandWidget(QtWidgets.QWidget, PypeConfigurationWidget): )) self._parent = parent + self.is_group = input_data.get("is_group", False) + super(DictExpandWidget, self).__init__(parent) self.setObjectName("DictExpandWidget") top_part = ClickableWidget(parent=self) @@ -916,6 +930,8 @@ class DictInvisible(QtWidgets.QWidget, PypeConfigurationWidget): ): self._parent = parent + self.is_group = input_data.get("is_group", False) + super(DictInvisible, self).__init__(parent) self.setObjectName("DictInvisible") @@ -993,6 +1009,8 @@ class DictFormWidget(QtWidgets.QWidget): def __init__( self, input_data, values, parent_keys, parent, label_widget=None ): + self.is_group = input_data.get("is_group", False) + super(DictFormWidget, self).__init__(parent) self.input_fields = {} @@ -1288,6 +1306,8 @@ class ModifiableDict(ExpandingWidget, PypeConfigurationWidget): ): self._parent = parent + self.is_group = input_data.get("is_group", False) + super(ModifiableDict, self).__init__(input_data["label"], parent) self.setObjectName("ModifiableDict") From 908cccca120d9b372fb6f946148ed9496d1a6740 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 23 Jul 2020 11:57:16 +0200 Subject: [PATCH 024/813] moved widgets out of the box to widgets.py --- pype/tools/config_setting/widgets/base.py | 14 --- pype/tools/config_setting/widgets/inputs.py | 99 ++--------------- pype/tools/config_setting/widgets/widgets.py | 105 +++++++++++++++++++ 3 files changed, 112 insertions(+), 106 deletions(-) create mode 100644 pype/tools/config_setting/widgets/widgets.py diff --git a/pype/tools/config_setting/widgets/base.py b/pype/tools/config_setting/widgets/base.py index 0cc64a66de..3a495c6ae1 100644 --- a/pype/tools/config_setting/widgets/base.py +++ b/pype/tools/config_setting/widgets/base.py @@ -10,19 +10,6 @@ class TypeToKlass: types = {} -class ClickableWidget(QtWidgets.QLabel): - clicked = QtCore.Signal() - - def __init__(self, *args, **kwargs): - super(ClickableWidget, self).__init__(*args, **kwargs) - self.setObjectName("ExpandLabel") - - def mouseReleaseEvent(self, event): - if event.button() == QtCore.Qt.LeftButton: - self.clicked.emit() - super(ClickableWidget, self).mouseReleaseEvent(event) - - class PypeConfigurationWidget: is_category = False is_overriden = False @@ -197,7 +184,6 @@ class ProjectListWidget(QtWidgets.QWidget): # ) - class ProjectWidget(QtWidgets.QWidget, PypeConfigurationWidget): config_dir = os.path.join( os.path.dirname(os.path.dirname(__file__)), diff --git a/pype/tools/config_setting/widgets/inputs.py b/pype/tools/config_setting/widgets/inputs.py index 02dd86c946..7ef154ac9e 100644 --- a/pype/tools/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/widgets/inputs.py @@ -1,7 +1,13 @@ import json from Qt import QtWidgets, QtCore, QtGui from . import config -from .base import PypeConfigurationWidget, TypeToKlass, ClickableWidget +from .base import PypeConfigurationWidget, TypeToKlass +from .widgets import ( + ClickableWidget, + ExpandingWidget, + ModifiedIntSpinBox, + ModifiedFloatSpinBox +) from .lib import NOT_SET, AS_WIDGET @@ -97,30 +103,6 @@ class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): return {self.key: self.item_value()} -class ModifiedIntSpinBox(QtWidgets.QSpinBox): - def __init__(self, *args, **kwargs): - super(ModifiedIntSpinBox, self).__init__(*args, **kwargs) - self.setFocusPolicy(QtCore.Qt.StrongFocus) - - def wheelEvent(self, event): - if self.hasFocus(): - super(ModifiedIntSpinBox, self).wheelEvent(event) - else: - event.ignore() - - -class ModifiedFloatSpinBox(QtWidgets.QDoubleSpinBox): - def __init__(self, *args, **kwargs): - super(ModifiedFloatSpinBox, self).__init__(*args, **kwargs) - self.setFocusPolicy(QtCore.Qt.StrongFocus) - - def wheelEvent(self, event): - if self.hasFocus(): - super(ModifiedFloatSpinBox, self).wheelEvent(event) - else: - event.ignore() - - class IntegerWidget(QtWidgets.QWidget, PypeConfigurationWidget): value_changed = QtCore.Signal() @@ -720,73 +702,6 @@ class TextListWidget(QtWidgets.QWidget, PypeConfigurationWidget): return {self.key: self.item_value()} -class ExpandingWidget(QtWidgets.QWidget): - def __init__(self, label, parent): - super(ExpandingWidget, self).__init__(parent) - self.setObjectName("ExpandingWidget") - - top_part = ClickableWidget(parent=self) - - button_size = QtCore.QSize(5, 5) - button_toggle = QtWidgets.QToolButton(parent=top_part) - button_toggle.setProperty("btn-type", "expand-toggle") - button_toggle.setIconSize(button_size) - button_toggle.setArrowType(QtCore.Qt.RightArrow) - button_toggle.setCheckable(True) - button_toggle.setChecked(False) - - label_widget = QtWidgets.QLabel(label, parent=top_part) - label_widget.setObjectName("ExpandLabel") - - layout = QtWidgets.QHBoxLayout(top_part) - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(5) - layout.addWidget(button_toggle) - layout.addWidget(label_widget) - top_part.setLayout(layout) - - self.setAttribute(QtCore.Qt.WA_StyledBackground) - - self.top_part = top_part - self.button_toggle = button_toggle - self.label_widget = label_widget - - self.top_part.clicked.connect(self._top_part_clicked) - self.button_toggle.clicked.connect(self.toggle_content) - - def set_content_widget(self, content_widget): - main_layout = QtWidgets.QVBoxLayout(self) - main_layout.setContentsMargins(9, 9, 9, 9) - - content_widget.setVisible(False) - - main_layout.addWidget(self.top_part) - main_layout.addWidget(content_widget) - self.setLayout(main_layout) - - self.content_widget = content_widget - - def _top_part_clicked(self): - self.toggle_content(not self.button_toggle.isChecked()) - - def toggle_content(self, *args): - if len(args) > 0: - checked = args[0] - else: - checked = self.button_toggle.isChecked() - arrow_type = QtCore.Qt.RightArrow - if checked: - arrow_type = QtCore.Qt.DownArrow - self.button_toggle.setChecked(checked) - self.button_toggle.setArrowType(arrow_type) - self.content_widget.setVisible(checked) - self.parent().updateGeometry() - - def resizeEvent(self, event): - super(ExpandingWidget, self).resizeEvent(event) - self.content_widget.updateGeometry() - - class DictExpandWidget(QtWidgets.QWidget, PypeConfigurationWidget): def __init__( self, input_data, values, parent_keys, parent, label_widget=None diff --git a/pype/tools/config_setting/widgets/widgets.py b/pype/tools/config_setting/widgets/widgets.py new file mode 100644 index 0000000000..34fdfde1a5 --- /dev/null +++ b/pype/tools/config_setting/widgets/widgets.py @@ -0,0 +1,105 @@ +from Qt import QtWidgets, QtCore + + +class ModifiedIntSpinBox(QtWidgets.QSpinBox): + def __init__(self, *args, **kwargs): + super(ModifiedIntSpinBox, self).__init__(*args, **kwargs) + self.setFocusPolicy(QtCore.Qt.StrongFocus) + + def wheelEvent(self, event): + if self.hasFocus(): + super(ModifiedIntSpinBox, self).wheelEvent(event) + else: + event.ignore() + + +class ModifiedFloatSpinBox(QtWidgets.QDoubleSpinBox): + def __init__(self, *args, **kwargs): + super(ModifiedFloatSpinBox, self).__init__(*args, **kwargs) + self.setFocusPolicy(QtCore.Qt.StrongFocus) + + def wheelEvent(self, event): + if self.hasFocus(): + super(ModifiedFloatSpinBox, self).wheelEvent(event) + else: + event.ignore() + + +class ClickableWidget(QtWidgets.QLabel): + clicked = QtCore.Signal() + + def __init__(self, *args, **kwargs): + super(ClickableWidget, self).__init__(*args, **kwargs) + self.setObjectName("ExpandLabel") + + def mouseReleaseEvent(self, event): + if event.button() == QtCore.Qt.LeftButton: + self.clicked.emit() + super(ClickableWidget, self).mouseReleaseEvent(event) + + +class ExpandingWidget(QtWidgets.QWidget): + def __init__(self, label, parent): + super(ExpandingWidget, self).__init__(parent) + self.setObjectName("ExpandingWidget") + + top_part = ClickableWidget(parent=self) + + button_size = QtCore.QSize(5, 5) + button_toggle = QtWidgets.QToolButton(parent=top_part) + button_toggle.setProperty("btn-type", "expand-toggle") + button_toggle.setIconSize(button_size) + button_toggle.setArrowType(QtCore.Qt.RightArrow) + button_toggle.setCheckable(True) + button_toggle.setChecked(False) + + label_widget = QtWidgets.QLabel(label, parent=top_part) + label_widget.setObjectName("ExpandLabel") + + layout = QtWidgets.QHBoxLayout(top_part) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(5) + layout.addWidget(button_toggle) + layout.addWidget(label_widget) + top_part.setLayout(layout) + + self.setAttribute(QtCore.Qt.WA_StyledBackground) + + self.top_part = top_part + self.button_toggle = button_toggle + self.label_widget = label_widget + + self.top_part.clicked.connect(self._top_part_clicked) + self.button_toggle.clicked.connect(self.toggle_content) + + def set_content_widget(self, content_widget): + main_layout = QtWidgets.QVBoxLayout(self) + main_layout.setContentsMargins(9, 9, 9, 9) + + content_widget.setVisible(False) + + main_layout.addWidget(self.top_part) + main_layout.addWidget(content_widget) + self.setLayout(main_layout) + + self.content_widget = content_widget + + def _top_part_clicked(self): + self.toggle_content(not self.button_toggle.isChecked()) + + def toggle_content(self, *args): + if len(args) > 0: + checked = args[0] + else: + checked = self.button_toggle.isChecked() + arrow_type = QtCore.Qt.RightArrow + if checked: + arrow_type = QtCore.Qt.DownArrow + self.button_toggle.setChecked(checked) + self.button_toggle.setArrowType(arrow_type) + self.content_widget.setVisible(checked) + self.parent().updateGeometry() + + def resizeEvent(self, event): + super(ExpandingWidget, self).resizeEvent(event) + self.content_widget.updateGeometry() From 63c13c655e2b41694055ef8940ba7824b95fd352 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 23 Jul 2020 11:59:11 +0200 Subject: [PATCH 025/813] removed category attribute (as was replaced with group attribute) --- pype/tools/config_setting/widgets/base.py | 2 +- pype/tools/config_setting/widgets/inputs.py | 10 ---------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/pype/tools/config_setting/widgets/base.py b/pype/tools/config_setting/widgets/base.py index 3a495c6ae1..0fb4cd94cb 100644 --- a/pype/tools/config_setting/widgets/base.py +++ b/pype/tools/config_setting/widgets/base.py @@ -11,7 +11,7 @@ class TypeToKlass: class PypeConfigurationWidget: - is_category = False + is_group = False is_overriden = False is_modified = False diff --git a/pype/tools/config_setting/widgets/inputs.py b/pype/tools/config_setting/widgets/inputs.py index 7ef154ac9e..64deb4d909 100644 --- a/pype/tools/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/widgets/inputs.py @@ -762,7 +762,6 @@ class DictExpandWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.top_part.clicked.connect(self._top_part_clicked) self.button_toggle.clicked.connect(self.toggle_content) - self._is_category = False self._is_overriden = False self.input_fields = [] @@ -794,10 +793,6 @@ class DictExpandWidget(QtWidgets.QWidget, PypeConfigurationWidget): super(DictExpandWidget, self).resizeEvent(event) self.content_widget.updateGeometry() - @property - def is_category(self): - return self._is_category - @property def is_overriden(self): return self._is_overriden @@ -856,7 +851,6 @@ class DictInvisible(QtWidgets.QWidget, PypeConfigurationWidget): layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(5) - self._is_category = False self._is_overriden = False self.input_fields = [] @@ -874,10 +868,6 @@ class DictInvisible(QtWidgets.QWidget, PypeConfigurationWidget): def is_overidable(self): return self._parent.is_overidable - @property - def is_category(self): - return self._is_category - @property def is_overriden(self): return self._is_overriden From f184f71a0fbbe19b798ed7fa83e4dd77f208dd91 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 23 Jul 2020 17:12:45 +0200 Subject: [PATCH 026/813] handle project view selection --- pype/tools/config_setting/widgets/base.py | 92 ++++++++++++++++------- 1 file changed, 63 insertions(+), 29 deletions(-) diff --git a/pype/tools/config_setting/widgets/base.py b/pype/tools/config_setting/widgets/base.py index 0fb4cd94cb..bd19873b5f 100644 --- a/pype/tools/config_setting/widgets/base.py +++ b/pype/tools/config_setting/widgets/base.py @@ -120,37 +120,80 @@ class StudioWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.content_layout.addWidget(item) +class ProjectListView(QtWidgets.QListView): + left_mouse_released_at = QtCore.Signal(QtCore.QModelIndex) + + def mouseReleaseEvent(self, event): + if event.button() == QtCore.Qt.LeftButton: + index = self.indexAt(event.pos()) + self.left_mouse_released_at.emit(index) + super(ProjectListView, self).mouseReleaseEvent(event) + + class ProjectListWidget(QtWidgets.QWidget): default = "< Default >" - def __init__(self, parent=None): + def __init__(self, parent): + self._parent = parent + + self.current_project = None + super(ProjectListWidget, self).__init__(parent) - label = QtWidgets.QLabel("Project") - project_list = QtWidgets.QListView(self) + label_widget = QtWidgets.QLabel("Projects") + project_list = ProjectListView(self) project_list.setModel(QtGui.QStandardItemModel()) + # Do not allow editing + project_list.setEditTriggers( + QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers + ) + # Do not automatically handle selection + project_list.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection) + layout = QtWidgets.QVBoxLayout(self) - # content_margin = 5 - # layout.setContentsMargins( - # content_margin, - # content_margin, - # content_margin, - # content_margin - # ) - # layout.setSpacing(3) - layout.addWidget(label, 0) + layout.setSpacing(3) + layout.addWidget(label_widget, 0) layout.addWidget(project_list, 1) + project_list.left_mouse_released_at.connect(self.on_item_clicked) + self.project_list = project_list self.refresh() + def on_item_clicked(self, new_index): + new_project_name = new_index.data(QtCore.Qt.DisplayRole) + if new_project_name is None: + return + + if self.current_project == new_project_name: + return + + if self.validate_context_change(): + self.select_project(new_project_name) + self.current_project = new_project_name + + def validate_context_change(self): + # TODO add check if project can be changed (is modified) + return True + def project_name(self): - current_selection = self.project_list.currentText() - if current_selection == self.default: + if self.current_project == self.default: return None - return current_selection + return self.current_project + + def select_project(self, project_name): + model = self.project_list.model() + found_items = model.findItems(project_name) + if not found_items: + found_items = model.findItems(self.default) + + index = model.indexFromItem(found_items[0]) + self.project_list.selectionModel().clear() + self.project_list.selectionModel().setCurrentIndex( + index, QtCore.QItemSelectionModel.SelectionFlag.SelectCurrent + ) def refresh(self): selected_project = None @@ -163,25 +206,16 @@ class ProjectListWidget(QtWidgets.QWidget): items = [self.default] io.install() for project_doc in tuple(io.projects()): - print(project_doc["name"]) items.append(project_doc["name"]) for item in items: model.appendRow(QtGui.QStandardItem(item)) - if not selected_project: - selected_project = self.default + self.select_project(selected_project) - found_items = model.findItems(selected_project) - if found_items: - index = model.indexFromItem(found_items[0]) - c = QtCore.QItemSelectionModel.SelectionFlag.SelectCurrent - self.project_list.selectionModel().select( - index, c - ) - # self.project_list.selectionModel().setCurrentIndex( - # index, c - # ) + self.current_project = self.project_list.currentIndex().data( + QtCore.Qt.DisplayRole + ) class ProjectWidget(QtWidgets.QWidget, PypeConfigurationWidget): @@ -207,7 +241,7 @@ class ProjectWidget(QtWidgets.QWidget, PypeConfigurationWidget): scroll_widget.setWidgetResizable(True) scroll_widget.setWidget(content_widget) - project_list_widget = ProjectListWidget() + project_list_widget = ProjectListWidget(self) content_layout.addWidget(project_list_widget) self.project_list_widget = project_list_widget From 355d60c3b4b8224a680af956d693fd3be47c977d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 23 Jul 2020 18:03:10 +0200 Subject: [PATCH 027/813] make sure inputs has is_modified and is_overriden not with metaclass --- pype/tools/config_setting/widgets/base.py | 4 ---- pype/tools/config_setting/widgets/inputs.py | 22 ++++++++++++++++++++- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/pype/tools/config_setting/widgets/base.py b/pype/tools/config_setting/widgets/base.py index bd19873b5f..ebe15a370c 100644 --- a/pype/tools/config_setting/widgets/base.py +++ b/pype/tools/config_setting/widgets/base.py @@ -11,10 +11,6 @@ class TypeToKlass: class PypeConfigurationWidget: - is_group = False - is_overriden = False - is_modified = False - def config_value(self): raise NotImplementedError( "Method `config_value` is not implemented for `{}`.".format( diff --git a/pype/tools/config_setting/widgets/inputs.py b/pype/tools/config_setting/widgets/inputs.py index 64deb4d909..1ba062ec82 100644 --- a/pype/tools/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/widgets/inputs.py @@ -20,7 +20,9 @@ class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): self._as_widget = values is AS_WIDGET self._parent = parent + self.is_modified = False self.is_group = False + self.is_overriden = False super(BooleanWidget, self).__init__(parent) @@ -112,7 +114,9 @@ class IntegerWidget(QtWidgets.QWidget, PypeConfigurationWidget): self._parent = parent self._as_widget = values is AS_WIDGET + self.is_modified = False self.is_group = False + self.is_overriden = False super(IntegerWidget, self).__init__(parent) @@ -205,7 +209,9 @@ class FloatWidget(QtWidgets.QWidget, PypeConfigurationWidget): self._parent = parent self._as_widget = values is AS_WIDGET + self.is_modified = False self.is_group = False + self.is_overriden = False super(FloatWidget, self).__init__(parent) @@ -302,7 +308,9 @@ class TextSingleLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): self._parent = parent self._as_widget = values is AS_WIDGET + self.is_modified = False self.is_group = False + self.is_overriden = False super(TextSingleLineWidget, self).__init__(parent) @@ -387,7 +395,9 @@ class TextMultiLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): ): self._parent = parent + self.is_modified = False self.is_group = False + self.is_overriden = False super(TextMultiLineWidget, self).__init__(parent) @@ -627,7 +637,9 @@ class TextListWidget(QtWidgets.QWidget, PypeConfigurationWidget): ): self._parent = parent + self.is_modified = False self.is_group = False + self.is_overriden = False super(TextListWidget, self).__init__(parent) self.setObjectName("TextListWidget") @@ -712,6 +724,8 @@ class DictExpandWidget(QtWidgets.QWidget, PypeConfigurationWidget): )) self._parent = parent + self.is_modified = False + self.is_overriden = False self.is_group = input_data.get("is_group", False) super(DictExpandWidget, self).__init__(parent) @@ -840,6 +854,8 @@ class DictInvisible(QtWidgets.QWidget, PypeConfigurationWidget): ): self._parent = parent + self.is_modified = False + self.is_overriden = False self.is_group = input_data.get("is_group", False) super(DictInvisible, self).__init__(parent) @@ -914,7 +930,9 @@ class DictFormWidget(QtWidgets.QWidget): def __init__( self, input_data, values, parent_keys, parent, label_widget=None ): - self.is_group = input_data.get("is_group", False) + self.is_modified = False + self.is_overriden = False + self.is_group = False super(DictFormWidget, self).__init__(parent) @@ -1211,6 +1229,8 @@ class ModifiableDict(ExpandingWidget, PypeConfigurationWidget): ): self._parent = parent + self.is_modified = False + self.is_overriden = False self.is_group = input_data.get("is_group", False) super(ModifiableDict, self).__init__(input_data["label"], parent) From 6b42c7bdc17763b84244fe1f4c7c06bc09e92b77 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 24 Jul 2020 15:22:23 +0200 Subject: [PATCH 028/813] child modified attribute added --- pype/tools/config_setting/style/style.css | 5 ++ pype/tools/config_setting/widgets/inputs.py | 87 +++++++++++++++------ 2 files changed, 68 insertions(+), 24 deletions(-) diff --git a/pype/tools/config_setting/style/style.css b/pype/tools/config_setting/style/style.css index f559a4c3b3..db46cc4c24 100644 --- a/pype/tools/config_setting/style/style.css +++ b/pype/tools/config_setting/style/style.css @@ -34,6 +34,7 @@ QLabel[state="overriden"] { } QWidget[input-state="original"] {} + QWidget[input-state="modified"] { border-color: #137cbd; } @@ -83,6 +84,10 @@ QPushButton[btn-type="expand-toggle"] { background: #1d272f; } +#ModifiableDict[state="child-modified"] { + border-color: #137cbd; +} + #TextListSubWidget { border: 1px solid #455c6e; border-radius: 3px; diff --git a/pype/tools/config_setting/widgets/inputs.py b/pype/tools/config_setting/widgets/inputs.py index 1ba062ec82..9b24227a1d 100644 --- a/pype/tools/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/widgets/inputs.py @@ -1,5 +1,5 @@ import json -from Qt import QtWidgets, QtCore, QtGui +from Qt import QtWidgets, QtCore from . import config from .base import PypeConfigurationWidget, TypeToKlass from .widgets import ( @@ -67,6 +67,10 @@ class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): def clear_value(self): self.reset_value() + @property + def child_modified(self): + return self.is_modified + @property def is_overidable(self): return self._parent.is_overidable @@ -148,6 +152,10 @@ class IntegerWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.int_input.valueChanged.connect(self._on_value_change) + @property + def child_modified(self): + return self.is_modified + @property def is_overidable(self): return self._parent.is_overidable @@ -253,6 +261,10 @@ class FloatWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.float_input.valueChanged.connect(self._on_value_change) + @property + def child_modified(self): + return self.is_modified + @property def is_overidable(self): return self._parent.is_overidable @@ -342,6 +354,10 @@ class TextSingleLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.text_input.textChanged.connect(self._on_value_change) + @property + def child_modified(self): + return self.is_modified + @property def is_overidable(self): return self._parent.is_overidable @@ -427,6 +443,10 @@ class TextMultiLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.text_input.textChanged.connect(self._on_value_change) + @property + def child_modified(self): + return self.is_modified + @property def is_overidable(self): return self._parent.is_overidable @@ -671,6 +691,10 @@ class TextListWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.origin_value = self.item_value() + @property + def child_modified(self): + return self.is_modified + @property def is_overidable(self): return self._parent.is_overidable @@ -808,17 +832,11 @@ class DictExpandWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.content_widget.updateGeometry() @property - def is_overriden(self): - return self._is_overriden - - @property - def is_modified(self): - _is_modified = False + def child_modified(self): for input_field in self.input_fields: - if input_field.is_modified: - _is_modified = True - break - return _is_modified + if input_field.child_modified: + return True + return False def item_value(self): output = {} @@ -885,17 +903,11 @@ class DictInvisible(QtWidgets.QWidget, PypeConfigurationWidget): return self._parent.is_overidable @property - def is_overriden(self): - return self._is_overriden - - @property - def is_modified(self): - _is_modified = False + def child_modified(self): for input_field in self.input_fields: - if input_field.is_modified: - _is_modified = True - break - return _is_modified + if input_field.child_modified: + return True + return False def item_value(self): output = {} @@ -930,6 +942,8 @@ class DictFormWidget(QtWidgets.QWidget): def __init__( self, input_data, values, parent_keys, parent, label_widget=None ): + self._parent = parent + self.is_modified = False self.is_overriden = False self.is_group = False @@ -952,6 +966,13 @@ class DictFormWidget(QtWidgets.QWidget): output.update(input_field.config_value()) return output + @property + def child_modified(self): + for input_field in self.input_fields: + if input_field.child_modified: + return True + return False + @property def is_overidable(self): return self._parent.is_overidable @@ -1086,14 +1107,23 @@ class ModifiableDictItem(QtWidgets.QWidget, PypeConfigurationWidget): def is_overidable(self): return self._parent.is_overidable + def is_key_modified(self): + return self._key() != self.origin_key + + def is_value_modified(self): + return self.value_input.is_modified + + @property + def is_modified(self): + return self.is_value_modified() or self.is_key_modified() + def _update_style(self): - is_modified = self._key() != self.origin_key # if self._is_overidable and self.is_overriden: # if is_modified: # state = "overriden-modified" # else: # state = "overriden" - if is_modified: + if self.is_key_modified(): state = "modified" else: state = "original" @@ -1230,6 +1260,7 @@ class ModifiableDict(ExpandingWidget, PypeConfigurationWidget): self._parent = parent self.is_modified = False + self.child_modified = False self.is_overriden = False self.is_group = input_data.get("is_group", False) @@ -1249,7 +1280,7 @@ class ModifiableDict(ExpandingWidget, PypeConfigurationWidget): self.origin_value = self.item_value() def _on_value_change(self, value=None): - self.is_modified = self.item_value() != self.origin_value + self.child_modified = self.item_value() != self.origin_value self.is_overriden = True self._update_style() @@ -1259,6 +1290,14 @@ class ModifiableDict(ExpandingWidget, PypeConfigurationWidget): return self._parent.is_overidable def _update_style(self): + if self.child_modified: + widget_state = "child-modified" + else: + widget_state = "" + + self.setProperty("state", widget_state) + self.style().polish(self) + if self.is_overidable and self.is_overriden: if self.is_modified: state = "overriden-modified" From 7058dc7d10312fb8c11dc4e45a0225634100dad0 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 24 Jul 2020 16:49:20 +0200 Subject: [PATCH 029/813] style fixes --- pype/tools/config_setting/style/style.css | 2 +- pype/tools/config_setting/widgets/base.py | 4 +- pype/tools/config_setting/widgets/inputs.py | 98 +++++++++++++++++---- 3 files changed, 83 insertions(+), 21 deletions(-) diff --git a/pype/tools/config_setting/style/style.css b/pype/tools/config_setting/style/style.css index db46cc4c24..db2d746a23 100644 --- a/pype/tools/config_setting/style/style.css +++ b/pype/tools/config_setting/style/style.css @@ -84,7 +84,7 @@ QPushButton[btn-type="expand-toggle"] { background: #1d272f; } -#ModifiableDict[state="child-modified"] { +#DictExpandWidget[state="child-modified"], #ModifiableDict[state="child-modified"] { border-color: #137cbd; } diff --git a/pype/tools/config_setting/widgets/base.py b/pype/tools/config_setting/widgets/base.py index ebe15a370c..d2ccdddfc1 100644 --- a/pype/tools/config_setting/widgets/base.py +++ b/pype/tools/config_setting/widgets/base.py @@ -58,7 +58,7 @@ class StudioWidget(QtWidgets.QWidget, PypeConfigurationWidget): scroll_widget = QtWidgets.QScrollArea(self) content_widget = QtWidgets.QWidget(scroll_widget) content_layout = QtWidgets.QVBoxLayout(content_widget) - content_layout.setContentsMargins(0, 0, 0, 0) + content_layout.setContentsMargins(3, 3, 3, 3) content_layout.setSpacing(0) content_layout.setAlignment(QtCore.Qt.AlignTop) content_widget.setLayout(content_layout) @@ -229,7 +229,7 @@ class ProjectWidget(QtWidgets.QWidget, PypeConfigurationWidget): scroll_widget = QtWidgets.QScrollArea(self) content_widget = QtWidgets.QWidget(scroll_widget) content_layout = QtWidgets.QVBoxLayout(content_widget) - content_layout.setContentsMargins(0, 0, 0, 0) + content_layout.setContentsMargins(3, 3, 3, 3) content_layout.setSpacing(0) content_layout.setAlignment(QtCore.Qt.AlignTop) content_widget.setLayout(content_layout) diff --git a/pype/tools/config_setting/widgets/inputs.py b/pype/tools/config_setting/widgets/inputs.py index 9b24227a1d..c2a6eff0cc 100644 --- a/pype/tools/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/widgets/inputs.py @@ -19,7 +19,8 @@ class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): ): self._as_widget = values is AS_WIDGET self._parent = parent - + print(10*"*", parent) + print(values) self.is_modified = False self.is_group = False self.is_overriden = False @@ -94,7 +95,7 @@ class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): else: state = "original" - if not self._as_widget: + if self._as_widget: property_name = "input-state" else: property_name = "state" @@ -290,19 +291,25 @@ class FloatWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.value_changed.emit() def _update_style(self): - if not self._as_widget: - if self.is_overidable and self.is_overriden: - if self.is_modified: - state = "overriden-modified" - else: - state = "overriden" - elif self.is_modified: - state = "modified" + if self.is_overidable and self.is_overriden: + if self.is_modified: + state = "overriden-modified" else: - state = "original" + state = "overriden" + elif self.is_modified: + state = "modified" + else: + state = "original" - self.label_widget.setProperty("state", state) - self.label_widget.style().polish(self.label_widget) + if self._as_widget: + property_name = "input-state" + widget = self.float_input + else: + property_name = "state" + widget = self.label_widget + + widget.setProperty(property_name, state) + widget.style().polish(widget) def item_value(self): return self.float_input.value() @@ -393,8 +400,15 @@ class TextSingleLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): else: state = "original" - self.label_widget.setProperty("state", state) - self.label_widget.style().polish(self.label_widget) + if self._as_widget: + property_name = "input-state" + widget = self.text_input + else: + property_name = "state" + widget = self.label_widget + + widget.setProperty(property_name, state) + widget.style().polish(widget) def item_value(self): return self.text_input.text() @@ -482,8 +496,15 @@ class TextMultiLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): else: state = "original" - self.label_widget.setProperty("state", state) - self.label_widget.style().polish(self.label_widget) + if self._as_widget: + property_name = "input-state" + widget = self.text_input + else: + property_name = "state" + widget = self.label_widget + + widget.setProperty(property_name, state) + widget.style().polish(widget) def item_value(self): return self.text_input.toPlainText() @@ -652,6 +673,8 @@ class TextListSubWidget(QtWidgets.QWidget, PypeConfigurationWidget): class TextListWidget(QtWidgets.QWidget, PypeConfigurationWidget): + value_changed = QtCore.Signal() + def __init__( self, input_data, values, parent_keys, parent, label_widget=None ): @@ -705,6 +728,8 @@ class TextListWidget(QtWidgets.QWidget, PypeConfigurationWidget): self._update_style() + self.value_changed.emit() + def set_value(self, value, origin_value=False): self.value_widget.set_value(value) if origin_value: @@ -739,6 +764,8 @@ class TextListWidget(QtWidgets.QWidget, PypeConfigurationWidget): class DictExpandWidget(QtWidgets.QWidget, PypeConfigurationWidget): + value_changed = QtCore.Signal() + def __init__( self, input_data, values, parent_keys, parent, label_widget=None ): @@ -831,6 +858,33 @@ class DictExpandWidget(QtWidgets.QWidget, PypeConfigurationWidget): super(DictExpandWidget, self).resizeEvent(event) self.content_widget.updateGeometry() + def _on_value_change(self, value=None): + self.is_overriden = True + + self._update_style() + + def _update_style(self): + if self.child_modified: + widget_state = "child-modified" + else: + widget_state = "" + + self.setProperty("state", widget_state) + self.style().polish(self) + + if self.is_overidable and self.is_overriden: + if self.is_modified: + state = "overriden-modified" + else: + state = "overriden" + elif self.is_modified: + state = "modified" + else: + state = "original" + + self.button_toggle_text.setProperty("state", state) + self.button_toggle_text.style().polish(self.button_toggle_text) + @property def child_modified(self): for input_field in self.input_fields: @@ -860,6 +914,7 @@ class DictExpandWidget(QtWidgets.QWidget, PypeConfigurationWidget): item = klass( child_configuration, values, self.keys, self ) + item.value_changed.connect(self._on_value_change) self.content_layout.addWidget(item) self.input_fields.append(item) @@ -939,6 +994,8 @@ class DictInvisible(QtWidgets.QWidget, PypeConfigurationWidget): class DictFormWidget(QtWidgets.QWidget): + value_changed = QtCore.Signal() + def __init__( self, input_data, values, parent_keys, parent, label_widget=None ): @@ -958,6 +1015,9 @@ class DictFormWidget(QtWidgets.QWidget): for child_data in input_data.get("children", []): self.add_children_gui(child_data, values) + def _on_value_change(self): + self.value_changed.emit() + def item_value(self): output = {} for input_field in self.input_fields.values(): @@ -968,7 +1028,7 @@ class DictFormWidget(QtWidgets.QWidget): @property def child_modified(self): - for input_field in self.input_fields: + for input_field in self.input_fields.values(): if input_field.child_modified: return True return False @@ -989,9 +1049,11 @@ class DictFormWidget(QtWidgets.QWidget): klass = TypeToKlass.types.get(item_type) label_widget = QtWidgets.QLabel(label) + item = klass( child_configuration, values, self.keys, self, label_widget ) + item.value_changed.connect(self._on_value_change) self.content_layout.addRow(label_widget, item) self.input_fields[key] = item return item From e083e9585912bf920475244a0096e6e8eb1a3c57 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 24 Jul 2020 17:53:20 +0200 Subject: [PATCH 030/813] made it a little bit globally usable --- pype/tools/config_setting/interface.py | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/pype/tools/config_setting/interface.py b/pype/tools/config_setting/interface.py index e95f3f5fe3..e227443c95 100644 --- a/pype/tools/config_setting/interface.py +++ b/pype/tools/config_setting/interface.py @@ -1,17 +1,26 @@ import os import sys -os.environ["PYPE_CONFIG"] = ( - "C:/Users/jakub.trllo/Desktop/pype/pype-setup/repos/pype-config" + + +def folder_up(path, times=1): + if times <= 0: + return path + return folder_up(os.path.dirname(path), times - 1) + + +PYPE_SETUP_PATH = folder_up(__file__, 6) + +os.environ["PYPE_CONFIG"] = os.path.join( + PYPE_SETUP_PATH, "repos", "pype-config" ) os.environ["AVALON_MONGO"] = "mongodb://localhost:2707" sys_paths = ( "C:/Users/Public/pype_env2/Lib/site-packages", - "C:/Users/jakub.trllo/Desktop/pype/pype-setup", - "C:/Users/jakub.trllo/Desktop/pype/pype-setup/repos/pype", - "C:/Users/jakub.trllo/Desktop/pype/pype-setup/repos/avalon-core", - "C:/Users/jakub.trllo/Desktop/pype/pype-setup/repos/pyblish-base", - "C:/Users/jakub.trllo/Desktop/pype/pype-setup/repos/pyblish-lite", - "C:/Users/jakub.trllo/Desktop/pype/pype-setup/repos/pype-config" + PYPE_SETUP_PATH, + os.path.join(PYPE_SETUP_PATH, "repos", "pype"), + os.path.join(PYPE_SETUP_PATH, "repos", "avalon-core"), + os.path.join(PYPE_SETUP_PATH, "repos", "pyblish-base"), + os.path.join(PYPE_SETUP_PATH, "repos", "pype-config"), ) for path in sys_paths: sys.path.append(path) From 8d92a455e6b2083df3f8476a838f7908b00d9f77 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 24 Jul 2020 18:07:45 +0200 Subject: [PATCH 031/813] minor tweaks in temp config files --- .../global/es/applications.json | 39 ------------------- .../config_gui_schema/studio_gui_schema.json | 15 ++++--- 2 files changed, 10 insertions(+), 44 deletions(-) delete mode 100644 pype/tools/config_setting/config/studio_presets/global/es/applications.json diff --git a/pype/tools/config_setting/config/studio_presets/global/es/applications.json b/pype/tools/config_setting/config/studio_presets/global/es/applications.json deleted file mode 100644 index 35e399444c..0000000000 --- a/pype/tools/config_setting/config/studio_presets/global/es/applications.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "blender_2.80": true, - "blender_2.81": true, - "blender_2.82": true, - "blender_2.83": true, - "harmony_17": true, - "houdini_16": true, - "houdini_17": true, - "houdini_18": true, - "maya_2016": true, - "maya_2017": true, - "maya_2018": true, - "maya_2019": true, - "maya_2020": true, - "nuke_10.0": true, - "nuke_11.0": true, - "nuke_11.2": true, - "nuke_11.3": true, - "nuke_12.0": true, - "nukex_10.0": true, - "nukex_11.0": true, - "nukex_11.2": true, - "nukex_11.3": true, - "nukex_12.0": true, - "nukestudio_10.0": true, - "nukestudio_11.0": true, - "nukestudio_11.2": true, - "nukestudio_11.3": true, - "nukestudio_12.0": true, - "photoshop_2020": true, - "premiere_2019": true, - "premiere_2020": true, - "python_2": true, - "python_3": true, - "resolve_16": true, - "shell": true, - "storyboardpro_7": true, - "unreal_4.21": true -} diff --git a/pype/tools/config_setting/config_gui_schema/studio_gui_schema.json b/pype/tools/config_setting/config_gui_schema/studio_gui_schema.json index 7d902bb8db..1a49735b8a 100644 --- a/pype/tools/config_setting/config_gui_schema/studio_gui_schema.json +++ b/pype/tools/config_setting/config_gui_schema/studio_gui_schema.json @@ -4,11 +4,16 @@ "label": "Studio", "children": [ { - "type": "schema", - "children": [ - "applications_gui_schema", - "tools_gui_schema" - ] + "key": "global", + "type": "dict-invisible", + "label": "Global", + "children": [{ + "type": "schema", + "children": [ + "applications_gui_schema", + "tools_gui_schema" + ] + }] }, { "key": "muster", "type": "dict-invisible", From cbb77c05b10affef9a66a2bfb2284635adbaa71f Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 24 Jul 2020 18:07:57 +0200 Subject: [PATCH 032/813] preparation for studio save --- pype/tools/config_setting/widgets/base.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/pype/tools/config_setting/widgets/base.py b/pype/tools/config_setting/widgets/base.py index d2ccdddfc1..337beb040e 100644 --- a/pype/tools/config_setting/widgets/base.py +++ b/pype/tools/config_setting/widgets/base.py @@ -80,10 +80,10 @@ class StudioWidget(QtWidgets.QWidget, PypeConfigurationWidget): footer_widget = QtWidgets.QWidget() footer_layout = QtWidgets.QHBoxLayout(footer_widget) - btn = QtWidgets.QPushButton("Finish") + save_btn = QtWidgets.QPushButton("Save") spacer_widget = QtWidgets.QWidget() footer_layout.addWidget(spacer_widget, 1) - footer_layout.addWidget(btn, 0) + footer_layout.addWidget(save_btn, 0) layout = QtWidgets.QVBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) @@ -93,9 +93,9 @@ class StudioWidget(QtWidgets.QWidget, PypeConfigurationWidget): layout.addWidget(scroll_widget, 1) layout.addWidget(footer_widget, 0) - btn.clicked.connect(self.___finish) + save_btn.clicked.connect(self._save) - def ___finish(self): + def _save(self): output = {} for item in self.input_fields: output.update(item.config_value()) @@ -104,6 +104,9 @@ class StudioWidget(QtWidgets.QWidget, PypeConfigurationWidget): _output = {key: output} output = _output + config_with_metadata = config.studio_presets_with_metadata() + + print(json.dumps(config_with_metadata, indent=4)) print(json.dumps(output, indent=4)) def add_children_gui(self, child_configuration, values): From 5ba9eb2eed39d05647d360621353d9a3a4ba3cb1 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 24 Jul 2020 18:08:35 +0200 Subject: [PATCH 033/813] paths are correct and metadata presets are with metadata --- pype/tools/config_setting/interface.py | 6 ++---- pype/tools/config_setting/widgets/config.py | 2 ++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pype/tools/config_setting/interface.py b/pype/tools/config_setting/interface.py index e227443c95..a8c05f5af3 100644 --- a/pype/tools/config_setting/interface.py +++ b/pype/tools/config_setting/interface.py @@ -8,8 +8,7 @@ def folder_up(path, times=1): return folder_up(os.path.dirname(path), times - 1) -PYPE_SETUP_PATH = folder_up(__file__, 6) - +PYPE_SETUP_PATH = folder_up(os.path.realpath(__file__), 6) os.environ["PYPE_CONFIG"] = os.path.join( PYPE_SETUP_PATH, "repos", "pype-config" ) @@ -19,8 +18,7 @@ sys_paths = ( PYPE_SETUP_PATH, os.path.join(PYPE_SETUP_PATH, "repos", "pype"), os.path.join(PYPE_SETUP_PATH, "repos", "avalon-core"), - os.path.join(PYPE_SETUP_PATH, "repos", "pyblish-base"), - os.path.join(PYPE_SETUP_PATH, "repos", "pype-config"), + os.path.join(PYPE_SETUP_PATH, "repos", "pyblish-base") ) for path in sys_paths: sys.path.append(path) diff --git a/pype/tools/config_setting/widgets/config.py b/pype/tools/config_setting/widgets/config.py index 335299cb2f..58b1e03a25 100644 --- a/pype/tools/config_setting/widgets/config.py +++ b/pype/tools/config_setting/widgets/config.py @@ -160,10 +160,12 @@ def global_project_presets(**kwargs): def studio_presets_with_metadata(*args, **kwargs): + kwargs["with_metadata"] = True return load_jsons_from_dir(studio_presets_path, *args, **kwargs) def global_project_presets_with_metadata(**kwargs): + kwargs["with_metadata"] = True return load_jsons_from_dir(project_presets_path, **kwargs) From 5ba7a45235ca82486fe3db36774d4892aa3fedd4 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 24 Jul 2020 18:35:44 +0200 Subject: [PATCH 034/813] more close to saving studio presets --- pype/tools/config_setting/widgets/base.py | 42 ++++++++++++++++++++--- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/pype/tools/config_setting/widgets/base.py b/pype/tools/config_setting/widgets/base.py index 337beb040e..a5f912d7cb 100644 --- a/pype/tools/config_setting/widgets/base.py +++ b/pype/tools/config_setting/widgets/base.py @@ -4,6 +4,7 @@ from Qt import QtWidgets, QtCore, QtGui from . import config from .lib import NOT_SET from avalon import io +from queue import Queue class TypeToKlass: @@ -96,18 +97,49 @@ class StudioWidget(QtWidgets.QWidget, PypeConfigurationWidget): save_btn.clicked.connect(self._save) def _save(self): - output = {} + all_values = {} for item in self.input_fields: - output.update(item.config_value()) + all_values.update(item.config_value()) for key in reversed(self.keys): - _output = {key: output} - output = _output + _all_values = {key: all_values} + all_values = _all_values + # Skip first key + all_values = all_values["studio"] + + # Load studio data with metadata config_with_metadata = config.studio_presets_with_metadata() print(json.dumps(config_with_metadata, indent=4)) - print(json.dumps(output, indent=4)) + print(json.dumps(all_values, indent=4)) + + per_file_values = {} + process_queue = Queue() + for _key, _values in all_values.items(): + process_queue.put(( + config.studio_presets_path, _key, config_with_metadata, _values + )) + + while not process_queue.empty(): + path, key, metadata, values = process_queue.get() + new_path = os.path.join(path, key) + # TODO this should not be + if key in metadata: + key_metadata = metadata[key] + + if key_metadata["type"] == "file": + new_path += ".json" + per_file_values[new_path] = values + continue + + for new_key, new_values in values.items(): + process_queue.put( + (new_path, new_key, key_metadata["value"], new_values) + ) + + for path in per_file_values: + print(path) def add_children_gui(self, child_configuration, values): item_type = child_configuration["type"] From 22adf6f9e3ffb3dfe2891f0bc23198a112babbe6 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 27 Jul 2020 10:51:17 +0200 Subject: [PATCH 035/813] fix dictinary value changed --- pype/tools/config_setting/widgets/inputs.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pype/tools/config_setting/widgets/inputs.py b/pype/tools/config_setting/widgets/inputs.py index c2a6eff0cc..01a22bd702 100644 --- a/pype/tools/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/widgets/inputs.py @@ -861,6 +861,8 @@ class DictExpandWidget(QtWidgets.QWidget, PypeConfigurationWidget): def _on_value_change(self, value=None): self.is_overriden = True + self.value_changed.emit() + self._update_style() def _update_style(self): From 4d52556f2b4cbbbe7b4c9c28944d4c88d877bab3 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 27 Jul 2020 11:14:56 +0200 Subject: [PATCH 036/813] fix values keys --- pype/tools/config_setting/widgets/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/tools/config_setting/widgets/base.py b/pype/tools/config_setting/widgets/base.py index a5f912d7cb..b4ad206076 100644 --- a/pype/tools/config_setting/widgets/base.py +++ b/pype/tools/config_setting/widgets/base.py @@ -130,7 +130,7 @@ class StudioWidget(QtWidgets.QWidget, PypeConfigurationWidget): if key_metadata["type"] == "file": new_path += ".json" - per_file_values[new_path] = values + per_file_values[new_path] = {key: values} continue for new_key, new_values in values.items(): From ce9f4315086b039ca0eb7b3b2ae44a09df2d32b7 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 27 Jul 2020 12:04:48 +0200 Subject: [PATCH 037/813] saving studio files --- pype/tools/config_setting/widgets/base.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pype/tools/config_setting/widgets/base.py b/pype/tools/config_setting/widgets/base.py index b4ad206076..b68ec4126a 100644 --- a/pype/tools/config_setting/widgets/base.py +++ b/pype/tools/config_setting/widgets/base.py @@ -130,7 +130,7 @@ class StudioWidget(QtWidgets.QWidget, PypeConfigurationWidget): if key_metadata["type"] == "file": new_path += ".json" - per_file_values[new_path] = {key: values} + per_file_values[new_path] = values continue for new_key, new_values in values.items(): @@ -138,8 +138,9 @@ class StudioWidget(QtWidgets.QWidget, PypeConfigurationWidget): (new_path, new_key, key_metadata["value"], new_values) ) - for path in per_file_values: - print(path) + for file_path, file_values in per_file_values.items(): + with open(file_path, "w") as file_stream: + json.dump(file_values, file_stream, indent=4) def add_children_gui(self, child_configuration, values): item_type = child_configuration["type"] From 8ebbf14097557f22dd87f59a040bc1f353cd221d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 27 Jul 2020 17:36:04 +0200 Subject: [PATCH 038/813] added child overriden attribute --- pype/tools/config_setting/style/style.css | 8 +++ pype/tools/config_setting/widgets/inputs.py | 70 +++++++++++++++++++-- 2 files changed, 74 insertions(+), 4 deletions(-) diff --git a/pype/tools/config_setting/style/style.css b/pype/tools/config_setting/style/style.css index db2d746a23..1371056c1a 100644 --- a/pype/tools/config_setting/style/style.css +++ b/pype/tools/config_setting/style/style.css @@ -88,6 +88,14 @@ QPushButton[btn-type="expand-toggle"] { border-color: #137cbd; } +#DictExpandWidget[state="child-overriden"], #ModifiableDict[state="child-overriden"] { + border-color: #ff8c1a; +} + +#DictExpandWidget[state="child-overriden-modified"], #ModifiableDict[state="child-overriden-modified"] { + border-color: #00b386; +} + #TextListSubWidget { border: 1px solid #455c6e; border-radius: 3px; diff --git a/pype/tools/config_setting/widgets/inputs.py b/pype/tools/config_setting/widgets/inputs.py index 01a22bd702..9bed1207a8 100644 --- a/pype/tools/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/widgets/inputs.py @@ -19,8 +19,7 @@ class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): ): self._as_widget = values is AS_WIDGET self._parent = parent - print(10*"*", parent) - print(values) + self.is_modified = False self.is_group = False self.is_overriden = False @@ -72,6 +71,10 @@ class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): def child_modified(self): return self.is_modified + @property + def child_overriden(self): + return self.is_overriden + @property def is_overidable(self): return self._parent.is_overidable @@ -157,6 +160,10 @@ class IntegerWidget(QtWidgets.QWidget, PypeConfigurationWidget): def child_modified(self): return self.is_modified + @property + def child_overriden(self): + return self.is_overriden + @property def is_overidable(self): return self._parent.is_overidable @@ -266,6 +273,10 @@ class FloatWidget(QtWidgets.QWidget, PypeConfigurationWidget): def child_modified(self): return self.is_modified + @property + def child_overriden(self): + return self.is_overriden + @property def is_overidable(self): return self._parent.is_overidable @@ -365,6 +376,10 @@ class TextSingleLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): def child_modified(self): return self.is_modified + @property + def child_overriden(self): + return self.is_overriden + @property def is_overidable(self): return self._parent.is_overidable @@ -461,6 +476,10 @@ class TextMultiLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): def child_modified(self): return self.is_modified + @property + def child_overriden(self): + return self.is_overriden + @property def is_overidable(self): return self._parent.is_overidable @@ -718,6 +737,10 @@ class TextListWidget(QtWidgets.QWidget, PypeConfigurationWidget): def child_modified(self): return self.is_modified + @property + def child_overriden(self): + return self.is_overriden + @property def is_overidable(self): return self._parent.is_overidable @@ -866,8 +889,15 @@ class DictExpandWidget(QtWidgets.QWidget, PypeConfigurationWidget): self._update_style() def _update_style(self): - if self.child_modified: + child_modified = self.child_modified + child_overriden = self.child_overriden + + if child_modified and child_overriden: + widget_state = "child-overriden-modified" + elif child_modified: widget_state = "child-modified" + elif child_overriden: + widget_state = "child-overriden" else: widget_state = "" @@ -894,6 +924,13 @@ class DictExpandWidget(QtWidgets.QWidget, PypeConfigurationWidget): return True return False + @property + def child_overriden(self): + for input_field in self.input_fields: + if input_field.child_overriden: + return True + return False + def item_value(self): output = {} for input_field in self.input_fields: @@ -966,6 +1003,13 @@ class DictInvisible(QtWidgets.QWidget, PypeConfigurationWidget): return True return False + @property + def child_overriden(self): + for input_field in self.input_fields: + if input_field.child_overriden: + return True + return False + def item_value(self): output = {} for input_field in self.input_fields: @@ -1035,6 +1079,13 @@ class DictFormWidget(QtWidgets.QWidget): return True return False + @property + def child_overriden(self): + for input_field in self.input_fields: + if input_field.child_overriden: + return True + return False + @property def is_overidable(self): return self._parent.is_overidable @@ -1349,13 +1400,24 @@ class ModifiableDict(ExpandingWidget, PypeConfigurationWidget): self._update_style() + @property + def child_overriden(self): + return self.is_overriden + @property def is_overidable(self): return self._parent.is_overidable def _update_style(self): - if self.child_modified: + child_modified = self.child_modified + child_overriden = self.child_overriden + + if child_modified and child_overriden: + widget_state = "child-overriden-modified" + elif child_modified: widget_state = "child-modified" + elif child_overriden: + widget_state = "child-overriden" else: widget_state = "" From 669631d71edc96c8adeb5ff32954389a4f5edfb7 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 27 Jul 2020 18:55:36 +0200 Subject: [PATCH 039/813] removed duplicated class --- pype/tools/config_setting/widgets/inputs.py | 50 --------------------- 1 file changed, 50 deletions(-) diff --git a/pype/tools/config_setting/widgets/inputs.py b/pype/tools/config_setting/widgets/inputs.py index 9bed1207a8..01c69c1c0b 100644 --- a/pype/tools/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/widgets/inputs.py @@ -1112,56 +1112,6 @@ class DictFormWidget(QtWidgets.QWidget): return item -class TextListItem(QtWidgets.QWidget, PypeConfigurationWidget): - _btn_size = 20 - value_changed = QtCore.Signal() - - def __init__(self, parent): - super(TextListItem, self).__init__(parent) - - layout = QtWidgets.QHBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(3) - - self.text_input = QtWidgets.QLineEdit() - self.add_btn = QtWidgets.QPushButton("+") - self.remove_btn = QtWidgets.QPushButton("-") - - self.add_btn.setProperty("btn-type", "text-list") - self.remove_btn.setProperty("btn-type", "text-list") - - layout.addWidget(self.text_input, 1) - layout.addWidget(self.add_btn, 0) - layout.addWidget(self.remove_btn, 0) - - self.add_btn.setFixedSize(self._btn_size, self._btn_size) - self.remove_btn.setFixedSize(self._btn_size, self._btn_size) - self.add_btn.clicked.connect(self.on_add_clicked) - self.remove_btn.clicked.connect(self.on_remove_clicked) - - self.text_input.textChanged.connect(self._on_value_change) - - self.is_single = False - - def _on_value_change(self): - self.value_changed.emit() - - def row(self): - return self.parent().input_fields.index(self) - - def on_add_clicked(self): - self.parent().add_row(row=self.row() + 1) - - def on_remove_clicked(self): - if self.is_single: - self.text_input.setText("") - else: - self.parent().remove_row(self) - - def config_value(self): - return self.text_input.text() - - class ModifiableDictItem(QtWidgets.QWidget, PypeConfigurationWidget): _btn_size = 20 value_changed = QtCore.Signal() From a8fb14a1bca842e45c81eb2d60b16f1a68f1505b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 27 Jul 2020 18:55:53 +0200 Subject: [PATCH 040/813] added is_group feature --- pype/tools/config_setting/widgets/inputs.py | 160 ++++++++++++++++++-- 1 file changed, 145 insertions(+), 15 deletions(-) diff --git a/pype/tools/config_setting/widgets/inputs.py b/pype/tools/config_setting/widgets/inputs.py index 01c69c1c0b..485eda6d35 100644 --- a/pype/tools/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/widgets/inputs.py @@ -11,6 +11,14 @@ from .widgets import ( from .lib import NOT_SET, AS_WIDGET +class SchemeGroupHierarchyBug(Exception): + def __init__(self, msg=None): + if not msg: + # TODO better message + msg = "SCHEME BUG: Attribute `is_group` is mixed in the hierarchy" + super(SchemeGroupHierarchyBug, self).__init(msg) + + class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): value_changed = QtCore.Signal() @@ -20,8 +28,19 @@ class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): self._as_widget = values is AS_WIDGET self._parent = parent + any_parent_is_group = parent.is_group + if not any_parent_is_group: + any_parent_is_group = parent.any_parent_is_group + + is_group = input_data.get("is_group", False) + if is_group and any_parent_is_group: + raise SchemeGroupHierarchyBug() + + if not any_parent_is_group and not is_group: + is_group = True + + self.is_group = is_group self.is_modified = False - self.is_group = False self.is_overriden = False super(BooleanWidget, self).__init__(parent) @@ -122,8 +141,19 @@ class IntegerWidget(QtWidgets.QWidget, PypeConfigurationWidget): self._parent = parent self._as_widget = values is AS_WIDGET + any_parent_is_group = parent.is_group + if not any_parent_is_group: + any_parent_is_group = parent.any_parent_is_group + + is_group = input_data.get("is_group", False) + if is_group and any_parent_is_group: + raise SchemeGroupHierarchyBug() + + if not any_parent_is_group and not is_group: + is_group = True + + self.is_group = is_group self.is_modified = False - self.is_group = False self.is_overriden = False super(IntegerWidget, self).__init__(parent) @@ -225,8 +255,19 @@ class FloatWidget(QtWidgets.QWidget, PypeConfigurationWidget): self._parent = parent self._as_widget = values is AS_WIDGET + any_parent_is_group = parent.is_group + if not any_parent_is_group: + any_parent_is_group = parent.any_parent_is_group + + is_group = input_data.get("is_group", False) + if is_group and any_parent_is_group: + raise SchemeGroupHierarchyBug() + + if not any_parent_is_group and not is_group: + is_group = True + + self.is_group = is_group self.is_modified = False - self.is_group = False self.is_overriden = False super(FloatWidget, self).__init__(parent) @@ -338,8 +379,19 @@ class TextSingleLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): self._parent = parent self._as_widget = values is AS_WIDGET + any_parent_is_group = parent.is_group + if not any_parent_is_group: + any_parent_is_group = parent.any_parent_is_group + + is_group = input_data.get("is_group", False) + if is_group and any_parent_is_group: + raise SchemeGroupHierarchyBug() + + if not any_parent_is_group and not is_group: + is_group = True + + self.is_group = is_group self.is_modified = False - self.is_group = False self.is_overriden = False super(TextSingleLineWidget, self).__init__(parent) @@ -440,8 +492,19 @@ class TextMultiLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): ): self._parent = parent + any_parent_is_group = parent.is_group + if not any_parent_is_group: + any_parent_is_group = parent.any_parent_is_group + + is_group = input_data.get("is_group", False) + if is_group and any_parent_is_group: + raise SchemeGroupHierarchyBug() + + if not any_parent_is_group and not is_group: + is_group = True + + self.is_group = is_group self.is_modified = False - self.is_group = False self.is_overriden = False super(TextMultiLineWidget, self).__init__(parent) @@ -699,8 +762,16 @@ class TextListWidget(QtWidgets.QWidget, PypeConfigurationWidget): ): self._parent = parent + any_parent_is_group = parent.is_group + if not any_parent_is_group: + any_parent_is_group = parent.any_parent_is_group + + is_group = input_data.get("is_group", False) + if is_group and any_parent_is_group: + raise SchemeGroupHierarchyBug() + self.is_modified = False - self.is_group = False + self.is_group = is_group self.is_overriden = False super(TextListWidget, self).__init__(parent) @@ -747,7 +818,8 @@ class TextListWidget(QtWidgets.QWidget, PypeConfigurationWidget): def _on_value_change(self, value=None): self.is_modified = self.item_value() != self.origin_value - self.is_overriden = True + if self.is_group and self.is_overidable: + self.is_overriden = True self._update_style() @@ -798,9 +870,19 @@ class DictExpandWidget(QtWidgets.QWidget, PypeConfigurationWidget): )) self._parent = parent + any_parent_is_group = parent.is_group + if not any_parent_is_group: + any_parent_is_group = parent.any_parent_is_group + + is_group = input_data.get("is_group", False) + if is_group and any_parent_is_group: + raise SchemeGroupHierarchyBug() + + self.any_parent_is_group = any_parent_is_group + self.is_modified = False - self.is_overriden = False - self.is_group = input_data.get("is_group", False) + self._is_overriden = False + self.is_group = is_group super(DictExpandWidget, self).__init__(parent) self.setObjectName("DictExpandWidget") @@ -850,7 +932,6 @@ class DictExpandWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.top_part.clicked.connect(self._top_part_clicked) self.button_toggle.clicked.connect(self.toggle_content) - self._is_overriden = False self.input_fields = [] self.key = input_data["key"] @@ -881,8 +962,15 @@ class DictExpandWidget(QtWidgets.QWidget, PypeConfigurationWidget): super(DictExpandWidget, self).resizeEvent(event) self.content_widget.updateGeometry() + @property + def is_overriden(self): + if self._is_overriden: + return self._is_overriden + + def _on_value_change(self, value=None): - self.is_overriden = True + if self.is_group: + self._is_overriden = True self.value_changed.emit() @@ -966,9 +1054,19 @@ class DictInvisible(QtWidgets.QWidget, PypeConfigurationWidget): ): self._parent = parent + any_parent_is_group = parent.is_group + if not any_parent_is_group: + any_parent_is_group = parent.any_parent_is_group + + is_group = input_data.get("is_group", False) + if is_group and any_parent_is_group: + raise SchemeGroupHierarchyBug() + + self.any_parent_is_group = any_parent_is_group + self.is_modified = False self.is_overriden = False - self.is_group = input_data.get("is_group", False) + self.is_group = is_group super(DictInvisible, self).__init__(parent) self.setObjectName("DictInvisible") @@ -979,7 +1077,6 @@ class DictInvisible(QtWidgets.QWidget, PypeConfigurationWidget): layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(5) - self._is_overriden = False self.input_fields = [] if "key" not in input_data: @@ -1047,6 +1144,12 @@ class DictFormWidget(QtWidgets.QWidget): ): self._parent = parent + any_parent_is_group = parent.is_group + if not any_parent_is_group: + any_parent_is_group = parent.any_parent_is_group + + self.any_parent_is_group = any_parent_is_group + self.is_modified = False self.is_overriden = False self.is_group = False @@ -1168,6 +1271,14 @@ class ModifiableDictItem(QtWidgets.QWidget, PypeConfigurationWidget): self._update_style() self.value_changed.emit() + @property + def is_group(self): + return self._parent.is_group + + @property + def any_parent_is_group(self): + return self._parent.any_parent_is_group + @property def is_overidable(self): return self._parent.is_overidable @@ -1253,6 +1364,14 @@ class ModifiableDictSubWidget(QtWidgets.QWidget, PypeConfigurationWidget): def is_overidable(self): return self._parent.is_overidable + @property + def is_group(self): + return self._parent.is_group + + @property + def any_parent_is_group(self): + return self._parent.any_parent_is_group + def _on_value_change(self): self.value_changed.emit() @@ -1324,10 +1443,20 @@ class ModifiableDict(ExpandingWidget, PypeConfigurationWidget): ): self._parent = parent + any_parent_is_group = parent.is_group + if not any_parent_is_group: + any_parent_is_group = parent.any_parent_is_group + + is_group = input_data.get("is_group", False) + if is_group and any_parent_is_group: + raise SchemeGroupHierarchyBug() + + self.any_parent_is_group = any_parent_is_group + self.is_modified = False self.child_modified = False self.is_overriden = False - self.is_group = input_data.get("is_group", False) + self.is_group = is_group super(ModifiableDict, self).__init__(input_data["label"], parent) self.setObjectName("ModifiableDict") @@ -1346,7 +1475,8 @@ class ModifiableDict(ExpandingWidget, PypeConfigurationWidget): def _on_value_change(self, value=None): self.child_modified = self.item_value() != self.origin_value - self.is_overriden = True + if self.is_group: + self.is_overriden = True self._update_style() From e7e3526c72b5ca06f716bb195e2dfc99b3be0ee0 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 27 Jul 2020 18:56:04 +0200 Subject: [PATCH 041/813] added is_group feature --- pype/tools/config_setting/widgets/base.py | 6 +- pype/tools/config_setting/widgets/inputs.py | 160 ++++++++++++++++++-- 2 files changed, 150 insertions(+), 16 deletions(-) diff --git a/pype/tools/config_setting/widgets/base.py b/pype/tools/config_setting/widgets/base.py index b68ec4126a..222cf2ba41 100644 --- a/pype/tools/config_setting/widgets/base.py +++ b/pype/tools/config_setting/widgets/base.py @@ -50,6 +50,8 @@ class StudioWidget(QtWidgets.QWidget, PypeConfigurationWidget): "config_gui_schema" ) is_overidable = False + is_group = False + any_parent_is_group = False def __init__(self, parent=None): super(StudioWidget, self).__init__(parent) @@ -124,7 +126,7 @@ class StudioWidget(QtWidgets.QWidget, PypeConfigurationWidget): while not process_queue.empty(): path, key, metadata, values = process_queue.get() new_path = os.path.join(path, key) - # TODO this should not be + # TODO this should not be if key in metadata: key_metadata = metadata[key] @@ -256,6 +258,8 @@ class ProjectWidget(QtWidgets.QWidget, PypeConfigurationWidget): "config_gui_schema" ) is_overidable = True + is_group = False + any_parent_is_group = False def __init__(self, parent=None): super(ProjectWidget, self).__init__(parent) diff --git a/pype/tools/config_setting/widgets/inputs.py b/pype/tools/config_setting/widgets/inputs.py index 01c69c1c0b..485eda6d35 100644 --- a/pype/tools/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/widgets/inputs.py @@ -11,6 +11,14 @@ from .widgets import ( from .lib import NOT_SET, AS_WIDGET +class SchemeGroupHierarchyBug(Exception): + def __init__(self, msg=None): + if not msg: + # TODO better message + msg = "SCHEME BUG: Attribute `is_group` is mixed in the hierarchy" + super(SchemeGroupHierarchyBug, self).__init(msg) + + class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): value_changed = QtCore.Signal() @@ -20,8 +28,19 @@ class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): self._as_widget = values is AS_WIDGET self._parent = parent + any_parent_is_group = parent.is_group + if not any_parent_is_group: + any_parent_is_group = parent.any_parent_is_group + + is_group = input_data.get("is_group", False) + if is_group and any_parent_is_group: + raise SchemeGroupHierarchyBug() + + if not any_parent_is_group and not is_group: + is_group = True + + self.is_group = is_group self.is_modified = False - self.is_group = False self.is_overriden = False super(BooleanWidget, self).__init__(parent) @@ -122,8 +141,19 @@ class IntegerWidget(QtWidgets.QWidget, PypeConfigurationWidget): self._parent = parent self._as_widget = values is AS_WIDGET + any_parent_is_group = parent.is_group + if not any_parent_is_group: + any_parent_is_group = parent.any_parent_is_group + + is_group = input_data.get("is_group", False) + if is_group and any_parent_is_group: + raise SchemeGroupHierarchyBug() + + if not any_parent_is_group and not is_group: + is_group = True + + self.is_group = is_group self.is_modified = False - self.is_group = False self.is_overriden = False super(IntegerWidget, self).__init__(parent) @@ -225,8 +255,19 @@ class FloatWidget(QtWidgets.QWidget, PypeConfigurationWidget): self._parent = parent self._as_widget = values is AS_WIDGET + any_parent_is_group = parent.is_group + if not any_parent_is_group: + any_parent_is_group = parent.any_parent_is_group + + is_group = input_data.get("is_group", False) + if is_group and any_parent_is_group: + raise SchemeGroupHierarchyBug() + + if not any_parent_is_group and not is_group: + is_group = True + + self.is_group = is_group self.is_modified = False - self.is_group = False self.is_overriden = False super(FloatWidget, self).__init__(parent) @@ -338,8 +379,19 @@ class TextSingleLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): self._parent = parent self._as_widget = values is AS_WIDGET + any_parent_is_group = parent.is_group + if not any_parent_is_group: + any_parent_is_group = parent.any_parent_is_group + + is_group = input_data.get("is_group", False) + if is_group and any_parent_is_group: + raise SchemeGroupHierarchyBug() + + if not any_parent_is_group and not is_group: + is_group = True + + self.is_group = is_group self.is_modified = False - self.is_group = False self.is_overriden = False super(TextSingleLineWidget, self).__init__(parent) @@ -440,8 +492,19 @@ class TextMultiLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): ): self._parent = parent + any_parent_is_group = parent.is_group + if not any_parent_is_group: + any_parent_is_group = parent.any_parent_is_group + + is_group = input_data.get("is_group", False) + if is_group and any_parent_is_group: + raise SchemeGroupHierarchyBug() + + if not any_parent_is_group and not is_group: + is_group = True + + self.is_group = is_group self.is_modified = False - self.is_group = False self.is_overriden = False super(TextMultiLineWidget, self).__init__(parent) @@ -699,8 +762,16 @@ class TextListWidget(QtWidgets.QWidget, PypeConfigurationWidget): ): self._parent = parent + any_parent_is_group = parent.is_group + if not any_parent_is_group: + any_parent_is_group = parent.any_parent_is_group + + is_group = input_data.get("is_group", False) + if is_group and any_parent_is_group: + raise SchemeGroupHierarchyBug() + self.is_modified = False - self.is_group = False + self.is_group = is_group self.is_overriden = False super(TextListWidget, self).__init__(parent) @@ -747,7 +818,8 @@ class TextListWidget(QtWidgets.QWidget, PypeConfigurationWidget): def _on_value_change(self, value=None): self.is_modified = self.item_value() != self.origin_value - self.is_overriden = True + if self.is_group and self.is_overidable: + self.is_overriden = True self._update_style() @@ -798,9 +870,19 @@ class DictExpandWidget(QtWidgets.QWidget, PypeConfigurationWidget): )) self._parent = parent + any_parent_is_group = parent.is_group + if not any_parent_is_group: + any_parent_is_group = parent.any_parent_is_group + + is_group = input_data.get("is_group", False) + if is_group and any_parent_is_group: + raise SchemeGroupHierarchyBug() + + self.any_parent_is_group = any_parent_is_group + self.is_modified = False - self.is_overriden = False - self.is_group = input_data.get("is_group", False) + self._is_overriden = False + self.is_group = is_group super(DictExpandWidget, self).__init__(parent) self.setObjectName("DictExpandWidget") @@ -850,7 +932,6 @@ class DictExpandWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.top_part.clicked.connect(self._top_part_clicked) self.button_toggle.clicked.connect(self.toggle_content) - self._is_overriden = False self.input_fields = [] self.key = input_data["key"] @@ -881,8 +962,15 @@ class DictExpandWidget(QtWidgets.QWidget, PypeConfigurationWidget): super(DictExpandWidget, self).resizeEvent(event) self.content_widget.updateGeometry() + @property + def is_overriden(self): + if self._is_overriden: + return self._is_overriden + + def _on_value_change(self, value=None): - self.is_overriden = True + if self.is_group: + self._is_overriden = True self.value_changed.emit() @@ -966,9 +1054,19 @@ class DictInvisible(QtWidgets.QWidget, PypeConfigurationWidget): ): self._parent = parent + any_parent_is_group = parent.is_group + if not any_parent_is_group: + any_parent_is_group = parent.any_parent_is_group + + is_group = input_data.get("is_group", False) + if is_group and any_parent_is_group: + raise SchemeGroupHierarchyBug() + + self.any_parent_is_group = any_parent_is_group + self.is_modified = False self.is_overriden = False - self.is_group = input_data.get("is_group", False) + self.is_group = is_group super(DictInvisible, self).__init__(parent) self.setObjectName("DictInvisible") @@ -979,7 +1077,6 @@ class DictInvisible(QtWidgets.QWidget, PypeConfigurationWidget): layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(5) - self._is_overriden = False self.input_fields = [] if "key" not in input_data: @@ -1047,6 +1144,12 @@ class DictFormWidget(QtWidgets.QWidget): ): self._parent = parent + any_parent_is_group = parent.is_group + if not any_parent_is_group: + any_parent_is_group = parent.any_parent_is_group + + self.any_parent_is_group = any_parent_is_group + self.is_modified = False self.is_overriden = False self.is_group = False @@ -1168,6 +1271,14 @@ class ModifiableDictItem(QtWidgets.QWidget, PypeConfigurationWidget): self._update_style() self.value_changed.emit() + @property + def is_group(self): + return self._parent.is_group + + @property + def any_parent_is_group(self): + return self._parent.any_parent_is_group + @property def is_overidable(self): return self._parent.is_overidable @@ -1253,6 +1364,14 @@ class ModifiableDictSubWidget(QtWidgets.QWidget, PypeConfigurationWidget): def is_overidable(self): return self._parent.is_overidable + @property + def is_group(self): + return self._parent.is_group + + @property + def any_parent_is_group(self): + return self._parent.any_parent_is_group + def _on_value_change(self): self.value_changed.emit() @@ -1324,10 +1443,20 @@ class ModifiableDict(ExpandingWidget, PypeConfigurationWidget): ): self._parent = parent + any_parent_is_group = parent.is_group + if not any_parent_is_group: + any_parent_is_group = parent.any_parent_is_group + + is_group = input_data.get("is_group", False) + if is_group and any_parent_is_group: + raise SchemeGroupHierarchyBug() + + self.any_parent_is_group = any_parent_is_group + self.is_modified = False self.child_modified = False self.is_overriden = False - self.is_group = input_data.get("is_group", False) + self.is_group = is_group super(ModifiableDict, self).__init__(input_data["label"], parent) self.setObjectName("ModifiableDict") @@ -1346,7 +1475,8 @@ class ModifiableDict(ExpandingWidget, PypeConfigurationWidget): def _on_value_change(self, value=None): self.child_modified = self.item_value() != self.origin_value - self.is_overriden = True + if self.is_group: + self.is_overriden = True self._update_style() From 9ef5bad10ea76dfe8beccd0925403715658c779b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 27 Jul 2020 18:57:49 +0200 Subject: [PATCH 042/813] added first testing groups --- .../config_gui_schema/ftrack_projects_gui_schema.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pype/tools/config_setting/config_gui_schema/ftrack_projects_gui_schema.json b/pype/tools/config_setting/config_gui_schema/ftrack_projects_gui_schema.json index febf84eb4a..2e930acca5 100644 --- a/pype/tools/config_setting/config_gui_schema/ftrack_projects_gui_schema.json +++ b/pype/tools/config_setting/config_gui_schema/ftrack_projects_gui_schema.json @@ -7,6 +7,7 @@ "key": "status_update", "type": "dict-expanding", "label": "Status updates", + "is_group": true, "children": [ { "key": "Ready", @@ -18,6 +19,7 @@ "key": "status_version_to_task", "type": "dict-expanding", "label": "Version status to Task status", + "is_group": true, "children": [ { "key": "in progress", From 9ec21131f3e258aea80cb762fc03a56eab0e627b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 28 Jul 2020 10:44:15 +0200 Subject: [PATCH 043/813] just fast commit --- pype/tools/config_setting/widgets/inputs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/tools/config_setting/widgets/inputs.py b/pype/tools/config_setting/widgets/inputs.py index 485eda6d35..c094dcd9db 100644 --- a/pype/tools/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/widgets/inputs.py @@ -966,7 +966,7 @@ class DictExpandWidget(QtWidgets.QWidget, PypeConfigurationWidget): def is_overriden(self): if self._is_overriden: return self._is_overriden - + return self._parent.is_overriden def _on_value_change(self, value=None): if self.is_group: From d234181f93d17de70706b2cd26b1c5fe0ec4a8e8 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 28 Jul 2020 17:08:56 +0200 Subject: [PATCH 044/813] added second item for testing --- .../config_gui_schema/ftrack_projects_gui_schema.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pype/tools/config_setting/config_gui_schema/ftrack_projects_gui_schema.json b/pype/tools/config_setting/config_gui_schema/ftrack_projects_gui_schema.json index 2e930acca5..4a35fc9b61 100644 --- a/pype/tools/config_setting/config_gui_schema/ftrack_projects_gui_schema.json +++ b/pype/tools/config_setting/config_gui_schema/ftrack_projects_gui_schema.json @@ -13,6 +13,10 @@ "key": "Ready", "type": "text-singleline", "label": "Ready" + }, { + "key": "Ready2", + "type": "text-singleline", + "label": "Ready2" } ] }, { From 57a040fadbfbba1e672c3abb7ab044ac0be908aa Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 28 Jul 2020 17:28:14 +0200 Subject: [PATCH 045/813] better overridable control --- pype/tools/config_setting/widgets/base.py | 4 +- pype/tools/config_setting/widgets/inputs.py | 195 +++++++++++++------- 2 files changed, 128 insertions(+), 71 deletions(-) diff --git a/pype/tools/config_setting/widgets/base.py b/pype/tools/config_setting/widgets/base.py index 222cf2ba41..ddeae365d8 100644 --- a/pype/tools/config_setting/widgets/base.py +++ b/pype/tools/config_setting/widgets/base.py @@ -50,6 +50,7 @@ class StudioWidget(QtWidgets.QWidget, PypeConfigurationWidget): "config_gui_schema" ) is_overidable = False + is_overriden = False is_group = False any_parent_is_group = False @@ -257,13 +258,14 @@ class ProjectWidget(QtWidgets.QWidget, PypeConfigurationWidget): os.path.dirname(os.path.dirname(__file__)), "config_gui_schema" ) - is_overidable = True + is_overriden = False is_group = False any_parent_is_group = False def __init__(self, parent=None): super(ProjectWidget, self).__init__(parent) + self.is_overidable = True self.input_fields = [] scroll_widget = QtWidgets.QScrollArea(self) diff --git a/pype/tools/config_setting/widgets/inputs.py b/pype/tools/config_setting/widgets/inputs.py index c094dcd9db..2b1b5b3af6 100644 --- a/pype/tools/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/widgets/inputs.py @@ -41,7 +41,7 @@ class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.is_group = is_group self.is_modified = False - self.is_overriden = False + self._is_overriden = False super(BooleanWidget, self).__init__(parent) @@ -92,26 +92,33 @@ class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): @property def child_overriden(self): - return self.is_overriden + return self._is_overriden @property def is_overidable(self): return self._parent.is_overidable + @property + def is_overriden(self): + if self._is_overriden: + return self._is_overriden + return self._parent.is_overriden + def _on_value_change(self, value=None): self.is_modified = self.item_value() != self.origin_value - self.is_overriden = True + if self.is_overidable: + self._is_overriden = True self._update_style() self.value_changed.emit() def _update_style(self): - if self.is_overidable and self.is_overriden: - if self.is_modified: - state = "overriden-modified" - else: - state = "overriden" + is_overriden = self.is_overriden + if is_overriden and self.is_modified: + state = "overriden-modified" + elif is_overriden: + state = "overriden" elif self.is_modified: state = "modified" else: @@ -154,7 +161,7 @@ class IntegerWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.is_group = is_group self.is_modified = False - self.is_overriden = False + self._is_overriden = False super(IntegerWidget, self).__init__(parent) @@ -192,12 +199,18 @@ class IntegerWidget(QtWidgets.QWidget, PypeConfigurationWidget): @property def child_overriden(self): - return self.is_overriden + return self._is_overriden @property def is_overidable(self): return self._parent.is_overidable + @property + def is_overriden(self): + if self._is_overriden: + return self._is_overriden + return self._parent.is_overriden + def set_value(self, value, origin_value=False): self.int_input.setValue(value) if origin_value: @@ -212,18 +225,19 @@ class IntegerWidget(QtWidgets.QWidget, PypeConfigurationWidget): def _on_value_change(self, value=None): self.is_modified = self.item_value() != self.origin_value - self.is_overriden = True + if self.is_overidable: + self._is_overriden = True self._update_style() self.value_changed.emit() def _update_style(self): - if self.is_overidable and self.is_overriden: - if self.is_modified: - state = "overriden-modified" - else: - state = "overriden" + is_overriden = self.is_overriden + if is_overriden and self.is_modified: + state = "overriden-modified" + elif is_overriden: + state = "overriden" elif self.is_modified: state = "modified" else: @@ -268,7 +282,7 @@ class FloatWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.is_group = is_group self.is_modified = False - self.is_overriden = False + self._is_overriden = False super(FloatWidget, self).__init__(parent) @@ -316,12 +330,18 @@ class FloatWidget(QtWidgets.QWidget, PypeConfigurationWidget): @property def child_overriden(self): - return self.is_overriden + return self._is_overriden @property def is_overidable(self): return self._parent.is_overidable + @property + def is_overriden(self): + if self._is_overriden: + return self._is_overriden + return self._parent.is_overriden + def set_value(self, value, origin_value=False): self.float_input.setValue(value) if origin_value: @@ -336,18 +356,19 @@ class FloatWidget(QtWidgets.QWidget, PypeConfigurationWidget): def _on_value_change(self, value=None): self.is_modified = self.item_value() != self.origin_value - self.is_overriden = True + if self.is_overidable: + self._is_overriden = True self._update_style() self.value_changed.emit() def _update_style(self): - if self.is_overidable and self.is_overriden: - if self.is_modified: - state = "overriden-modified" - else: - state = "overriden" + is_overriden = self.is_overriden + if is_overriden and self.is_modified: + state = "overriden-modified" + elif is_overriden: + state = "overriden" elif self.is_modified: state = "modified" else: @@ -392,7 +413,7 @@ class TextSingleLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.is_group = is_group self.is_modified = False - self.is_overriden = False + self._is_overriden = False super(TextSingleLineWidget, self).__init__(parent) @@ -430,12 +451,18 @@ class TextSingleLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): @property def child_overriden(self): - return self.is_overriden + return self._is_overriden @property def is_overidable(self): return self._parent.is_overidable + @property + def is_overriden(self): + if self._is_overriden: + return self._is_overriden + return self._parent.is_overriden + def set_value(self, value, origin_value=False): self.text_input.setText(value) if origin_value: @@ -450,18 +477,19 @@ class TextSingleLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): def _on_value_change(self, value=None): self.is_modified = self.item_value() != self.origin_value - self.is_overriden = True + if self.is_overidable: + self._is_overriden = True self._update_style() self.value_changed.emit() def _update_style(self): - if self.is_overidable and self.is_overriden: - if self.is_modified: - state = "overriden-modified" - else: - state = "overriden" + is_overriden = self.is_overriden + if is_overriden and self.is_modified: + state = "overriden-modified" + elif is_overriden: + state = "overriden" elif self.is_modified: state = "modified" else: @@ -505,7 +533,7 @@ class TextMultiLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.is_group = is_group self.is_modified = False - self.is_overriden = False + self._is_overriden = False super(TextMultiLineWidget, self).__init__(parent) @@ -541,12 +569,18 @@ class TextMultiLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): @property def child_overriden(self): - return self.is_overriden + return self._is_overriden @property def is_overidable(self): return self._parent.is_overidable + @property + def is_overriden(self): + if self._is_overriden: + return self._is_overriden + return self._parent.is_overriden + def set_value(self, value, origin_value=False): self.text_input.setPlainText(value) if origin_value: @@ -561,18 +595,19 @@ class TextMultiLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): def _on_value_change(self, value=None): self.is_modified = self.item_value() != self.origin_value - self.is_overriden = True + if self.is_overidable: + self._is_overriden = True self._update_style() self.value_changed.emit() def _update_style(self): - if self.is_overidable and self.is_overriden: - if self.is_modified: - state = "overriden-modified" - else: - state = "overriden" + is_overriden = self.is_overriden + if is_overriden and self.is_modified: + state = "overriden-modified" + elif is_overriden: + state = "overriden" elif self.is_modified: state = "modified" else: @@ -770,9 +805,12 @@ class TextListWidget(QtWidgets.QWidget, PypeConfigurationWidget): if is_group and any_parent_is_group: raise SchemeGroupHierarchyBug() + if not any_parent_is_group and not is_group: + is_group = True + self.is_modified = False self.is_group = is_group - self.is_overriden = False + self._is_overriden = False super(TextListWidget, self).__init__(parent) self.setObjectName("TextListWidget") @@ -810,16 +848,22 @@ class TextListWidget(QtWidgets.QWidget, PypeConfigurationWidget): @property def child_overriden(self): - return self.is_overriden + return self._is_overriden @property def is_overidable(self): return self._parent.is_overidable + @property + def is_overriden(self): + if self._is_overriden: + return self._is_overriden + return self._parent.is_overriden + def _on_value_change(self, value=None): self.is_modified = self.item_value() != self.origin_value - if self.is_group and self.is_overidable: - self.is_overriden = True + if self.is_overidable: + self._is_overriden = True self._update_style() @@ -838,11 +882,11 @@ class TextListWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.set_value([]) def _update_style(self): - if self.is_overidable and self.is_overriden: - if self.is_modified: - state = "overriden-modified" - else: - state = "overriden" + is_overriden = self.is_overriden + if is_overriden and self.is_modified: + state = "overriden-modified" + elif is_overriden: + state = "overriden" elif self.is_modified: state = "modified" else: @@ -969,7 +1013,7 @@ class DictExpandWidget(QtWidgets.QWidget, PypeConfigurationWidget): return self._parent.is_overriden def _on_value_change(self, value=None): - if self.is_group: + if self.is_group and self.is_overidable: self._is_overriden = True self.value_changed.emit() @@ -992,13 +1036,10 @@ class DictExpandWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.setProperty("state", widget_state) self.style().polish(self) - if self.is_overidable and self.is_overriden: - if self.is_modified: - state = "overriden-modified" - else: - state = "overriden" - elif self.is_modified: - state = "modified" + if child_modified and self.is_overriden: + state = "overriden-modified" + elif self.is_overriden: + state = "overriden" else: state = "original" @@ -1065,7 +1106,6 @@ class DictInvisible(QtWidgets.QWidget, PypeConfigurationWidget): self.any_parent_is_group = any_parent_is_group self.is_modified = False - self.is_overriden = False self.is_group = is_group super(DictInvisible, self).__init__(parent) @@ -1089,6 +1129,10 @@ class DictInvisible(QtWidgets.QWidget, PypeConfigurationWidget): for child_data in input_data.get("children", []): self.add_children_gui(child_data, values) + @property + def is_overriden(self): + return self._parent.is_overriden + @property def is_overidable(self): return self._parent.is_overidable @@ -1184,7 +1228,7 @@ class DictFormWidget(QtWidgets.QWidget): @property def child_overriden(self): - for input_field in self.input_fields: + for input_field in self.input_fields.values(): if input_field.child_overriden: return True return False @@ -1283,6 +1327,10 @@ class ModifiableDictItem(QtWidgets.QWidget, PypeConfigurationWidget): def is_overidable(self): return self._parent.is_overidable + @property + def is_overriden(self): + return self._parent.is_overriden + def is_key_modified(self): return self._key() != self.origin_key @@ -1364,6 +1412,10 @@ class ModifiableDictSubWidget(QtWidgets.QWidget, PypeConfigurationWidget): def is_overidable(self): return self._parent.is_overidable + @property + def is_overriden(self): + return self._parent.is_overriden + @property def is_group(self): return self._parent.is_group @@ -1455,7 +1507,7 @@ class ModifiableDict(ExpandingWidget, PypeConfigurationWidget): self.is_modified = False self.child_modified = False - self.is_overriden = False + self._is_overriden = False self.is_group = is_group super(ModifiableDict, self).__init__(input_data["label"], parent) @@ -1475,19 +1527,25 @@ class ModifiableDict(ExpandingWidget, PypeConfigurationWidget): def _on_value_change(self, value=None): self.child_modified = self.item_value() != self.origin_value - if self.is_group: - self.is_overriden = True + if self.is_group and self.is_overidable: + self._is_overriden = True self._update_style() @property def child_overriden(self): - return self.is_overriden + return self._is_overriden @property def is_overidable(self): return self._parent.is_overidable + @property + def is_overriden(self): + if self._is_overriden: + return self._is_overriden + return self._parent.is_overriden + def _update_style(self): child_modified = self.child_modified child_overriden = self.child_overriden @@ -1504,13 +1562,10 @@ class ModifiableDict(ExpandingWidget, PypeConfigurationWidget): self.setProperty("state", widget_state) self.style().polish(self) - if self.is_overidable and self.is_overriden: - if self.is_modified: - state = "overriden-modified" - else: - state = "overriden" - elif self.is_modified: - state = "modified" + if child_modified and self.is_overriden: + state = "overriden-modified" + elif self.is_overriden: + state = "overriden" else: state = "original" From f7323144e0073b2201aadaf02627390b28354db2 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 28 Jul 2020 18:54:22 +0200 Subject: [PATCH 046/813] working overriden workflow --- pype/tools/config_setting/style/style.css | 6 - pype/tools/config_setting/widgets/base.py | 10 + pype/tools/config_setting/widgets/inputs.py | 313 ++++++++++---------- 3 files changed, 171 insertions(+), 158 deletions(-) diff --git a/pype/tools/config_setting/style/style.css b/pype/tools/config_setting/style/style.css index 1371056c1a..aabffc3f84 100644 --- a/pype/tools/config_setting/style/style.css +++ b/pype/tools/config_setting/style/style.css @@ -19,8 +19,6 @@ QLineEdit:focus, QSpinBox:focus, QDoubleSpinBox:focus, QPlainTextEdit:focus { border: 1px solid #ffffff; } -QLabel[state="original"] {} - QLabel[state="modified"] { color: #137cbd; } @@ -33,8 +31,6 @@ QLabel[state="overriden"] { color: #ff8c1a; } -QWidget[input-state="original"] {} - QWidget[input-state="modified"] { border-color: #137cbd; } @@ -61,8 +57,6 @@ QPushButton[btn-type="expand-toggle"] { background: transparent; } -#DictKey[state="original"] {} - #DictKey[state="modified"] { border-color: #137cbd; } diff --git a/pype/tools/config_setting/widgets/base.py b/pype/tools/config_setting/widgets/base.py index ddeae365d8..e01f14aa70 100644 --- a/pype/tools/config_setting/widgets/base.py +++ b/pype/tools/config_setting/widgets/base.py @@ -12,6 +12,8 @@ class TypeToKlass: class PypeConfigurationWidget: + default_state = "" + def config_value(self): raise NotImplementedError( "Method `config_value` is not implemented for `{}`.".format( @@ -38,6 +40,14 @@ class PypeConfigurationWidget: value = value[key] return value + def style_state(self, is_overriden, is_modified): + items = [] + if is_overriden: + items.append("overriden") + if is_modified: + items.append("modified") + return "-".join(items) or self.default_state + def add_children_gui(self, child_configuration, values): raise NotImplementedError(( "Method `add_children_gui` is not implemented for `{}`." diff --git a/pype/tools/config_setting/widgets/inputs.py b/pype/tools/config_setting/widgets/inputs.py index 2b1b5b3af6..e5b929b7ca 100644 --- a/pype/tools/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/widgets/inputs.py @@ -20,7 +20,7 @@ class SchemeGroupHierarchyBug(Exception): class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): - value_changed = QtCore.Signal() + value_changed = QtCore.Signal(object) def __init__( self, input_data, values, parent_keys, parent, label_widget=None @@ -43,6 +43,8 @@ class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.is_modified = False self._is_overriden = False + self._state = None + super(BooleanWidget, self).__init__(parent) layout = QtWidgets.QVBoxLayout(self) @@ -104,25 +106,23 @@ class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): return self._is_overriden return self._parent.is_overriden - def _on_value_change(self, value=None): + def _on_value_change(self, item=None): self.is_modified = self.item_value() != self.origin_value if self.is_overidable: self._is_overriden = True - self._update_style() + self.update_style() - self.value_changed.emit() + self.value_changed.emit(self) - def _update_style(self): - is_overriden = self.is_overriden - if is_overriden and self.is_modified: - state = "overriden-modified" - elif is_overriden: - state = "overriden" - elif self.is_modified: - state = "modified" - else: - state = "original" + def update_style(self, is_overriden=None): + if is_overriden is None: + is_overriden = self.is_overriden + is_modified = self.is_modified + + state = self.style_state(is_overriden, is_modified) + if self._state == state: + return if self._as_widget: property_name = "input-state" @@ -131,6 +131,7 @@ class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.label_widget.setProperty(property_name, state) self.label_widget.style().polish(self.label_widget) + self._state = state def item_value(self): return self.checkbox.isChecked() @@ -140,7 +141,7 @@ class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): class IntegerWidget(QtWidgets.QWidget, PypeConfigurationWidget): - value_changed = QtCore.Signal() + value_changed = QtCore.Signal(object) def __init__( self, input_data, values, parent_keys, parent, label_widget=None @@ -163,6 +164,8 @@ class IntegerWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.is_modified = False self._is_overriden = False + self._state = None + super(IntegerWidget, self).__init__(parent) layout = QtWidgets.QVBoxLayout(self) @@ -223,25 +226,23 @@ class IntegerWidget(QtWidgets.QWidget, PypeConfigurationWidget): def reset_value(self): self.set_value(self.origin_value) - def _on_value_change(self, value=None): + def _on_value_change(self, item=None): self.is_modified = self.item_value() != self.origin_value if self.is_overidable: self._is_overriden = True - self._update_style() + self.update_style() - self.value_changed.emit() + self.value_changed.emit(self) - def _update_style(self): - is_overriden = self.is_overriden - if is_overriden and self.is_modified: - state = "overriden-modified" - elif is_overriden: - state = "overriden" - elif self.is_modified: - state = "modified" - else: - state = "original" + def update_style(self, is_overriden=None): + if is_overriden is None: + is_overriden = self.is_overriden + is_modified = self.is_modified + + state = self.style_state(is_overriden, is_modified) + if self._state == state: + return if self._as_widget: property_name = "input-state" @@ -261,7 +262,7 @@ class IntegerWidget(QtWidgets.QWidget, PypeConfigurationWidget): class FloatWidget(QtWidgets.QWidget, PypeConfigurationWidget): - value_changed = QtCore.Signal() + value_changed = QtCore.Signal(object) def __init__( self, input_data, values, parent_keys, parent, label_widget=None @@ -284,6 +285,8 @@ class FloatWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.is_modified = False self._is_overriden = False + self._state = None + super(FloatWidget, self).__init__(parent) layout = QtWidgets.QVBoxLayout(self) @@ -354,25 +357,23 @@ class FloatWidget(QtWidgets.QWidget, PypeConfigurationWidget): def clear_value(self): self.set_value(0) - def _on_value_change(self, value=None): + def _on_value_change(self, item=None): self.is_modified = self.item_value() != self.origin_value if self.is_overidable: self._is_overriden = True - self._update_style() + self.update_style() - self.value_changed.emit() + self.value_changed.emit(self) - def _update_style(self): - is_overriden = self.is_overriden - if is_overriden and self.is_modified: - state = "overriden-modified" - elif is_overriden: - state = "overriden" - elif self.is_modified: - state = "modified" - else: - state = "original" + def update_style(self, is_overriden=None): + if is_overriden is None: + is_overriden = self.is_overriden + is_modified = self.is_modified + + state = self.style_state(is_overriden, is_modified) + if self._state == state: + return if self._as_widget: property_name = "input-state" @@ -392,7 +393,7 @@ class FloatWidget(QtWidgets.QWidget, PypeConfigurationWidget): class TextSingleLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): - value_changed = QtCore.Signal() + value_changed = QtCore.Signal(object) def __init__( self, input_data, values, parent_keys, parent, label_widget=None @@ -415,6 +416,8 @@ class TextSingleLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.is_modified = False self._is_overriden = False + self._state = None + super(TextSingleLineWidget, self).__init__(parent) layout = QtWidgets.QVBoxLayout(self) @@ -475,25 +478,23 @@ class TextSingleLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): def clear_value(self): self.set_value("") - def _on_value_change(self, value=None): + def _on_value_change(self, item=None): self.is_modified = self.item_value() != self.origin_value if self.is_overidable: self._is_overriden = True - self._update_style() + self.update_style() - self.value_changed.emit() + self.value_changed.emit(self) - def _update_style(self): - is_overriden = self.is_overriden - if is_overriden and self.is_modified: - state = "overriden-modified" - elif is_overriden: - state = "overriden" - elif self.is_modified: - state = "modified" - else: - state = "original" + def update_style(self, is_overriden=None): + if is_overriden is None: + is_overriden = self.is_overriden + is_modified = self.is_modified + + state = self.style_state(is_overriden, is_modified) + if self._state == state: + return if self._as_widget: property_name = "input-state" @@ -513,7 +514,7 @@ class TextSingleLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): class TextMultiLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): - value_changed = QtCore.Signal() + value_changed = QtCore.Signal(object) def __init__( self, input_data, values, parent_keys, parent, label_widget=None @@ -535,6 +536,8 @@ class TextMultiLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.is_modified = False self._is_overriden = False + self._state = None + super(TextMultiLineWidget, self).__init__(parent) layout = QtWidgets.QVBoxLayout(self) @@ -593,25 +596,23 @@ class TextMultiLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): def clear_value(self): self.set_value("") - def _on_value_change(self, value=None): + def _on_value_change(self, item=None): self.is_modified = self.item_value() != self.origin_value if self.is_overidable: self._is_overriden = True - self._update_style() + self.update_style() - self.value_changed.emit() + self.value_changed.emit(self) - def _update_style(self): - is_overriden = self.is_overriden - if is_overriden and self.is_modified: - state = "overriden-modified" - elif is_overriden: - state = "overriden" - elif self.is_modified: - state = "modified" - else: - state = "original" + def update_style(self, is_overriden=None): + if is_overriden is None: + is_overriden = self.is_overriden + is_modified = self.is_modified + + state = self.style_state(is_overriden, is_modified) + if self._state == state: + return if self._as_widget: property_name = "input-state" @@ -632,7 +633,7 @@ class TextMultiLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): class TextListItem(QtWidgets.QWidget, PypeConfigurationWidget): _btn_size = 20 - value_changed = QtCore.Signal() + value_changed = QtCore.Signal(object) def __init__(self, parent): super(TextListItem, self).__init__(parent) @@ -661,8 +662,8 @@ class TextListItem(QtWidgets.QWidget, PypeConfigurationWidget): self.is_single = False - def _on_value_change(self): - self.value_changed.emit() + def _on_value_change(self, item=None): + self.value_changed.emit(self) def row(self): return self.parent().input_fields.index(self) @@ -681,7 +682,7 @@ class TextListItem(QtWidgets.QWidget, PypeConfigurationWidget): class TextListSubWidget(QtWidgets.QWidget, PypeConfigurationWidget): - value_changed = QtCore.Signal() + value_changed = QtCore.Signal(object) def __init__(self, input_data, values, parent_keys, parent): super(TextListSubWidget, self).__init__(parent) @@ -723,8 +724,8 @@ class TextListSubWidget(QtWidgets.QWidget, PypeConfigurationWidget): def clear_value(self): self.set_value([]) - def _on_value_change(self): - self.value_changed.emit() + def _on_value_change(self, item=None): + self.value_changed.emit(self) def count(self): return len(self.input_fields) @@ -790,7 +791,7 @@ class TextListSubWidget(QtWidgets.QWidget, PypeConfigurationWidget): class TextListWidget(QtWidgets.QWidget, PypeConfigurationWidget): - value_changed = QtCore.Signal() + value_changed = QtCore.Signal(object) def __init__( self, input_data, values, parent_keys, parent, label_widget=None @@ -812,6 +813,8 @@ class TextListWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.is_group = is_group self._is_overriden = False + self._state = None + super(TextListWidget, self).__init__(parent) self.setObjectName("TextListWidget") @@ -860,14 +863,14 @@ class TextListWidget(QtWidgets.QWidget, PypeConfigurationWidget): return self._is_overriden return self._parent.is_overriden - def _on_value_change(self, value=None): + def _on_value_change(self, item=None): self.is_modified = self.item_value() != self.origin_value if self.is_overidable: self._is_overriden = True - self._update_style() + self.update_style() - self.value_changed.emit() + self.value_changed.emit(self) def set_value(self, value, origin_value=False): self.value_widget.set_value(value) @@ -881,16 +884,14 @@ class TextListWidget(QtWidgets.QWidget, PypeConfigurationWidget): def clear_value(self): self.set_value([]) - def _update_style(self): - is_overriden = self.is_overriden - if is_overriden and self.is_modified: - state = "overriden-modified" - elif is_overriden: - state = "overriden" - elif self.is_modified: - state = "modified" - else: - state = "original" + def update_style(self, is_overriden=None): + if is_overriden is None: + is_overriden = self.is_overriden + is_modified = self.is_modified + + state = self.style_state(is_overriden, is_modified) + if self._state == state: + return self.label_widget.setProperty("state", state) self.label_widget.style().polish(self.label_widget) @@ -903,7 +904,7 @@ class TextListWidget(QtWidgets.QWidget, PypeConfigurationWidget): class DictExpandWidget(QtWidgets.QWidget, PypeConfigurationWidget): - value_changed = QtCore.Signal() + value_changed = QtCore.Signal(object) def __init__( self, input_data, values, parent_keys, parent, label_widget=None @@ -928,6 +929,9 @@ class DictExpandWidget(QtWidgets.QWidget, PypeConfigurationWidget): self._is_overriden = False self.is_group = is_group + self._state = None + self._child_state = None + super(DictExpandWidget, self).__init__(parent) self.setObjectName("DictExpandWidget") top_part = ClickableWidget(parent=self) @@ -1012,40 +1016,50 @@ class DictExpandWidget(QtWidgets.QWidget, PypeConfigurationWidget): return self._is_overriden return self._parent.is_overriden - def _on_value_change(self, value=None): - if self.is_group and self.is_overidable: - self._is_overriden = True + def _on_value_change(self, item=None): + if self.is_group: + if self.is_overidable: + self._is_overriden = True + # TODO update items + if item is not None: + is_overriden = self.is_overriden + for _item in self.input_fields: + if _item is not item: + _item.update_style(is_overriden) - self.value_changed.emit() + self.value_changed.emit(self) - self._update_style() + self.update_style() - def _update_style(self): + def update_style(self, is_overriden=None): child_modified = self.child_modified - child_overriden = self.child_overriden + if is_overriden is None: + child_overriden = self.child_overriden + child_state = self.style_state(child_overriden, child_modified) + if child_state: + child_state = "child-{}".format(child_state) - if child_modified and child_overriden: - widget_state = "child-overriden-modified" - elif child_modified: - widget_state = "child-modified" - elif child_overriden: - widget_state = "child-overriden" + if child_state != self._child_state: + self.setProperty("state", child_state) + self.style().polish(self) + self._child_state = child_state + + if is_overriden is None: + is_overriden = self.is_overriden + + if child_modified and not is_overriden: + state = self.default_state else: - widget_state = "" + state = self.style_state(is_overriden, child_modified) - self.setProperty("state", widget_state) - self.style().polish(self) - - if child_modified and self.is_overriden: - state = "overriden-modified" - elif self.is_overriden: - state = "overriden" - else: - state = "original" + if self._state == state: + return self.button_toggle_text.setProperty("state", state) self.button_toggle_text.style().polish(self.button_toggle_text) + self._state = state + @property def child_modified(self): for input_field in self.input_fields: @@ -1181,7 +1195,7 @@ class DictInvisible(QtWidgets.QWidget, PypeConfigurationWidget): class DictFormWidget(QtWidgets.QWidget): - value_changed = QtCore.Signal() + value_changed = QtCore.Signal(object) def __init__( self, input_data, values, parent_keys, parent, label_widget=None @@ -1208,8 +1222,8 @@ class DictFormWidget(QtWidgets.QWidget): for child_data in input_data.get("children", []): self.add_children_gui(child_data, values) - def _on_value_change(self): - self.value_changed.emit() + def _on_value_change(self, item=None): + self.value_changed.emit(self) def item_value(self): output = {} @@ -1261,7 +1275,7 @@ class DictFormWidget(QtWidgets.QWidget): class ModifiableDictItem(QtWidgets.QWidget, PypeConfigurationWidget): _btn_size = 20 - value_changed = QtCore.Signal() + value_changed = QtCore.Signal(object) def __init__(self, object_type, parent): self._parent = parent @@ -1311,9 +1325,9 @@ class ModifiableDictItem(QtWidgets.QWidget, PypeConfigurationWidget): def _key(self): return self.key_input.text() - def _on_value_change(self): - self._update_style() - self.value_changed.emit() + def _on_value_change(self, item=None): + self.update_style() + self.value_changed.emit(self) @property def is_group(self): @@ -1341,16 +1355,11 @@ class ModifiableDictItem(QtWidgets.QWidget, PypeConfigurationWidget): def is_modified(self): return self.is_value_modified() or self.is_key_modified() - def _update_style(self): - # if self._is_overidable and self.is_overriden: - # if is_modified: - # state = "overriden-modified" - # else: - # state = "overriden" + def update_style(self, is_overriden=None): if self.is_key_modified(): state = "modified" else: - state = "original" + state = "" self.key_input.setProperty("state", state) self.key_input.style().polish(self.key_input) @@ -1377,7 +1386,7 @@ class ModifiableDictItem(QtWidgets.QWidget, PypeConfigurationWidget): class ModifiableDictSubWidget(QtWidgets.QWidget, PypeConfigurationWidget): - value_changed = QtCore.Signal() + value_changed = QtCore.Signal(object) def __init__(self, input_data, values, parent_keys, parent): self._parent = parent @@ -1424,8 +1433,8 @@ class ModifiableDictSubWidget(QtWidgets.QWidget, PypeConfigurationWidget): def any_parent_is_group(self): return self._parent.any_parent_is_group - def _on_value_change(self): - self.value_changed.emit() + def _on_value_change(self, item=None): + self.value_changed.emit(self) def count(self): return len(self.input_fields) @@ -1503,6 +1512,9 @@ class ModifiableDict(ExpandingWidget, PypeConfigurationWidget): if is_group and any_parent_is_group: raise SchemeGroupHierarchyBug() + if not any_parent_is_group and not is_group: + is_group = True + self.any_parent_is_group = any_parent_is_group self.is_modified = False @@ -1525,12 +1537,12 @@ class ModifiableDict(ExpandingWidget, PypeConfigurationWidget): self.origin_value = self.item_value() - def _on_value_change(self, value=None): + def _on_value_change(self, item=None): self.child_modified = self.item_value() != self.origin_value if self.is_group and self.is_overidable: self._is_overriden = True - self._update_style() + self.update_style() @property def child_overriden(self): @@ -1546,32 +1558,29 @@ class ModifiableDict(ExpandingWidget, PypeConfigurationWidget): return self._is_overriden return self._parent.is_overriden - def _update_style(self): + def update_style(self, is_overriden=None): child_modified = self.child_modified - child_overriden = self.child_overriden + if is_overriden is None: + child_overriden = self.child_overriden + child_state = self.style_state(child_overriden, child_modified) + if child_state != self._child_state: + self.setProperty("state", child_state) + self.style().polish(self) + self._child_state = child_state - if child_modified and child_overriden: - widget_state = "child-overriden-modified" - elif child_modified: - widget_state = "child-modified" - elif child_overriden: - widget_state = "child-overriden" + if is_overriden is None: + is_overriden = self.is_overriden + + if child_modified and not is_overriden: + state = self.default_state else: - widget_state = "" - - self.setProperty("state", widget_state) - self.style().polish(self) - - if child_modified and self.is_overriden: - state = "overriden-modified" - elif self.is_overriden: - state = "overriden" - else: - state = "original" + state = self.style_state(self.is_overriden, child_modified) self.label_widget.setProperty("state", state) self.label_widget.style().polish(self.label_widget) + self._state = state + def item_value(self): return self.value_widget.config_value() From ec7bbcf19e4fe59437f8daee3ba3c591e25ab6d0 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 29 Jul 2020 15:55:22 +0200 Subject: [PATCH 047/813] small bugfixes and added rawjson widget --- .../config_gui_schema/plugins_gui_schema.json | 24 +++ .../config_gui_schema/project_gui_schema.json | 3 +- pype/tools/config_setting/widgets/base.py | 57 ++++--- pype/tools/config_setting/widgets/inputs.py | 139 +++++++++++++++++- 4 files changed, 198 insertions(+), 25 deletions(-) create mode 100644 pype/tools/config_setting/config_gui_schema/plugins_gui_schema.json diff --git a/pype/tools/config_setting/config_gui_schema/plugins_gui_schema.json b/pype/tools/config_setting/config_gui_schema/plugins_gui_schema.json new file mode 100644 index 0000000000..79c1f85b85 --- /dev/null +++ b/pype/tools/config_setting/config_gui_schema/plugins_gui_schema.json @@ -0,0 +1,24 @@ +{ + "key": "plugins", + "type": "dict-expanding", + "label": "Plugins", + "children": [ + { + "key": "maya", + "type": "dict-expanding", + "label": "Maya", + "is_group": true, + "children": [ + { + "key": "test1", + "type": "raw-json", + "label": "Test1" + }, { + "key": "test2", + "type": "raw-json", + "label": "Test2" + } + ] + } + ] +} diff --git a/pype/tools/config_setting/config_gui_schema/project_gui_schema.json b/pype/tools/config_setting/config_gui_schema/project_gui_schema.json index 38c07ec33d..d2a6221f99 100644 --- a/pype/tools/config_setting/config_gui_schema/project_gui_schema.json +++ b/pype/tools/config_setting/config_gui_schema/project_gui_schema.json @@ -6,7 +6,8 @@ { "type": "schema", "children": [ - "ftrack_projects_gui_schema" + "ftrack_projects_gui_schema", + "plugins_gui_schema" ] } ] diff --git a/pype/tools/config_setting/widgets/base.py b/pype/tools/config_setting/widgets/base.py index e01f14aa70..52184b4c8a 100644 --- a/pype/tools/config_setting/widgets/base.py +++ b/pype/tools/config_setting/widgets/base.py @@ -177,6 +177,7 @@ class ProjectListView(QtWidgets.QListView): class ProjectListWidget(QtWidgets.QWidget): default = "< Default >" + project_changed = QtCore.Signal() def __init__(self, parent): self._parent = parent @@ -218,6 +219,10 @@ class ProjectListWidget(QtWidgets.QWidget): if self.validate_context_change(): self.select_project(new_project_name) self.current_project = new_project_name + self.project_changed.emit() + return + + self.select_project(self.current_project) def validate_context_change(self): # TODO add check if project can be changed (is modified) @@ -275,7 +280,7 @@ class ProjectWidget(QtWidgets.QWidget, PypeConfigurationWidget): def __init__(self, parent=None): super(ProjectWidget, self).__init__(parent) - self.is_overidable = True + self.is_overidable = False self.input_fields = [] scroll_widget = QtWidgets.QScrollArea(self) @@ -292,23 +297,13 @@ class ProjectWidget(QtWidgets.QWidget, PypeConfigurationWidget): project_list_widget = ProjectListWidget(self) content_layout.addWidget(project_list_widget) - self.project_list_widget = project_list_widget - self.scroll_widget = scroll_widget - self.content_layout = content_layout - self.content_widget = content_widget - - values = config.project_presets() - schema = config.gui_schema("project_gui_schema") - self.keys = schema.get("keys", []) - self.add_children_gui(schema, values) - footer_widget = QtWidgets.QWidget() footer_layout = QtWidgets.QHBoxLayout(footer_widget) - btn = QtWidgets.QPushButton("Finish") + save_btn = QtWidgets.QPushButton("Save") spacer_widget = QtWidgets.QWidget() footer_layout.addWidget(spacer_widget, 1) - footer_layout.addWidget(btn, 0) + footer_layout.addWidget(save_btn, 0) presets_widget = QtWidgets.QWidget() presets_layout = QtWidgets.QVBoxLayout(presets_widget) @@ -326,18 +321,18 @@ class ProjectWidget(QtWidgets.QWidget, PypeConfigurationWidget): layout.addWidget(project_list_widget, 0) layout.addWidget(presets_widget, 1) - btn.clicked.connect(self.___finish) + save_btn.clicked.connect(self._save) + project_list_widget.project_changed.connect(self._on_project_change) - def ___finish(self): - output = {} - for item in self.input_fields: - output.update(item.config_value()) + self.project_list_widget = project_list_widget + self.scroll_widget = scroll_widget + self.content_layout = content_layout + self.content_widget = content_widget - for key in reversed(self.keys): - _output = {key: output} - output = _output - - print(json.dumps(output, indent=4)) + values = config.global_project_presets() + schema = config.gui_schema("project_gui_schema") + self.keys = schema.get("keys", []) + self.add_children_gui(schema, values) def add_children_gui(self, child_configuration, values): item_type = child_configuration["type"] @@ -348,3 +343,19 @@ class ProjectWidget(QtWidgets.QWidget, PypeConfigurationWidget): ) self.input_fields.append(item) self.content_layout.addWidget(item) + + def _on_project_change(self): + self.is_overidable = ( + self.project_list_widget.project_name() is not None + ) + + def _save(self): + output = {} + for item in self.input_fields: + output.update(item.config_value()) + + for key in reversed(self.keys): + _output = {key: output} + output = _output + + print(json.dumps(output, indent=4)) diff --git a/pype/tools/config_setting/widgets/inputs.py b/pype/tools/config_setting/widgets/inputs.py index e5b929b7ca..58898aeccb 100644 --- a/pype/tools/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/widgets/inputs.py @@ -1,5 +1,5 @@ import json -from Qt import QtWidgets, QtCore +from Qt import QtWidgets, QtCore, QtGui from . import config from .base import PypeConfigurationWidget, TypeToKlass from .widgets import ( @@ -520,6 +520,7 @@ class TextMultiLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): self, input_data, values, parent_keys, parent, label_widget=None ): self._parent = parent + self._as_widget = values is AS_WIDGET any_parent_is_group = parent.is_group if not any_parent_is_group: @@ -631,6 +632,141 @@ class TextMultiLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): return {self.key: self.item_value()} +class RawJsonWidget(QtWidgets.QWidget, PypeConfigurationWidget): + value_changed = QtCore.Signal(object) + + def __init__( + self, input_data, values, parent_keys, parent, label_widget=None + ): + self._parent = parent + self._as_widget = values is AS_WIDGET + + any_parent_is_group = parent.is_group + if not any_parent_is_group: + any_parent_is_group = parent.any_parent_is_group + + is_group = input_data.get("is_group", False) + if is_group and any_parent_is_group: + raise SchemeGroupHierarchyBug() + + if not any_parent_is_group and not is_group: + is_group = True + + self.is_group = is_group + self.is_modified = False + self._is_overriden = False + + self._state = None + + super(RawJsonWidget, self).__init__(parent) + + layout = QtWidgets.QVBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(0) + + self.text_input = QtWidgets.QPlainTextEdit() + self.text_input.setTabStopDistance( + QtGui.QFontMetricsF( + self.text_input.font() + ).horizontalAdvance(" ") * 4 + ) + if not label_widget: + label = input_data["label"] + label_widget = QtWidgets.QLabel(label) + layout.addWidget(label_widget) + layout.addWidget(self.text_input) + + self.label_widget = label_widget + + self.key = input_data["key"] + keys = list(parent_keys) + keys.append(self.key) + self.keys = keys + + value = self.value_from_values(values) + if value is not NOT_SET: + self.text_input.setPlainText(value) + + self.origin_value = self.item_value() + + self.text_input.textChanged.connect(self._on_value_change) + + @property + def child_modified(self): + return self.is_modified + + @property + def child_overriden(self): + return self._is_overriden + + @property + def is_overidable(self): + return self._parent.is_overidable + + @property + def is_overriden(self): + if self._is_overriden: + return self._is_overriden + return self._parent.is_overriden + + def validate_value(self, value): + try: + json.dumps(value) + return True + except Exception: + return False + + def set_value(self, value, origin_value=False): + is_valid = self.validate_value(value) + if is_valid: + value = json.dumps(value, indent=4) + self.text_input.setPlainText(value) + + if origin_value: + self.origin_value = self.item_value() + self._on_value_change() + + def reset_value(self): + self.set_value(self.origin_value) + + def clear_value(self): + self.set_value("{{}}") + + def _on_value_change(self, item=None): + self.is_modified = self.item_value() != self.origin_value + if self.is_overidable: + self._is_overriden = True + + self.update_style() + + self.value_changed.emit(self) + + def update_style(self, is_overriden=None): + if is_overriden is None: + is_overriden = self.is_overriden + is_modified = self.is_modified + + state = self.style_state(is_overriden, is_modified) + if self._state == state: + return + + if self._as_widget: + property_name = "input-state" + widget = self.text_input + else: + property_name = "state" + widget = self.label_widget + + widget.setProperty(property_name, state) + widget.style().polish(widget) + + def item_value(self): + return self.text_input.toPlainText() + + def config_value(self): + return {self.key: self.item_value()} + + class TextListItem(QtWidgets.QWidget, PypeConfigurationWidget): _btn_size = 20 value_changed = QtCore.Signal(object) @@ -1591,6 +1727,7 @@ class ModifiableDict(ExpandingWidget, PypeConfigurationWidget): TypeToKlass.types["boolean"] = BooleanWidget TypeToKlass.types["text-singleline"] = TextSingleLineWidget TypeToKlass.types["text-multiline"] = TextMultiLineWidget +TypeToKlass.types["raw-json"] = RawJsonWidget TypeToKlass.types["int"] = IntegerWidget TypeToKlass.types["float"] = FloatWidget TypeToKlass.types["dict-expanding"] = DictExpandWidget From 0ab6161193abfc2066edc230d143bb4064a055e6 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 29 Jul 2020 16:07:45 +0200 Subject: [PATCH 048/813] minor changes in raw json --- pype/tools/config_setting/style/style.css | 4 ++++ pype/tools/config_setting/widgets/inputs.py | 20 ++++++++++++++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/pype/tools/config_setting/style/style.css b/pype/tools/config_setting/style/style.css index aabffc3f84..adf8344f89 100644 --- a/pype/tools/config_setting/style/style.css +++ b/pype/tools/config_setting/style/style.css @@ -57,6 +57,10 @@ QPushButton[btn-type="expand-toggle"] { background: transparent; } +#RawJson[state="invalid"] { + border-color: #ff5511; +} + #DictKey[state="modified"] { border-color: #137cbd; } diff --git a/pype/tools/config_setting/widgets/inputs.py b/pype/tools/config_setting/widgets/inputs.py index 58898aeccb..50bd3d58f4 100644 --- a/pype/tools/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/widgets/inputs.py @@ -658,6 +658,8 @@ class RawJsonWidget(QtWidgets.QWidget, PypeConfigurationWidget): self._state = None + self.is_valid = None + super(RawJsonWidget, self).__init__(parent) layout = QtWidgets.QVBoxLayout(self) @@ -665,6 +667,7 @@ class RawJsonWidget(QtWidgets.QWidget, PypeConfigurationWidget): layout.setSpacing(0) self.text_input = QtWidgets.QPlainTextEdit() + self.text_input.setObjectName("RawJson") self.text_input.setTabStopDistance( QtGui.QFontMetricsF( self.text_input.font() @@ -710,6 +713,8 @@ class RawJsonWidget(QtWidgets.QWidget, PypeConfigurationWidget): return self._parent.is_overriden def validate_value(self, value): + if not value: + return True try: json.dumps(value) return True @@ -720,6 +725,7 @@ class RawJsonWidget(QtWidgets.QWidget, PypeConfigurationWidget): is_valid = self.validate_value(value) if is_valid: value = json.dumps(value, indent=4) + self.text_input.setPlainText(value) if origin_value: @@ -730,13 +736,23 @@ class RawJsonWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.set_value(self.origin_value) def clear_value(self): - self.set_value("{{}}") + self.set_value("") def _on_value_change(self, item=None): - self.is_modified = self.item_value() != self.origin_value + value = self.item_value() + self.is_modified = value != self.origin_value if self.is_overidable: self._is_overriden = True + is_valid = self.validate_value(value) + if is_valid != self.is_valid: + self.is_valid = is_valid + if is_valid: + state = "" + else: + state = "invalid" + self.text_input.setProperty("state", state) + self.text_input.style().polish(self.text_input) self.update_style() self.value_changed.emit(self) From 7f156a957e9eabce4d214a573cdb55bf0303796e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 6 Aug 2020 10:47:09 +0200 Subject: [PATCH 049/813] input have is_modified property methods and was_overriden attribute --- pype/tools/config_setting/widgets/inputs.py | 65 ++++++++++++++++----- 1 file changed, 49 insertions(+), 16 deletions(-) diff --git a/pype/tools/config_setting/widgets/inputs.py b/pype/tools/config_setting/widgets/inputs.py index 50bd3d58f4..5fadf2ee60 100644 --- a/pype/tools/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/widgets/inputs.py @@ -40,7 +40,8 @@ class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): is_group = True self.is_group = is_group - self.is_modified = False + self._is_modified = False + self.was_overriden = False self._is_overriden = False self._state = None @@ -96,6 +97,10 @@ class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): def child_overriden(self): return self._is_overriden + @property + def is_modified(self): + return self._is_modified + @property def is_overidable(self): return self._parent.is_overidable @@ -107,7 +112,7 @@ class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): return self._parent.is_overriden def _on_value_change(self, item=None): - self.is_modified = self.item_value() != self.origin_value + self._is_modified = self.item_value() != self.origin_value if self.is_overidable: self._is_overriden = True @@ -161,7 +166,8 @@ class IntegerWidget(QtWidgets.QWidget, PypeConfigurationWidget): is_group = True self.is_group = is_group - self.is_modified = False + self._is_modified = False + self.was_overriden = False self._is_overriden = False self._state = None @@ -204,6 +210,10 @@ class IntegerWidget(QtWidgets.QWidget, PypeConfigurationWidget): def child_overriden(self): return self._is_overriden + @property + def is_modified(self): + return self._is_modified + @property def is_overidable(self): return self._parent.is_overidable @@ -227,7 +237,7 @@ class IntegerWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.set_value(self.origin_value) def _on_value_change(self, item=None): - self.is_modified = self.item_value() != self.origin_value + self._is_modified = self.item_value() != self.origin_value if self.is_overidable: self._is_overriden = True @@ -282,7 +292,8 @@ class FloatWidget(QtWidgets.QWidget, PypeConfigurationWidget): is_group = True self.is_group = is_group - self.is_modified = False + self._is_modified = False + self.was_overriden = False self._is_overriden = False self._state = None @@ -335,6 +346,10 @@ class FloatWidget(QtWidgets.QWidget, PypeConfigurationWidget): def child_overriden(self): return self._is_overriden + @property + def is_modified(self): + return self._is_modified + @property def is_overidable(self): return self._parent.is_overidable @@ -358,7 +373,7 @@ class FloatWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.set_value(0) def _on_value_change(self, item=None): - self.is_modified = self.item_value() != self.origin_value + self._is_modified = self.item_value() != self.origin_value if self.is_overidable: self._is_overriden = True @@ -413,7 +428,8 @@ class TextSingleLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): is_group = True self.is_group = is_group - self.is_modified = False + self._is_modified = False + self.was_overriden = False self._is_overriden = False self._state = None @@ -456,6 +472,10 @@ class TextSingleLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): def child_overriden(self): return self._is_overriden + @property + def is_modified(self): + return self._is_modified + @property def is_overidable(self): return self._parent.is_overidable @@ -479,7 +499,7 @@ class TextSingleLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.set_value("") def _on_value_change(self, item=None): - self.is_modified = self.item_value() != self.origin_value + self._is_modified = self.item_value() != self.origin_value if self.is_overidable: self._is_overriden = True @@ -535,6 +555,7 @@ class TextMultiLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.is_group = is_group self.is_modified = False + self.was_overriden = False self._is_overriden = False self._state = None @@ -575,6 +596,10 @@ class TextMultiLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): def child_overriden(self): return self._is_overriden + @property + def is_modified(self): + return self._is_modified + @property def is_overidable(self): return self._parent.is_overidable @@ -598,7 +623,7 @@ class TextMultiLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.set_value("") def _on_value_change(self, item=None): - self.is_modified = self.item_value() != self.origin_value + self._is_modified = self.item_value() != self.origin_value if self.is_overidable: self._is_overriden = True @@ -653,7 +678,8 @@ class RawJsonWidget(QtWidgets.QWidget, PypeConfigurationWidget): is_group = True self.is_group = is_group - self.is_modified = False + self._is_modified = False + self.was_overriden = False self._is_overriden = False self._state = None @@ -702,6 +728,10 @@ class RawJsonWidget(QtWidgets.QWidget, PypeConfigurationWidget): def child_overriden(self): return self._is_overriden + @property + def is_modified(self): + return self._is_modified + @property def is_overidable(self): return self._parent.is_overidable @@ -740,7 +770,7 @@ class RawJsonWidget(QtWidgets.QWidget, PypeConfigurationWidget): def _on_value_change(self, item=None): value = self.item_value() - self.is_modified = value != self.origin_value + self._is_modified = value != self.origin_value if self.is_overidable: self._is_overriden = True @@ -961,7 +991,7 @@ class TextListWidget(QtWidgets.QWidget, PypeConfigurationWidget): if not any_parent_is_group and not is_group: is_group = True - self.is_modified = False + self._is_modified = False self.is_group = is_group self._is_overriden = False @@ -1005,6 +1035,10 @@ class TextListWidget(QtWidgets.QWidget, PypeConfigurationWidget): def child_overriden(self): return self._is_overriden + @property + def is_modified(self): + return self._is_modified + @property def is_overidable(self): return self._parent.is_overidable @@ -1016,7 +1050,7 @@ class TextListWidget(QtWidgets.QWidget, PypeConfigurationWidget): return self._parent.is_overriden def _on_value_change(self, item=None): - self.is_modified = self.item_value() != self.origin_value + self._is_modified = self.item_value() != self.origin_value if self.is_overidable: self._is_overriden = True @@ -1713,6 +1747,8 @@ class ModifiableDict(ExpandingWidget, PypeConfigurationWidget): def update_style(self, is_overriden=None): child_modified = self.child_modified if is_overriden is None: + is_overriden = self.is_overriden + child_overriden = self.child_overriden child_state = self.style_state(child_overriden, child_modified) if child_state != self._child_state: @@ -1720,9 +1756,6 @@ class ModifiableDict(ExpandingWidget, PypeConfigurationWidget): self.style().polish(self) self._child_state = child_state - if is_overriden is None: - is_overriden = self.is_overriden - if child_modified and not is_overriden: state = self.default_state else: From fde11ec034de0d9f414119c878dcb007f7603b9c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 6 Aug 2020 11:15:05 +0200 Subject: [PATCH 050/813] is_modified also looks if overridatio nhas changed --- pype/tools/config_setting/widgets/inputs.py | 27 +++++++++++---------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/pype/tools/config_setting/widgets/inputs.py b/pype/tools/config_setting/widgets/inputs.py index 5fadf2ee60..052520d8d5 100644 --- a/pype/tools/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/widgets/inputs.py @@ -41,7 +41,7 @@ class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.is_group = is_group self._is_modified = False - self.was_overriden = False + self._was_overriden = False self._is_overriden = False self._state = None @@ -99,7 +99,7 @@ class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): @property def is_modified(self): - return self._is_modified + return self._is_modified or (self._was_overriden != self.is_overriden) @property def is_overidable(self): @@ -167,7 +167,7 @@ class IntegerWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.is_group = is_group self._is_modified = False - self.was_overriden = False + self._was_overriden = False self._is_overriden = False self._state = None @@ -212,7 +212,7 @@ class IntegerWidget(QtWidgets.QWidget, PypeConfigurationWidget): @property def is_modified(self): - return self._is_modified + return self._is_modified or (self._was_overriden != self.is_overriden) @property def is_overidable(self): @@ -293,7 +293,7 @@ class FloatWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.is_group = is_group self._is_modified = False - self.was_overriden = False + self._was_overriden = False self._is_overriden = False self._state = None @@ -348,7 +348,7 @@ class FloatWidget(QtWidgets.QWidget, PypeConfigurationWidget): @property def is_modified(self): - return self._is_modified + return self._is_modified or (self._was_overriden != self.is_overriden) @property def is_overidable(self): @@ -429,7 +429,7 @@ class TextSingleLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.is_group = is_group self._is_modified = False - self.was_overriden = False + self._was_overriden = False self._is_overriden = False self._state = None @@ -474,7 +474,7 @@ class TextSingleLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): @property def is_modified(self): - return self._is_modified + return self._is_modified or (self._was_overriden != self.is_overriden) @property def is_overidable(self): @@ -555,7 +555,7 @@ class TextMultiLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.is_group = is_group self.is_modified = False - self.was_overriden = False + self._was_overriden = False self._is_overriden = False self._state = None @@ -598,7 +598,7 @@ class TextMultiLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): @property def is_modified(self): - return self._is_modified + return self._is_modified or (self._was_overriden != self.is_overriden) @property def is_overidable(self): @@ -679,7 +679,7 @@ class RawJsonWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.is_group = is_group self._is_modified = False - self.was_overriden = False + self._was_overriden = False self._is_overriden = False self._state = None @@ -730,7 +730,7 @@ class RawJsonWidget(QtWidgets.QWidget, PypeConfigurationWidget): @property def is_modified(self): - return self._is_modified + return self._is_modified or (self._was_overriden != self.is_overriden) @property def is_overidable(self): @@ -993,6 +993,7 @@ class TextListWidget(QtWidgets.QWidget, PypeConfigurationWidget): self._is_modified = False self.is_group = is_group + self._was_overriden = False self._is_overriden = False self._state = None @@ -1037,7 +1038,7 @@ class TextListWidget(QtWidgets.QWidget, PypeConfigurationWidget): @property def is_modified(self): - return self._is_modified + return self._is_modified or (self._was_overriden != self.is_overriden) @property def is_overidable(self): From 2a4a81bde19fe47a01d03c79af9878a6d170c86d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 6 Aug 2020 14:45:27 +0200 Subject: [PATCH 051/813] raw json input has special input widget --- pype/tools/config_setting/style/style.css | 2 +- pype/tools/config_setting/widgets/inputs.py | 90 ++++++++++++++------- 2 files changed, 60 insertions(+), 32 deletions(-) diff --git a/pype/tools/config_setting/style/style.css b/pype/tools/config_setting/style/style.css index adf8344f89..a0c5db86a1 100644 --- a/pype/tools/config_setting/style/style.css +++ b/pype/tools/config_setting/style/style.css @@ -57,7 +57,7 @@ QPushButton[btn-type="expand-toggle"] { background: transparent; } -#RawJson[state="invalid"] { +#RawJsonInput[state="invalid"] { border-color: #ff5511; } diff --git a/pype/tools/config_setting/widgets/inputs.py b/pype/tools/config_setting/widgets/inputs.py index 052520d8d5..bd7d4b501f 100644 --- a/pype/tools/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/widgets/inputs.py @@ -657,6 +657,63 @@ class TextMultiLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): return {self.key: self.item_value()} +class RawJsonInput(QtWidgets.QPlainTextEdit): + tab_length = 4 + + def __init__(self, *args, **kwargs): + super(RawJsonInput, self).__init__(*args, **kwargs) + self.setObjectName("RawJsonInput") + self.setTabStopDistance( + QtGui.QFontMetricsF( + self.font() + ).horizontalAdvance(" ") * self.tab_length + ) + + self.is_valid = None + + def set_value(self, value): + self.setPlainText(value) + + def setPlainText(self, *args, **kwargs): + super(RawJsonInput, self).setPlainText(*args, **kwargs) + self.validate() + + def focusOutEvent(self, event): + super(RawJsonInput, self).focusOutEvent(event) + self.validate() + + def validate_value(self, value): + if isinstance(value, str) and not value: + return True + + try: + json.loads(value) + return True + except Exception: + return False + + def update_style(self, is_valid=None): + if is_valid is None: + return self.validate() + + if is_valid != self.is_valid: + self.is_valid = is_valid + if is_valid: + state = "" + else: + state = "invalid" + self.setProperty("state", state) + self.style().polish(self) + + def value(self): + return self.toPlainText() + + def validate(self): + value = self.value() + is_valid = self.validate_value(value) + self.update_style(is_valid) + + class RawJsonWidget(QtWidgets.QWidget, PypeConfigurationWidget): value_changed = QtCore.Signal(object) @@ -684,21 +741,14 @@ class RawJsonWidget(QtWidgets.QWidget, PypeConfigurationWidget): self._state = None - self.is_valid = None - super(RawJsonWidget, self).__init__(parent) layout = QtWidgets.QVBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) - self.text_input = QtWidgets.QPlainTextEdit() - self.text_input.setObjectName("RawJson") - self.text_input.setTabStopDistance( - QtGui.QFontMetricsF( - self.text_input.font() - ).horizontalAdvance(" ") * 4 - ) + self.text_input = RawJsonInput() + if not label_widget: label = input_data["label"] label_widget = QtWidgets.QLabel(label) @@ -742,20 +792,7 @@ class RawJsonWidget(QtWidgets.QWidget, PypeConfigurationWidget): return self._is_overriden return self._parent.is_overriden - def validate_value(self, value): - if not value: - return True - try: - json.dumps(value) - return True - except Exception: - return False - def set_value(self, value, origin_value=False): - is_valid = self.validate_value(value) - if is_valid: - value = json.dumps(value, indent=4) - self.text_input.setPlainText(value) if origin_value: @@ -774,15 +811,6 @@ class RawJsonWidget(QtWidgets.QWidget, PypeConfigurationWidget): if self.is_overidable: self._is_overriden = True - is_valid = self.validate_value(value) - if is_valid != self.is_valid: - self.is_valid = is_valid - if is_valid: - state = "" - else: - state = "invalid" - self.text_input.setProperty("state", state) - self.text_input.style().polish(self.text_input) self.update_style() self.value_changed.emit(self) From fb2681f98618b41a54e6dabc78364acf7b6a391c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 6 Aug 2020 15:53:55 +0200 Subject: [PATCH 052/813] renamed origin_value to default_value --- pype/tools/config_setting/widgets/inputs.py | 106 ++++++++++---------- 1 file changed, 54 insertions(+), 52 deletions(-) diff --git a/pype/tools/config_setting/widgets/inputs.py b/pype/tools/config_setting/widgets/inputs.py index bd7d4b501f..e3e60612cc 100644 --- a/pype/tools/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/widgets/inputs.py @@ -73,22 +73,24 @@ class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): if value is not NOT_SET: self.checkbox.setChecked(value) - self.origin_value = self.item_value() + self.default_value = self.item_value() self.checkbox.stateChanged.connect(self._on_value_change) - def set_value(self, value, origin_value=False): + def set_value(self, value, default_value=False): self.checkbox.setChecked(value) - if origin_value: - self.origin_value = self.item_value() + if default_value: + self.default_value = self.item_value() self._on_value_change() def reset_value(self): - self.set_value(self.origin_value) + self.set_value(self.default_value) def clear_value(self): self.reset_value() + def apply_overrides(self, overrides): + @property def child_modified(self): return self.is_modified @@ -112,7 +114,7 @@ class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): return self._parent.is_overriden def _on_value_change(self, item=None): - self._is_modified = self.item_value() != self.origin_value + self._is_modified = self.item_value() != self.default_value if self.is_overidable: self._is_overriden = True @@ -198,7 +200,7 @@ class IntegerWidget(QtWidgets.QWidget, PypeConfigurationWidget): if value is not NOT_SET: self.int_input.setValue(value) - self.origin_value = self.item_value() + self.default_value = self.item_value() self.int_input.valueChanged.connect(self._on_value_change) @@ -224,20 +226,20 @@ class IntegerWidget(QtWidgets.QWidget, PypeConfigurationWidget): return self._is_overriden return self._parent.is_overriden - def set_value(self, value, origin_value=False): + def set_value(self, value, default_value=False): self.int_input.setValue(value) - if origin_value: - self.origin_value = self.item_value() + if default_value: + self.default_value = self.item_value() self._on_value_change() def clear_value(self): self.set_value(0) def reset_value(self): - self.set_value(self.origin_value) + self.set_value(self.default_value) def _on_value_change(self, item=None): - self._is_modified = self.item_value() != self.origin_value + self._is_modified = self.item_value() != self.default_value if self.is_overidable: self._is_overriden = True @@ -334,7 +336,7 @@ class FloatWidget(QtWidgets.QWidget, PypeConfigurationWidget): if value is not NOT_SET: self.float_input.setValue(value) - self.origin_value = self.item_value() + self.default_value = self.item_value() self.float_input.valueChanged.connect(self._on_value_change) @@ -360,20 +362,20 @@ class FloatWidget(QtWidgets.QWidget, PypeConfigurationWidget): return self._is_overriden return self._parent.is_overriden - def set_value(self, value, origin_value=False): + def set_value(self, value, default_value=False): self.float_input.setValue(value) - if origin_value: - self.origin_value = self.item_value() + if default_value: + self.default_value = self.item_value() self._on_value_change() def reset_value(self): - self.set_value(self.origin_value) + self.set_value(self.default_value) def clear_value(self): self.set_value(0) def _on_value_change(self, item=None): - self._is_modified = self.item_value() != self.origin_value + self._is_modified = self.item_value() != self.default_value if self.is_overidable: self._is_overriden = True @@ -460,7 +462,7 @@ class TextSingleLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): if value is not NOT_SET: self.text_input.setText(value) - self.origin_value = self.item_value() + self.default_value = self.item_value() self.text_input.textChanged.connect(self._on_value_change) @@ -486,20 +488,20 @@ class TextSingleLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): return self._is_overriden return self._parent.is_overriden - def set_value(self, value, origin_value=False): + def set_value(self, value, default_value=False): self.text_input.setText(value) - if origin_value: - self.origin_value = self.item_value() + if default_value: + self.default_value = self.item_value() self._on_value_change() def reset_value(self): - self.set_value(self.origin_value) + self.set_value(self.default_value) def clear_value(self): self.set_value("") def _on_value_change(self, item=None): - self._is_modified = self.item_value() != self.origin_value + self._is_modified = self.item_value() != self.default_value if self.is_overidable: self._is_overriden = True @@ -584,7 +586,7 @@ class TextMultiLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): if value is not NOT_SET: self.text_input.setPlainText(value) - self.origin_value = self.item_value() + self.default_value = self.item_value() self.text_input.textChanged.connect(self._on_value_change) @@ -610,20 +612,20 @@ class TextMultiLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): return self._is_overriden return self._parent.is_overriden - def set_value(self, value, origin_value=False): + def set_value(self, value, default_value=False): self.text_input.setPlainText(value) - if origin_value: - self.origin_value = self.item_value() + if default_value: + self.default_value = self.item_value() self._on_value_change() def reset_value(self): - self.set_value(self.origin_value) + self.set_value(self.default_value) def clear_value(self): self.set_value("") def _on_value_change(self, item=None): - self._is_modified = self.item_value() != self.origin_value + self._is_modified = self.item_value() != self.default_value if self.is_overidable: self._is_overriden = True @@ -766,7 +768,7 @@ class RawJsonWidget(QtWidgets.QWidget, PypeConfigurationWidget): if value is not NOT_SET: self.text_input.setPlainText(value) - self.origin_value = self.item_value() + self.default_value = self.item_value() self.text_input.textChanged.connect(self._on_value_change) @@ -792,22 +794,22 @@ class RawJsonWidget(QtWidgets.QWidget, PypeConfigurationWidget): return self._is_overriden return self._parent.is_overriden - def set_value(self, value, origin_value=False): + def set_value(self, value, default_value=False): self.text_input.setPlainText(value) - if origin_value: - self.origin_value = self.item_value() + if default_value: + self.default_value = self.item_value() self._on_value_change() def reset_value(self): - self.set_value(self.origin_value) + self.set_value(self.default_value) def clear_value(self): self.set_value("") def _on_value_change(self, item=None): value = self.item_value() - self._is_modified = value != self.origin_value + self._is_modified = value != self.default_value if self.is_overidable: self._is_overriden = True @@ -915,21 +917,21 @@ class TextListSubWidget(QtWidgets.QWidget, PypeConfigurationWidget): if value is not NOT_SET: self.set_value(value) - self.origin_value = self.item_value() + self.default_value = self.item_value() - def set_value(self, value, origin_value=False): + def set_value(self, value, default_value=False): for input_field in self.input_fields: self.remove_row(input_field) for item_text in value: self.add_row(text=item_text) - if origin_value: - self.origin_value = self.item_value() + if default_value: + self.default_value = self.item_value() self._on_value_change() def reset_value(self): - self.set_value(self.origin_value) + self.set_value(self.default_value) def clear_value(self): self.set_value([]) @@ -1054,7 +1056,7 @@ class TextListWidget(QtWidgets.QWidget, PypeConfigurationWidget): layout.addWidget(self.value_widget) self.setLayout(layout) - self.origin_value = self.item_value() + self.default_value = self.item_value() @property def child_modified(self): @@ -1079,7 +1081,7 @@ class TextListWidget(QtWidgets.QWidget, PypeConfigurationWidget): return self._parent.is_overriden def _on_value_change(self, item=None): - self._is_modified = self.item_value() != self.origin_value + self._is_modified = self.item_value() != self.default_value if self.is_overidable: self._is_overriden = True @@ -1087,14 +1089,14 @@ class TextListWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.value_changed.emit(self) - def set_value(self, value, origin_value=False): + def set_value(self, value, default_value=False): self.value_widget.set_value(value) - if origin_value: - self.origin_value = self.item_value() + if default_value: + self.default_value = self.item_value() self._on_value_change() def reset_value(self): - self.set_value(self.origin_value) + self.set_value(self.default_value) def clear_value(self): self.set_value([]) @@ -1533,7 +1535,7 @@ class ModifiableDictItem(QtWidgets.QWidget, PypeConfigurationWidget): self.value_input.value_changed.connect(self._on_value_change) self.origin_key = self._key() - self.origin_value = self.value_input.item_value() + self.default_value = self.value_input.item_value() self.is_single = False @@ -1630,7 +1632,7 @@ class ModifiableDictSubWidget(QtWidgets.QWidget, PypeConfigurationWidget): if self.count() == 0: self.add_row() - self.origin_value = self.config_value() + self.default_value = self.config_value() @property def is_overidable(self): @@ -1680,7 +1682,7 @@ class ModifiableDictSubWidget(QtWidgets.QWidget, PypeConfigurationWidget): if value is not None and key is not None: item_widget.origin_key = key item_widget.key_input.setText(key) - item_widget.value_input.set_value(value, origin_value=True) + item_widget.value_input.set_value(value, default_value=True) else: self._on_value_change() self.parent().updateGeometry() @@ -1750,10 +1752,10 @@ class ModifiableDict(ExpandingWidget, PypeConfigurationWidget): self.key = input_data["key"] - self.origin_value = self.item_value() + self.default_value = self.item_value() def _on_value_change(self, item=None): - self.child_modified = self.item_value() != self.origin_value + self.child_modified = self.item_value() != self.default_value if self.is_group and self.is_overidable: self._is_overriden = True From a59aa34a8ab27b202c377c2134db3a9b17bbfda0 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 6 Aug 2020 16:04:39 +0200 Subject: [PATCH 053/813] just added override_value attribute --- pype/tools/config_setting/widgets/inputs.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/pype/tools/config_setting/widgets/inputs.py b/pype/tools/config_setting/widgets/inputs.py index e3e60612cc..5f0411b72b 100644 --- a/pype/tools/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/widgets/inputs.py @@ -74,6 +74,7 @@ class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.checkbox.setChecked(value) self.default_value = self.item_value() + self.override_value = None self.checkbox.stateChanged.connect(self._on_value_change) @@ -201,6 +202,7 @@ class IntegerWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.int_input.setValue(value) self.default_value = self.item_value() + self.override_value = None self.int_input.valueChanged.connect(self._on_value_change) @@ -337,6 +339,7 @@ class FloatWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.float_input.setValue(value) self.default_value = self.item_value() + self.override_value = None self.float_input.valueChanged.connect(self._on_value_change) @@ -463,6 +466,7 @@ class TextSingleLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.text_input.setText(value) self.default_value = self.item_value() + self.override_value = None self.text_input.textChanged.connect(self._on_value_change) @@ -587,6 +591,7 @@ class TextMultiLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.text_input.setPlainText(value) self.default_value = self.item_value() + self.override_value = None self.text_input.textChanged.connect(self._on_value_change) @@ -769,6 +774,7 @@ class RawJsonWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.text_input.setPlainText(value) self.default_value = self.item_value() + self.override_value = None self.text_input.textChanged.connect(self._on_value_change) @@ -918,6 +924,7 @@ class TextListSubWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.set_value(value) self.default_value = self.item_value() + self.override_value = None def set_value(self, value, default_value=False): for input_field in self.input_fields: @@ -1057,6 +1064,7 @@ class TextListWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.setLayout(layout) self.default_value = self.item_value() + self.override_value = None @property def child_modified(self): @@ -1534,9 +1542,12 @@ class ModifiableDictItem(QtWidgets.QWidget, PypeConfigurationWidget): self.key_input.textChanged.connect(self._on_value_change) self.value_input.value_changed.connect(self._on_value_change) - self.origin_key = self._key() + self.default_key = self._key() self.default_value = self.value_input.item_value() + self.override_key = None + self.override_value = None + self.is_single = False def _key(self): @@ -1563,7 +1574,7 @@ class ModifiableDictItem(QtWidgets.QWidget, PypeConfigurationWidget): return self._parent.is_overriden def is_key_modified(self): - return self._key() != self.origin_key + return self._key() != self.default_key def is_value_modified(self): return self.value_input.is_modified @@ -1633,6 +1644,7 @@ class ModifiableDictSubWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.add_row() self.default_value = self.config_value() + self.override_value = None @property def is_overidable(self): @@ -1680,7 +1692,7 @@ class ModifiableDictSubWidget(QtWidgets.QWidget, PypeConfigurationWidget): # Set value if entered value is not None # else (when add button clicked) trigger `_on_value_change` if value is not None and key is not None: - item_widget.origin_key = key + item_widget.default_key = key item_widget.key_input.setText(key) item_widget.value_input.set_value(value, default_value=True) else: @@ -1753,6 +1765,7 @@ class ModifiableDict(ExpandingWidget, PypeConfigurationWidget): self.key = input_data["key"] self.default_value = self.item_value() + self.override_value = None def _on_value_change(self, item=None): self.child_modified = self.item_value() != self.default_value From 407fbbe77babb1fddf7fec433152d7c2b3cf8831 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 6 Aug 2020 16:57:09 +0200 Subject: [PATCH 054/813] call aplpy_overrides on project change --- pype/tools/config_setting/widgets/base.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/pype/tools/config_setting/widgets/base.py b/pype/tools/config_setting/widgets/base.py index 52184b4c8a..a90eefd400 100644 --- a/pype/tools/config_setting/widgets/base.py +++ b/pype/tools/config_setting/widgets/base.py @@ -345,9 +345,17 @@ class ProjectWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.content_layout.addWidget(item) def _on_project_change(self): - self.is_overidable = ( - self.project_list_widget.project_name() is not None - ) + project_name = self.project_list_widget.project_name() + + if project_name is None: + overrides = None + self.is_overidable = False + else: + overrides = config.project_preset_overrides(project_name) + self.is_overidable = True + + for item in self.input_fields: + item.apply_overrides(overrides) def _save(self): output = {} From 2e7980104f1cee00828455148e48250a8fc7c04b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 6 Aug 2020 16:57:54 +0200 Subject: [PATCH 055/813] addde apply_overrides to inputs --- pype/tools/config_setting/widgets/inputs.py | 76 +++++++++++++++++---- 1 file changed, 63 insertions(+), 13 deletions(-) diff --git a/pype/tools/config_setting/widgets/inputs.py b/pype/tools/config_setting/widgets/inputs.py index 5f0411b72b..ea6a9e3483 100644 --- a/pype/tools/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/widgets/inputs.py @@ -78,19 +78,30 @@ class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.checkbox.stateChanged.connect(self._on_value_change) - def set_value(self, value, default_value=False): + def set_value(self, value, *, default_value=False): self.checkbox.setChecked(value) if default_value: self.default_value = self.item_value() self._on_value_change() def reset_value(self): - self.set_value(self.default_value) + if self.is_overidable: + self.set_value(self.override_value) + else: + self.set_value(self.default_value) def clear_value(self): self.reset_value() - def apply_overrides(self, overrides): + def apply_overrides(self, override_value): + self.override_value = override_value + if override_value is None: + self._is_overriden = False + value = self.default_value + else: + self._is_overriden = True + value = override_value + self.checkbox.setChecked(value) @property def child_modified(self): @@ -115,9 +126,17 @@ class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): return self._parent.is_overriden def _on_value_change(self, item=None): - self._is_modified = self.item_value() != self.default_value + _value = self.item_value() + is_modified = None if self.is_overidable: self._is_overriden = True + if self.override_value is not None: + is_modified = _value != self.override_value + + if is_modified is None: + is_modified = _value != self.default_value + + self._is_modified = is_modified self.update_style() @@ -228,7 +247,7 @@ class IntegerWidget(QtWidgets.QWidget, PypeConfigurationWidget): return self._is_overriden return self._parent.is_overriden - def set_value(self, value, default_value=False): + def set_value(self, value, *, default_value=False): self.int_input.setValue(value) if default_value: self.default_value = self.item_value() @@ -240,6 +259,16 @@ class IntegerWidget(QtWidgets.QWidget, PypeConfigurationWidget): def reset_value(self): self.set_value(self.default_value) + def apply_overrides(self, override_value): + self.override_value = override_value + if override_value is None: + self._is_overriden = False + value = self.default_value + else: + self._is_overriden = True + value = override_value + self.int_input.setValue(value) + def _on_value_change(self, item=None): self._is_modified = self.item_value() != self.default_value if self.is_overidable: @@ -365,7 +394,7 @@ class FloatWidget(QtWidgets.QWidget, PypeConfigurationWidget): return self._is_overriden return self._parent.is_overriden - def set_value(self, value, default_value=False): + def set_value(self, value, *, default_value=False): self.float_input.setValue(value) if default_value: self.default_value = self.item_value() @@ -374,6 +403,16 @@ class FloatWidget(QtWidgets.QWidget, PypeConfigurationWidget): def reset_value(self): self.set_value(self.default_value) + def apply_overrides(self, override_value): + self.override_value = override_value + if override_value is None: + self._is_overriden = False + value = self.default_value + else: + self._is_overriden = True + value = override_value + self.float_input.setChecked(value) + def clear_value(self): self.set_value(0) @@ -492,7 +531,7 @@ class TextSingleLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): return self._is_overriden return self._parent.is_overriden - def set_value(self, value, default_value=False): + def set_value(self, value, *, default_value=False): self.text_input.setText(value) if default_value: self.default_value = self.item_value() @@ -501,6 +540,9 @@ class TextSingleLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): def reset_value(self): self.set_value(self.default_value) + def apply_overrides(self, override_value): + self.set_value(override_value, override_value=True) + def clear_value(self): self.set_value("") @@ -617,7 +659,7 @@ class TextMultiLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): return self._is_overriden return self._parent.is_overriden - def set_value(self, value, default_value=False): + def set_value(self, value, *, default_value=False): self.text_input.setPlainText(value) if default_value: self.default_value = self.item_value() @@ -626,6 +668,9 @@ class TextMultiLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): def reset_value(self): self.set_value(self.default_value) + def apply_overrides(self, override_value): + self.set_value(override_value, override_value=True) + def clear_value(self): self.set_value("") @@ -678,7 +723,7 @@ class RawJsonInput(QtWidgets.QPlainTextEdit): self.is_valid = None - def set_value(self, value): + def set_value(self, value, *, default_value=False): self.setPlainText(value) def setPlainText(self, *args, **kwargs): @@ -800,9 +845,8 @@ class RawJsonWidget(QtWidgets.QWidget, PypeConfigurationWidget): return self._is_overriden return self._parent.is_overriden - def set_value(self, value, default_value=False): + def set_value(self, value, *, default_value=False): self.text_input.setPlainText(value) - if default_value: self.default_value = self.item_value() self._on_value_change() @@ -813,6 +857,9 @@ class RawJsonWidget(QtWidgets.QWidget, PypeConfigurationWidget): def clear_value(self): self.set_value("") + def apply_overrides(self, override_value): + self.set_value(override_value, override_value=True) + def _on_value_change(self, item=None): value = self.item_value() self._is_modified = value != self.default_value @@ -926,7 +973,7 @@ class TextListSubWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.default_value = self.item_value() self.override_value = None - def set_value(self, value, default_value=False): + def set_value(self, value, *, default_value=False): for input_field in self.input_fields: self.remove_row(input_field) @@ -1097,7 +1144,7 @@ class TextListWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.value_changed.emit(self) - def set_value(self, value, default_value=False): + def set_value(self, value, *, default_value=False): self.value_widget.set_value(value) if default_value: self.default_value = self.item_value() @@ -1109,6 +1156,9 @@ class TextListWidget(QtWidgets.QWidget, PypeConfigurationWidget): def clear_value(self): self.set_value([]) + def apply_overrides(self, override_value): + self.set_value(override_value, override_value=True) + def update_style(self, is_overriden=None): if is_overriden is None: is_overriden = self.is_overriden From 04f650b9b59637adc74e3b6e9a67eff2f142a5e8 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 6 Aug 2020 18:12:01 +0200 Subject: [PATCH 056/813] project overrides are appliable for boolean --- pype/tools/config_setting/widgets/inputs.py | 98 ++++++++++++++++++--- 1 file changed, 88 insertions(+), 10 deletions(-) diff --git a/pype/tools/config_setting/widgets/inputs.py b/pype/tools/config_setting/widgets/inputs.py index ea6a9e3483..53bb2d729d 100644 --- a/pype/tools/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/widgets/inputs.py @@ -95,13 +95,16 @@ class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): def apply_overrides(self, override_value): self.override_value = override_value + self._is_modified = False if override_value is None: self._is_overriden = False + self._was_overriden = False value = self.default_value else: self._is_overriden = True + self._was_overriden = True value = override_value - self.checkbox.setChecked(value) + self.set_value(value) @property def child_modified(self): @@ -113,7 +116,7 @@ class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): @property def is_modified(self): - return self._is_modified or (self._was_overriden != self.is_overriden) + return self._is_modified or (self._was_overriden != self._is_overriden) @property def is_overidable(self): @@ -267,7 +270,7 @@ class IntegerWidget(QtWidgets.QWidget, PypeConfigurationWidget): else: self._is_overriden = True value = override_value - self.int_input.setValue(value) + self.set_value(value) def _on_value_change(self, item=None): self._is_modified = self.item_value() != self.default_value @@ -411,7 +414,7 @@ class FloatWidget(QtWidgets.QWidget, PypeConfigurationWidget): else: self._is_overriden = True value = override_value - self.float_input.setChecked(value) + self.set_value(value) def clear_value(self): self.set_value(0) @@ -541,7 +544,14 @@ class TextSingleLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.set_value(self.default_value) def apply_overrides(self, override_value): - self.set_value(override_value, override_value=True) + self.override_value = override_value + if override_value is None: + self._is_overriden = False + value = self.default_value + else: + self._is_overriden = True + value = override_value + self.set_value(value) def clear_value(self): self.set_value("") @@ -669,7 +679,14 @@ class TextMultiLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.set_value(self.default_value) def apply_overrides(self, override_value): - self.set_value(override_value, override_value=True) + self.override_value = override_value + if override_value is None: + self._is_overriden = False + value = self.default_value + else: + self._is_overriden = True + value = override_value + self.set_value(value) def clear_value(self): self.set_value("") @@ -858,7 +875,14 @@ class RawJsonWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.set_value("") def apply_overrides(self, override_value): - self.set_value(override_value, override_value=True) + self.override_value = override_value + if override_value is None: + self._is_overriden = False + value = self.default_value + else: + self._is_overriden = True + value = override_value + self.set_value(value) def _on_value_change(self, item=None): value = self.item_value() @@ -1157,7 +1181,14 @@ class TextListWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.set_value([]) def apply_overrides(self, override_value): - self.set_value(override_value, override_value=True) + self.override_value = override_value + if override_value is None: + self._is_overriden = False + value = self.default_value + else: + self._is_overriden = True + value = override_value + self.set_value(value) def update_style(self, is_overriden=None): if is_overriden is None: @@ -1291,6 +1322,14 @@ class DictExpandWidget(QtWidgets.QWidget, PypeConfigurationWidget): return self._is_overriden return self._parent.is_overriden + def apply_overrides(self, override_value): + for item in self.input_fields: + if override_value is None: + child_value = None + else: + child_value = override_value.get(item.key) + item.apply_overrides(child_value) + def _on_value_change(self, item=None): if self.is_group: if self.is_overidable: @@ -1379,6 +1418,8 @@ class DictExpandWidget(QtWidgets.QWidget, PypeConfigurationWidget): class DictInvisible(QtWidgets.QWidget, PypeConfigurationWidget): + value_changed = QtCore.Signal(object) + def __init__( self, input_data, values, parent_keys, parent, label_widget=None ): @@ -1418,6 +1459,9 @@ class DictInvisible(QtWidgets.QWidget, PypeConfigurationWidget): for child_data in input_data.get("children", []): self.add_children_gui(child_data, values) + def update_style(self, *args, **kwargs): + return + @property def is_overriden(self): return self._parent.is_overriden @@ -1465,9 +1509,32 @@ class DictInvisible(QtWidgets.QWidget, PypeConfigurationWidget): ) self.layout().addWidget(item) + item.value_changed.connect(self._on_value_change) + self.input_fields.append(item) return item + def _on_value_change(self, item=None): + if self.is_group: + if self.is_overidable: + self._is_overriden = True + # TODO update items + if item is not None: + is_overriden = self.is_overriden + for _item in self.input_fields: + if _item is not item: + _item.update_style(is_overriden) + + self.value_changed.emit(self) + + def apply_overrides(self, override_value): + for item in self.input_fields: + if override_value is None: + child_value = None + else: + child_value = override_value.get(item.key) + item.apply_overrides(child_value) + class DictFormWidget(QtWidgets.QWidget): value_changed = QtCore.Signal(object) @@ -1777,6 +1844,10 @@ class ModifiableDictSubWidget(QtWidgets.QWidget, PypeConfigurationWidget): class ModifiableDict(ExpandingWidget, PypeConfigurationWidget): + # Should be used only for dictionary with one datatype as value + # TODO this is actually input field (do not care if is group or not) + value_changed = QtCore.Signal(object) + def __init__( self, input_data, values, parent_keys, parent, label_widget=None @@ -1819,8 +1890,12 @@ class ModifiableDict(ExpandingWidget, PypeConfigurationWidget): def _on_value_change(self, item=None): self.child_modified = self.item_value() != self.default_value - if self.is_group and self.is_overidable: - self._is_overriden = True + + if self.is_group: + if self.is_overidable: + self._is_overriden = True + + self.value_changed.emit(self) self.update_style() @@ -1838,6 +1913,9 @@ class ModifiableDict(ExpandingWidget, PypeConfigurationWidget): return self._is_overriden return self._parent.is_overriden + def apply_overrides(self, override_value): + print(self, override_value) + def update_style(self, is_overriden=None): child_modified = self.child_modified if is_overriden is None: From 369e1cc08a2b8c8d637b1b17839a7e3fa7f91883 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 6 Aug 2020 18:12:17 +0200 Subject: [PATCH 057/813] added testing data --- .../kuba_each_case/plugins/maya/publish.json | 8 ++++ .../config/studio_presets/global/tools.json | 6 +++ .../config_gui_schema/plugins_gui_schema.json | 37 +++++++++++++++---- 3 files changed, 43 insertions(+), 8 deletions(-) create mode 100644 pype/tools/config_setting/config/project_overrides/kuba_each_case/plugins/maya/publish.json create mode 100644 pype/tools/config_setting/config/studio_presets/global/tools.json diff --git a/pype/tools/config_setting/config/project_overrides/kuba_each_case/plugins/maya/publish.json b/pype/tools/config_setting/config/project_overrides/kuba_each_case/plugins/maya/publish.json new file mode 100644 index 0000000000..46fc343b6c --- /dev/null +++ b/pype/tools/config_setting/config/project_overrides/kuba_each_case/plugins/maya/publish.json @@ -0,0 +1,8 @@ +{ + "ValidateModelName": { + "enabled": true + }, + "ValidateAssemblyName": { + "enabled": false + } +} diff --git a/pype/tools/config_setting/config/studio_presets/global/tools.json b/pype/tools/config_setting/config/studio_presets/global/tools.json new file mode 100644 index 0000000000..53aab7b2ca --- /dev/null +++ b/pype/tools/config_setting/config/studio_presets/global/tools.json @@ -0,0 +1,6 @@ +{ + "mtoa_3.0.1": false, + "mtoa_3.1.1": false, + "mtoa_3.2.0": false, + "yeti_2.1.2": false +} \ No newline at end of file diff --git a/pype/tools/config_setting/config_gui_schema/plugins_gui_schema.json b/pype/tools/config_setting/config_gui_schema/plugins_gui_schema.json index 79c1f85b85..6c83fee172 100644 --- a/pype/tools/config_setting/config_gui_schema/plugins_gui_schema.json +++ b/pype/tools/config_setting/config_gui_schema/plugins_gui_schema.json @@ -7,16 +7,37 @@ "key": "maya", "type": "dict-expanding", "label": "Maya", - "is_group": true, "children": [ { - "key": "test1", - "type": "raw-json", - "label": "Test1" - }, { - "key": "test2", - "type": "raw-json", - "label": "Test2" + "key": "publish", + "type": "dict-expanding", + "label": "Publish plugins", + "is_group": true, + "children": [ + { + "key": "ValidateModelName", + "type": "dict-invisible", + "label": "Validate Model Name", + "children": [ + { + "key": "enabled", + "type": "boolean", + "label": "Enabled" + } + ] + }, { + "key": "ValidateAssemblyName", + "type": "dict-invisible", + "label": "Validate Assembly Name", + "children": [ + { + "key": "enabled", + "type": "boolean", + "label": "Enabled" + } + ] + } + ] } ] } From d6a998d953cc68e0b9f8fce6588728bf7fc464d6 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 6 Aug 2020 19:01:13 +0200 Subject: [PATCH 058/813] project change won't affect state attributes --- pype/tools/config_setting/widgets/inputs.py | 68 +++++++++++++++------ 1 file changed, 51 insertions(+), 17 deletions(-) diff --git a/pype/tools/config_setting/widgets/inputs.py b/pype/tools/config_setting/widgets/inputs.py index 53bb2d729d..a192a370b0 100644 --- a/pype/tools/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/widgets/inputs.py @@ -76,16 +76,29 @@ class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.default_value = self.item_value() self.override_value = None + self._ignore_value_change = False + self.checkbox.stateChanged.connect(self._on_value_change) - def set_value(self, value, *, default_value=False): + def set_value( + self, value, *, + ignore_attr_changes=True, + default_value=False + ): + # Ignore value change because if `self.isChecked()` has same + # value as `value` the `_on_value_change` is not triggered + self._ignore_value_change = True + self.checkbox.setChecked(value) + if default_value: + ignore_attr_changes = True self.default_value = self.item_value() - self._on_value_change() + + self._on_value_change(ignore_attr_changes=ignore_attr_changes) def reset_value(self): - if self.is_overidable: + if self.is_overidable and self.override_value is not None: self.set_value(self.override_value) else: self.set_value(self.default_value) @@ -95,7 +108,6 @@ class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): def apply_overrides(self, override_value): self.override_value = override_value - self._is_modified = False if override_value is None: self._is_overriden = False self._was_overriden = False @@ -104,7 +116,12 @@ class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): self._is_overriden = True self._was_overriden = True value = override_value - self.set_value(value) + + self._is_modified = False + + self.set_value(value, ignore_attr_changes=True) + + print("apply_overrides", self.keys, override_value) @property def child_modified(self): @@ -128,18 +145,23 @@ class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): return self._is_overriden return self._parent.is_overriden - def _on_value_change(self, item=None): - _value = self.item_value() - is_modified = None - if self.is_overidable: - self._is_overriden = True - if self.override_value is not None: - is_modified = _value != self.override_value + def _on_value_change(self, item=None, ignore_attr_changes=False): + if self._ignore_value_change: + self._ignore_value_change = False + return - if is_modified is None: - is_modified = _value != self.default_value + if not ignore_attr_changes: + _value = self.item_value() + is_modified = None + if self.is_overidable: + self._is_overriden = True + if self.override_value is not None: + is_modified = _value != self.override_value - self._is_modified = is_modified + if is_modified is None: + is_modified = _value != self.default_value + + self._is_modified = is_modified self.update_style() @@ -1293,6 +1315,8 @@ class DictExpandWidget(QtWidgets.QWidget, PypeConfigurationWidget): keys.append(self.key) self.keys = keys + self._ignore_value_change = False + for child_data in input_data.get("children", []): self.add_children_gui(child_data, values) @@ -1323,6 +1347,9 @@ class DictExpandWidget(QtWidgets.QWidget, PypeConfigurationWidget): return self._parent.is_overriden def apply_overrides(self, override_value): + self._ignore_value_change = True + + self._is_overriden = False for item in self.input_fields: if override_value is None: child_value = None @@ -1330,8 +1357,15 @@ class DictExpandWidget(QtWidgets.QWidget, PypeConfigurationWidget): child_value = override_value.get(item.key) item.apply_overrides(child_value) - def _on_value_change(self, item=None): - if self.is_group: + self._ignore_value_change = False + + self._on_value_change(ignore_attr_changes=True) + + def _on_value_change(self, item=None, ignore_attr_changes=False): + if self._ignore_value_change: + return + + if not ignore_attr_changes and self.is_group: if self.is_overidable: self._is_overriden = True # TODO update items From ca948504997739e334e661b680ad5bbc72d04d01 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 7 Aug 2020 11:59:11 +0200 Subject: [PATCH 059/813] added testing schema --- .../config_gui_schema/project_gui_schema.json | 1 + .../test_project_schema.json | 33 +++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 pype/tools/config_setting/config_gui_schema/test_project_schema.json diff --git a/pype/tools/config_setting/config_gui_schema/project_gui_schema.json b/pype/tools/config_setting/config_gui_schema/project_gui_schema.json index d2a6221f99..366400e5ff 100644 --- a/pype/tools/config_setting/config_gui_schema/project_gui_schema.json +++ b/pype/tools/config_setting/config_gui_schema/project_gui_schema.json @@ -6,6 +6,7 @@ { "type": "schema", "children": [ + "test_project_schema", "ftrack_projects_gui_schema", "plugins_gui_schema" ] diff --git a/pype/tools/config_setting/config_gui_schema/test_project_schema.json b/pype/tools/config_setting/config_gui_schema/test_project_schema.json new file mode 100644 index 0000000000..e789b422a3 --- /dev/null +++ b/pype/tools/config_setting/config_gui_schema/test_project_schema.json @@ -0,0 +1,33 @@ +{ + "key": "test_inputs", + "type": "dict-expanding", + "label": "Test inputs", + "is_group": true, + "children": [ + { + "key": "boolean", + "type": "boolean", + "label": "Boolean" + }, { + "key": "test_singleline", + "type": "text-singleline", + "label": "Text singleline" + }, { + "key": "test_multiline", + "type": "text-multiline", + "label": "Text multiline" + }, { + "key": "raw_json", + "type": "raw-json", + "label": "Raw json" + }, { + "key": "int", + "type": "int", + "label": "Int" + }, { + "key": "float", + "type": "float", + "label": "Float" + } + ] +} From 3d24d4db53fbff285ffa67658f27486bf7d37497 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 7 Aug 2020 11:59:29 +0200 Subject: [PATCH 060/813] added ignore to integer --- pype/tools/config_setting/widgets/inputs.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/pype/tools/config_setting/widgets/inputs.py b/pype/tools/config_setting/widgets/inputs.py index a192a370b0..7e91d11a21 100644 --- a/pype/tools/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/widgets/inputs.py @@ -121,8 +121,6 @@ class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.set_value(value, ignore_attr_changes=True) - print("apply_overrides", self.keys, override_value) - @property def child_modified(self): return self.is_modified @@ -248,6 +246,8 @@ class IntegerWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.default_value = self.item_value() self.override_value = None + self._ignore_value_change = False + self.int_input.valueChanged.connect(self._on_value_change) @property @@ -294,10 +294,15 @@ class IntegerWidget(QtWidgets.QWidget, PypeConfigurationWidget): value = override_value self.set_value(value) - def _on_value_change(self, item=None): - self._is_modified = self.item_value() != self.default_value - if self.is_overidable: - self._is_overriden = True + def _on_value_change(self, item=None, ignore_attr_changes=False): + if self._ignore_value_change: + self._ignore_value_change = False + return + + if not ignore_attr_changes: + self._is_modified = self.item_value() != self.default_value + if self.is_overidable: + self._is_overriden = True self.update_style() @@ -634,7 +639,7 @@ class TextMultiLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): is_group = True self.is_group = is_group - self.is_modified = False + self._is_modified = False self._was_overriden = False self._is_overriden = False From 1c5b923888cbb2ca2246e2f7150fd0e816be7186 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 7 Aug 2020 17:18:36 +0200 Subject: [PATCH 061/813] overrides are apllies in right way --- pype/tools/config_setting/widgets/base.py | 5 + pype/tools/config_setting/widgets/inputs.py | 220 ++++++++++++++------ 2 files changed, 156 insertions(+), 69 deletions(-) diff --git a/pype/tools/config_setting/widgets/base.py b/pype/tools/config_setting/widgets/base.py index a90eefd400..9413b07733 100644 --- a/pype/tools/config_setting/widgets/base.py +++ b/pype/tools/config_setting/widgets/base.py @@ -63,6 +63,7 @@ class StudioWidget(QtWidgets.QWidget, PypeConfigurationWidget): is_overriden = False is_group = False any_parent_is_group = False + ignore_value_changes = False def __init__(self, parent=None): super(StudioWidget, self).__init__(parent) @@ -281,6 +282,8 @@ class ProjectWidget(QtWidgets.QWidget, PypeConfigurationWidget): super(ProjectWidget, self).__init__(parent) self.is_overidable = False + self.ignore_value_changes = False + self.input_fields = [] scroll_widget = QtWidgets.QScrollArea(self) @@ -354,8 +357,10 @@ class ProjectWidget(QtWidgets.QWidget, PypeConfigurationWidget): overrides = config.project_preset_overrides(project_name) self.is_overidable = True + self.ignore_value_changes = True for item in self.input_fields: item.apply_overrides(overrides) + self.ignore_value_changes = False def _save(self): output = {} diff --git a/pype/tools/config_setting/widgets/inputs.py b/pype/tools/config_setting/widgets/inputs.py index 7e91d11a21..a0226d7b73 100644 --- a/pype/tools/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/widgets/inputs.py @@ -76,26 +76,17 @@ class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.default_value = self.item_value() self.override_value = None - self._ignore_value_change = False - self.checkbox.stateChanged.connect(self._on_value_change) - def set_value( - self, value, *, - ignore_attr_changes=True, - default_value=False - ): + def set_value(self, value, *, default_value=False): # Ignore value change because if `self.isChecked()` has same # value as `value` the `_on_value_change` is not triggered - self._ignore_value_change = True - self.checkbox.setChecked(value) if default_value: - ignore_attr_changes = True self.default_value = self.item_value() - self._on_value_change(ignore_attr_changes=ignore_attr_changes) + self._on_value_change() def reset_value(self): if self.is_overidable and self.override_value is not None: @@ -119,7 +110,8 @@ class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): self._is_modified = False - self.set_value(value, ignore_attr_changes=True) + self.set_value(value) + self.update_style() @property def child_modified(self): @@ -143,23 +135,25 @@ class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): return self._is_overriden return self._parent.is_overriden - def _on_value_change(self, item=None, ignore_attr_changes=False): - if self._ignore_value_change: - self._ignore_value_change = False + @property + def ignore_value_changes(self): + return self._parent.ignore_value_changes + + def _on_value_change(self, item=None): + if self.ignore_value_changes: return - if not ignore_attr_changes: - _value = self.item_value() - is_modified = None - if self.is_overidable: - self._is_overriden = True - if self.override_value is not None: - is_modified = _value != self.override_value + _value = self.item_value() + is_modified = None + if self.is_overidable: + self._is_overriden = True + if self.override_value is not None: + is_modified = _value != self.override_value - if is_modified is None: - is_modified = _value != self.default_value + if is_modified is None: + is_modified = _value != self.default_value - self._is_modified = is_modified + self._is_modified = is_modified self.update_style() @@ -246,8 +240,6 @@ class IntegerWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.default_value = self.item_value() self.override_value = None - self._ignore_value_change = False - self.int_input.valueChanged.connect(self._on_value_change) @property @@ -272,6 +264,10 @@ class IntegerWidget(QtWidgets.QWidget, PypeConfigurationWidget): return self._is_overriden return self._parent.is_overriden + @property + def ignore_value_changes(self): + return self._parent.ignore_value_changes + def set_value(self, value, *, default_value=False): self.int_input.setValue(value) if default_value: @@ -288,21 +284,25 @@ class IntegerWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.override_value = override_value if override_value is None: self._is_overriden = False + self._was_overriden = False value = self.default_value else: self._is_overriden = True + self._was_overriden = True value = override_value - self.set_value(value) - def _on_value_change(self, item=None, ignore_attr_changes=False): - if self._ignore_value_change: - self._ignore_value_change = False + self._is_modified = False + + self.set_value(value) + self.update_style() + + def _on_value_change(self, item=None): + if self.ignore_value_changes: return - if not ignore_attr_changes: - self._is_modified = self.item_value() != self.default_value - if self.is_overidable: - self._is_overriden = True + self._is_modified = self.item_value() != self.default_value + if self.is_overidable: + self._is_overriden = True self.update_style() @@ -420,9 +420,11 @@ class FloatWidget(QtWidgets.QWidget, PypeConfigurationWidget): @property def is_overriden(self): - if self._is_overriden: - return self._is_overriden - return self._parent.is_overriden + return self._is_overriden or self._parent.is_overriden + + @property + def ignore_value_changes(self): + return self._parent.ignore_value_changes def set_value(self, value, *, default_value=False): self.float_input.setValue(value) @@ -441,12 +443,17 @@ class FloatWidget(QtWidgets.QWidget, PypeConfigurationWidget): else: self._is_overriden = True value = override_value + self.set_value(value) + self.update_style() def clear_value(self): self.set_value(0) def _on_value_change(self, item=None): + if self.ignore_value_changes: + return + self._is_modified = self.item_value() != self.default_value if self.is_overidable: self._is_overriden = True @@ -557,9 +564,11 @@ class TextSingleLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): @property def is_overriden(self): - if self._is_overriden: - return self._is_overriden - return self._parent.is_overriden + return self._is_overriden or self._parent.is_overriden + + @property + def ignore_value_changes(self): + return self._parent.ignore_value_changes def set_value(self, value, *, default_value=False): self.text_input.setText(value) @@ -571,19 +580,27 @@ class TextSingleLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.set_value(self.default_value) def apply_overrides(self, override_value): + self._is_modified = False self.override_value = override_value if override_value is None: self._is_overriden = False + self._was_overriden = False value = self.default_value else: self._is_overriden = True + self._was_overriden = True value = override_value + self.set_value(value) + self.update_style() def clear_value(self): self.set_value("") def _on_value_change(self, item=None): + if self.ignore_value_changes: + return + self._is_modified = self.item_value() != self.default_value if self.is_overidable: self._is_overriden = True @@ -692,9 +709,11 @@ class TextMultiLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): @property def is_overriden(self): - if self._is_overriden: - return self._is_overriden - return self._parent.is_overriden + return self._is_overriden or self._parent.is_overriden + + @property + def ignore_value_changes(self): + return self._parent.ignore_value_changes def set_value(self, value, *, default_value=False): self.text_input.setPlainText(value) @@ -713,12 +732,17 @@ class TextMultiLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): else: self._is_overriden = True value = override_value + self.set_value(value) + self.update_style() def clear_value(self): self.set_value("") def _on_value_change(self, item=None): + if self.ignore_value_changes: + return + self._is_modified = self.item_value() != self.default_value if self.is_overidable: self._is_overriden = True @@ -885,9 +909,11 @@ class RawJsonWidget(QtWidgets.QWidget, PypeConfigurationWidget): @property def is_overriden(self): - if self._is_overriden: - return self._is_overriden - return self._parent.is_overriden + return self._is_overriden or self._parent.is_overriden + + @property + def ignore_value_changes(self): + return self._parent.ignore_value_changes def set_value(self, value, *, default_value=False): self.text_input.setPlainText(value) @@ -909,11 +935,15 @@ class RawJsonWidget(QtWidgets.QWidget, PypeConfigurationWidget): else: self._is_overriden = True value = override_value + self.set_value(value) + self.update_style() def _on_value_change(self, item=None): - value = self.item_value() - self._is_modified = value != self.default_value + if self.ignore_value_changes: + return + + self._is_modified = self.item_value() != self.default_value if self.is_overidable: self._is_overriden = True @@ -1182,11 +1212,15 @@ class TextListWidget(QtWidgets.QWidget, PypeConfigurationWidget): @property def is_overriden(self): - if self._is_overriden: - return self._is_overriden - return self._parent.is_overriden + return self._is_overriden or self._parent.is_overriden + + @property + def ignore_value_changes(self): + return self._parent.ignore_value_changes def _on_value_change(self, item=None): + if self.ignore_value_changes: + return self._is_modified = self.item_value() != self.default_value if self.is_overidable: self._is_overriden = True @@ -1215,7 +1249,9 @@ class TextListWidget(QtWidgets.QWidget, PypeConfigurationWidget): else: self._is_overriden = True value = override_value + self.set_value(value) + self.update_style() def update_style(self, is_overriden=None): if is_overriden is None: @@ -1320,8 +1356,6 @@ class DictExpandWidget(QtWidgets.QWidget, PypeConfigurationWidget): keys.append(self.key) self.keys = keys - self._ignore_value_change = False - for child_data in input_data.get("children", []): self.add_children_gui(child_data, values) @@ -1347,30 +1381,39 @@ class DictExpandWidget(QtWidgets.QWidget, PypeConfigurationWidget): @property def is_overriden(self): - if self._is_overriden: - return self._is_overriden - return self._parent.is_overriden + return self._is_overriden or self._parent.is_overriden + + @property + def ignore_value_changes(self): + return self._parent.ignore_value_changes def apply_overrides(self, override_value): - self._ignore_value_change = True - + # Make sure this is set to False self._is_overriden = False + for item in self.input_fields: if override_value is None: child_value = None else: child_value = override_value.get(item.key) + item.apply_overrides(child_value) - self._ignore_value_change = False + self._is_overriden = ( + self.is_group + and self.is_overidable + and ( + override_value is not None + or self.child_overriden + ) + ) + self.update_style() - self._on_value_change(ignore_attr_changes=True) - - def _on_value_change(self, item=None, ignore_attr_changes=False): - if self._ignore_value_change: + def _on_value_change(self, item=None): + if self.ignore_value_changes: return - if not ignore_attr_changes and self.is_group: + if self.is_group: if self.is_overidable: self._is_overriden = True # TODO update items @@ -1457,6 +1500,7 @@ class DictExpandWidget(QtWidgets.QWidget, PypeConfigurationWidget): class DictInvisible(QtWidgets.QWidget, PypeConfigurationWidget): + # TODO is not overridable by itself value_changed = QtCore.Signal(object) def __init__( @@ -1474,6 +1518,7 @@ class DictInvisible(QtWidgets.QWidget, PypeConfigurationWidget): self.any_parent_is_group = any_parent_is_group + self._is_overriden = False self.is_modified = False self.is_group = is_group @@ -1503,7 +1548,7 @@ class DictInvisible(QtWidgets.QWidget, PypeConfigurationWidget): @property def is_overriden(self): - return self._parent.is_overriden + return self._is_overriden or self._parent.is_overriden @property def is_overidable(self): @@ -1523,6 +1568,10 @@ class DictInvisible(QtWidgets.QWidget, PypeConfigurationWidget): return True return False + @property + def ignore_value_changes(self): + return self._parent.ignore_value_changes + def item_value(self): output = {} for input_field in self.input_fields: @@ -1554,6 +1603,9 @@ class DictInvisible(QtWidgets.QWidget, PypeConfigurationWidget): return item def _on_value_change(self, item=None): + if self.ignore_value_changes: + return + if self.is_group: if self.is_overidable: self._is_overriden = True @@ -1567,6 +1619,7 @@ class DictInvisible(QtWidgets.QWidget, PypeConfigurationWidget): self.value_changed.emit(self) def apply_overrides(self, override_value): + self._is_overriden = False for item in self.input_fields: if override_value is None: child_value = None @@ -1574,6 +1627,16 @@ class DictInvisible(QtWidgets.QWidget, PypeConfigurationWidget): child_value = override_value.get(item.key) item.apply_overrides(child_value) + self._is_overriden = ( + self.is_group + and self.is_overidable + and ( + override_value is not None + or self.child_overriden + ) + ) + self.update_style() + class DictFormWidget(QtWidgets.QWidget): value_changed = QtCore.Signal(object) @@ -1604,6 +1667,8 @@ class DictFormWidget(QtWidgets.QWidget): self.add_children_gui(child_data, values) def _on_value_change(self, item=None): + if self.ignore_value_changes: + return self.value_changed.emit(self) def item_value(self): @@ -1632,6 +1697,10 @@ class DictFormWidget(QtWidgets.QWidget): def is_overidable(self): return self._parent.is_overidable + @property + def ignore_value_changes(self): + return self._parent.ignore_value_changes + def config_value(self): return self.item_value() @@ -1729,6 +1798,10 @@ class ModifiableDictItem(QtWidgets.QWidget, PypeConfigurationWidget): def is_overriden(self): return self._parent.is_overriden + @property + def ignore_value_changes(self): + return self._parent.ignore_value_changes + def is_key_modified(self): return self._key() != self.default_key @@ -1814,6 +1887,10 @@ class ModifiableDictSubWidget(QtWidgets.QWidget, PypeConfigurationWidget): def is_group(self): return self._parent.is_group + @property + def ignore_value_changes(self): + return self._parent.ignore_value_changes + @property def any_parent_is_group(self): return self._parent.any_parent_is_group @@ -1928,6 +2005,9 @@ class ModifiableDict(ExpandingWidget, PypeConfigurationWidget): self.override_value = None def _on_value_change(self, item=None): + if self.ignore_value_changes: + return + self.child_modified = self.item_value() != self.default_value if self.is_group: @@ -1948,9 +2028,11 @@ class ModifiableDict(ExpandingWidget, PypeConfigurationWidget): @property def is_overriden(self): - if self._is_overriden: - return self._is_overriden - return self._parent.is_overriden + return self._is_overriden or self._parent.is_overriden + + @property + def ignore_value_changes(self): + return self._parent.ignore_value_changes def apply_overrides(self, override_value): print(self, override_value) From b6f948826dd1c471e7299d959c456b04e9771646 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 7 Aug 2020 17:31:11 +0200 Subject: [PATCH 062/813] fix is modified appliance --- pype/tools/config_setting/widgets/inputs.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/pype/tools/config_setting/widgets/inputs.py b/pype/tools/config_setting/widgets/inputs.py index a0226d7b73..8464eb8459 100644 --- a/pype/tools/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/widgets/inputs.py @@ -98,6 +98,7 @@ class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.reset_value() def apply_overrides(self, override_value): + self._is_modified = False self.override_value = override_value if override_value is None: self._is_overriden = False @@ -108,8 +109,6 @@ class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): self._was_overriden = True value = override_value - self._is_modified = False - self.set_value(value) self.update_style() @@ -260,9 +259,7 @@ class IntegerWidget(QtWidgets.QWidget, PypeConfigurationWidget): @property def is_overriden(self): - if self._is_overriden: - return self._is_overriden - return self._parent.is_overriden + return self._is_overriden or self._parent.is_overriden @property def ignore_value_changes(self): @@ -281,6 +278,8 @@ class IntegerWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.set_value(self.default_value) def apply_overrides(self, override_value): + self._is_modified = False + self.override_value = override_value if override_value is None: self._is_overriden = False @@ -291,8 +290,6 @@ class IntegerWidget(QtWidgets.QWidget, PypeConfigurationWidget): self._was_overriden = True value = override_value - self._is_modified = False - self.set_value(value) self.update_style() @@ -436,6 +433,8 @@ class FloatWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.set_value(self.default_value) def apply_overrides(self, override_value): + self._is_modified = False + self.override_value = override_value if override_value is None: self._is_overriden = False @@ -581,6 +580,7 @@ class TextSingleLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): def apply_overrides(self, override_value): self._is_modified = False + self.override_value = override_value if override_value is None: self._is_overriden = False @@ -725,6 +725,8 @@ class TextMultiLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.set_value(self.default_value) def apply_overrides(self, override_value): + self._is_modified = False + self.override_value = override_value if override_value is None: self._is_overriden = False @@ -928,6 +930,8 @@ class RawJsonWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.set_value("") def apply_overrides(self, override_value): + self._is_modified = False + self.override_value = override_value if override_value is None: self._is_overriden = False @@ -1242,6 +1246,8 @@ class TextListWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.set_value([]) def apply_overrides(self, override_value): + self._is_modified = False + self.override_value = override_value if override_value is None: self._is_overriden = False From 02051472d6813abc335cae002088e8b615ebd36b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 7 Aug 2020 18:12:22 +0200 Subject: [PATCH 063/813] attribute cleanup on apply overrides --- pype/tools/config_setting/widgets/inputs.py | 100 +++++++------------- 1 file changed, 34 insertions(+), 66 deletions(-) diff --git a/pype/tools/config_setting/widgets/inputs.py b/pype/tools/config_setting/widgets/inputs.py index 8464eb8459..e0a18afe1d 100644 --- a/pype/tools/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/widgets/inputs.py @@ -99,6 +99,7 @@ class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): def apply_overrides(self, override_value): self._is_modified = False + self._state = None self.override_value = override_value if override_value is None: self._is_overriden = False @@ -122,7 +123,7 @@ class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): @property def is_modified(self): - return self._is_modified or (self._was_overriden != self._is_overriden) + return self._is_modified or (self._was_overriden != self.is_overriden) @property def is_overidable(self): @@ -130,9 +131,7 @@ class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): @property def is_overriden(self): - if self._is_overriden: - return self._is_overriden - return self._parent.is_overriden + return self._is_overriden or self._parent.is_overriden @property def ignore_value_changes(self): @@ -158,12 +157,8 @@ class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.value_changed.emit(self) - def update_style(self, is_overriden=None): - if is_overriden is None: - is_overriden = self.is_overriden - is_modified = self.is_modified - - state = self.style_state(is_overriden, is_modified) + def update_style(self): + state = self.style_state(self.is_overriden, self.is_modified) if self._state == state: return @@ -279,7 +274,7 @@ class IntegerWidget(QtWidgets.QWidget, PypeConfigurationWidget): def apply_overrides(self, override_value): self._is_modified = False - + self._state = None self.override_value = override_value if override_value is None: self._is_overriden = False @@ -305,12 +300,8 @@ class IntegerWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.value_changed.emit(self) - def update_style(self, is_overriden=None): - if is_overriden is None: - is_overriden = self.is_overriden - is_modified = self.is_modified - - state = self.style_state(is_overriden, is_modified) + def update_style(self): + state = self.style_state(self.is_overriden, self.is_modified) if self._state == state: return @@ -434,7 +425,7 @@ class FloatWidget(QtWidgets.QWidget, PypeConfigurationWidget): def apply_overrides(self, override_value): self._is_modified = False - + self._state = None self.override_value = override_value if override_value is None: self._is_overriden = False @@ -461,12 +452,8 @@ class FloatWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.value_changed.emit(self) - def update_style(self, is_overriden=None): - if is_overriden is None: - is_overriden = self.is_overriden - is_modified = self.is_modified - - state = self.style_state(is_overriden, is_modified) + def update_style(self): + state = self.style_state(self.is_overriden, self.is_modified) if self._state == state: return @@ -580,7 +567,7 @@ class TextSingleLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): def apply_overrides(self, override_value): self._is_modified = False - + self._state = None self.override_value = override_value if override_value is None: self._is_overriden = False @@ -609,12 +596,8 @@ class TextSingleLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.value_changed.emit(self) - def update_style(self, is_overriden=None): - if is_overriden is None: - is_overriden = self.is_overriden - is_modified = self.is_modified - - state = self.style_state(is_overriden, is_modified) + def update_style(self): + state = self.style_state(self.is_overriden, self.is_modified) if self._state == state: return @@ -726,7 +709,7 @@ class TextMultiLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): def apply_overrides(self, override_value): self._is_modified = False - + self._state = None self.override_value = override_value if override_value is None: self._is_overriden = False @@ -753,12 +736,8 @@ class TextMultiLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.value_changed.emit(self) - def update_style(self, is_overriden=None): - if is_overriden is None: - is_overriden = self.is_overriden - is_modified = self.is_modified - - state = self.style_state(is_overriden, is_modified) + def update_style(self): + state = self.style_state(self.is_overriden, self.is_modified) if self._state == state: return @@ -931,7 +910,7 @@ class RawJsonWidget(QtWidgets.QWidget, PypeConfigurationWidget): def apply_overrides(self, override_value): self._is_modified = False - + self._state = None self.override_value = override_value if override_value is None: self._is_overriden = False @@ -955,12 +934,8 @@ class RawJsonWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.value_changed.emit(self) - def update_style(self, is_overriden=None): - if is_overriden is None: - is_overriden = self.is_overriden - is_modified = self.is_modified - - state = self.style_state(is_overriden, is_modified) + def update_style(self): + state = self.style_state(self.is_overriden, self.is_modified) if self._state == state: return @@ -1247,7 +1222,7 @@ class TextListWidget(QtWidgets.QWidget, PypeConfigurationWidget): def apply_overrides(self, override_value): self._is_modified = False - + self._state = None self.override_value = override_value if override_value is None: self._is_overriden = False @@ -1259,12 +1234,8 @@ class TextListWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.set_value(value) self.update_style() - def update_style(self, is_overriden=None): - if is_overriden is None: - is_overriden = self.is_overriden - is_modified = self.is_modified - - state = self.style_state(is_overriden, is_modified) + def update_style(self): + state = self.style_state(self.is_overriden, self.is_modified) if self._state == state: return @@ -1396,7 +1367,8 @@ class DictExpandWidget(QtWidgets.QWidget, PypeConfigurationWidget): def apply_overrides(self, override_value): # Make sure this is set to False self._is_overriden = False - + self._state = None + self._child_state = None for item in self.input_fields: if override_value is None: child_value = None @@ -1427,7 +1399,7 @@ class DictExpandWidget(QtWidgets.QWidget, PypeConfigurationWidget): is_overriden = self.is_overriden for _item in self.input_fields: if _item is not item: - _item.update_style(is_overriden) + _item.update_style() self.value_changed.emit(self) @@ -1435,20 +1407,16 @@ class DictExpandWidget(QtWidgets.QWidget, PypeConfigurationWidget): def update_style(self, is_overriden=None): child_modified = self.child_modified - if is_overriden is None: - child_overriden = self.child_overriden - child_state = self.style_state(child_overriden, child_modified) - if child_state: - child_state = "child-{}".format(child_state) + child_state = self.style_state(self.child_overriden, child_modified) + if child_state: + child_state = "child-{}".format(child_state) - if child_state != self._child_state: - self.setProperty("state", child_state) - self.style().polish(self) - self._child_state = child_state - - if is_overriden is None: - is_overriden = self.is_overriden + if child_state != self._child_state: + self.setProperty("state", child_state) + self.style().polish(self) + self._child_state = child_state + is_overriden = self.is_overriden if child_modified and not is_overriden: state = self.default_state else: From aee77e630c36f5f1ccd064948b8acda0c9c109f7 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 7 Aug 2020 18:50:42 +0200 Subject: [PATCH 064/813] modifiabledict is working better now --- pype/tools/config_setting/widgets/inputs.py | 74 ++++++++++++++------- 1 file changed, 49 insertions(+), 25 deletions(-) diff --git a/pype/tools/config_setting/widgets/inputs.py b/pype/tools/config_setting/widgets/inputs.py index e0a18afe1d..839fe1e8a2 100644 --- a/pype/tools/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/widgets/inputs.py @@ -1786,7 +1786,7 @@ class ModifiableDictItem(QtWidgets.QWidget, PypeConfigurationWidget): def is_modified(self): return self.is_value_modified() or self.is_key_modified() - def update_style(self, is_overriden=None): + def update_style(self): if self.is_key_modified(): state = "modified" else: @@ -1957,10 +1957,11 @@ class ModifiableDict(ExpandingWidget, PypeConfigurationWidget): self.any_parent_is_group = any_parent_is_group - self.is_modified = False - self.child_modified = False - self._is_overriden = False self.is_group = is_group + self._is_modified = False + self._is_overriden = False + self._was_overriden = False + self._state = None super(ModifiableDict, self).__init__(input_data["label"], parent) self.setObjectName("ModifiableDict") @@ -1982,16 +1983,26 @@ class ModifiableDict(ExpandingWidget, PypeConfigurationWidget): if self.ignore_value_changes: return - self.child_modified = self.item_value() != self.default_value + if self.is_overidable: + self._is_overriden = True - if self.is_group: - if self.is_overidable: - self._is_overriden = True + if self.is_overriden: + self._is_modified = self.item_value() != self.override_value + else: + self._is_modified = self.item_value() != self.default_value self.value_changed.emit(self) self.update_style() + @property + def child_modified(self): + return self.is_modified + + @property + def is_modified(self): + return self._is_modified + @property def child_overriden(self): return self._is_overriden @@ -2004,29 +2015,42 @@ class ModifiableDict(ExpandingWidget, PypeConfigurationWidget): def is_overriden(self): return self._is_overriden or self._parent.is_overriden + @property + def is_modified(self): + return self._is_modified + @property def ignore_value_changes(self): return self._parent.ignore_value_changes def apply_overrides(self, override_value): - print(self, override_value) - - def update_style(self, is_overriden=None): - child_modified = self.child_modified - if is_overriden is None: - is_overriden = self.is_overriden - - child_overriden = self.child_overriden - child_state = self.style_state(child_overriden, child_modified) - if child_state != self._child_state: - self.setProperty("state", child_state) - self.style().polish(self) - self._child_state = child_state - - if child_modified and not is_overriden: - state = self.default_state + self._state = None + self._is_modified = False + self.override_value = override_value + if override_value is None: + self._is_overriden = False + self._was_overriden = False + value = self.default_value else: - state = self.style_state(self.is_overriden, child_modified) + self._is_overriden = True + self._was_overriden = True + value = override_value + + self.set_value(value) + self.update_style() + + def update_style(self): + state = self.style_state(self.is_overriden, self.is_modified) + if self._state == state: + return + + if state: + child_state = "child-{}".format(state) + else: + child_state = "" + + self.setProperty("state", child_state) + self.style().polish(self) self.label_widget.setProperty("state", state) self.label_widget.style().polish(self.label_widget) From de331447bec8b76a836b5e46a17de2ee02340f5b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 7 Aug 2020 19:21:35 +0200 Subject: [PATCH 065/813] added Websocket server to module imports --- pype/tools/tray/modules_imports.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pype/tools/tray/modules_imports.json b/pype/tools/tray/modules_imports.json index e9526dcddb..6a278840ea 100644 --- a/pype/tools/tray/modules_imports.json +++ b/pype/tools/tray/modules_imports.json @@ -54,5 +54,10 @@ "type": "module", "import_path": "pype.modules.adobe_communicator", "fromlist": ["pype", "modules"] + }, { + "title": "Websocket Server", + "type": "module", + "import_path": "pype.modules.websocket_server", + "fromlist": ["pype", "modules"] } ] From 68822216ca15a263130b71dc6711d8ec1605477b Mon Sep 17 00:00:00 2001 From: "petr.kalis" Date: Tue, 11 Aug 2020 10:23:54 +0200 Subject: [PATCH 066/813] Added client support --- .../websocket_server/websocket_server.py | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/pype/modules/websocket_server/websocket_server.py b/pype/modules/websocket_server/websocket_server.py index 56e71ea895..9d0d01d156 100644 --- a/pype/modules/websocket_server/websocket_server.py +++ b/pype/modules/websocket_server/websocket_server.py @@ -22,12 +22,16 @@ class WebSocketServer(): WIP """ + _instance = None def __init__(self): self.qaction = None self.failed_icon = None self._is_running = False default_port = 8099 + WebSocketServer._instance = self + self.client = None + self.handlers = {} try: self.presets = config.get_presets()["services"]["websocket_server"] @@ -76,8 +80,26 @@ class WebSocketServer(): module = importlib.import_module(module_name) cls = getattr(module, class_name) WebSocketAsync.add_route(class_name, cls) + self.handlers[class_name] = cls() # TODO refactor sys.path.pop() + def call(self, func): + log.debug("websocket.call {}".format(func)) + return self.websocket_thread.call_async(func) + + def task_finished(self, task): + print("task finished {}".format(task.result)) + print("client socket {}".format(self.client.client.socket)) + + def get_routes(self): + WebSocketAsync.get_routes() + + @staticmethod + def get_instance(): + if WebSocketServer._instance == None: + WebSocketServer() + return WebSocketServer._instance + def tray_start(self): self.websocket_thread.start() @@ -124,6 +146,7 @@ class WebsocketServerThread(threading.Thread): self.loop = None self.runner = None self.site = None + self.tasks = [] def run(self): self.is_running = True @@ -153,6 +176,20 @@ class WebsocketServerThread(threading.Thread): self.module.thread_stopped() log.info("Websocket server stopped") + def call_async(self, func): + # log.debug("call async") + # print("call aysnc") + # log.debug("my loop {}".format(self.loop)) + # task = self.loop.create_task(func) + # print("waitning") + # log.debug("waiting for task {}".format(func)) + # self.loop.run_until_complete(task) + # log.debug("returned value {}".format(task.result)) + # return task.result + task = self.loop.create_task(func) + task.add_done_callback(self.module.task_finished) + self.tasks.append(task) + async def start_server(self): """ Starts runner and TCPsite """ self.runner = web.AppRunner(self.module.app) @@ -169,6 +206,12 @@ class WebsocketServerThread(threading.Thread): periodically. """ while self.is_running: + while self.tasks: + task = self.tasks.pop(0) + log.debug("waiting for task {}".format(task)) + await task + log.debug("returned value {}".format(task.result)) + await asyncio.sleep(0.5) log.debug("Starting shutdown") From 0b6ef8c6f51b8a427a2e782547bc7476474cdd9e Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 11 Aug 2020 13:52:21 +0100 Subject: [PATCH 067/813] Isolate view on instance members if more than camera (one object). --- pype/plugins/maya/publish/extract_playblast.py | 5 +++++ pype/plugins/maya/publish/extract_thumbnail.py | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/pype/plugins/maya/publish/extract_playblast.py b/pype/plugins/maya/publish/extract_playblast.py index 8d45f98b90..3c9811d4c4 100644 --- a/pype/plugins/maya/publish/extract_playblast.py +++ b/pype/plugins/maya/publish/extract_playblast.py @@ -77,6 +77,11 @@ class ExtractPlayblast(pype.api.Extractor): pm.currentTime(refreshFrameInt - 1, edit=True) pm.currentTime(refreshFrameInt, edit=True) + # Isolate view is requested by having objects in the set besides a + # camera. + if len(instance.data["setMembers"]) > 1: + preset["isolate"] = instance.data["setMembers"] + with maintained_time(): filename = preset.get("filename", "%TEMP%") diff --git a/pype/plugins/maya/publish/extract_thumbnail.py b/pype/plugins/maya/publish/extract_thumbnail.py index c0eb2a608e..2edd19a559 100644 --- a/pype/plugins/maya/publish/extract_thumbnail.py +++ b/pype/plugins/maya/publish/extract_thumbnail.py @@ -77,6 +77,11 @@ class ExtractThumbnail(pype.api.Extractor): pm.currentTime(refreshFrameInt - 1, edit=True) pm.currentTime(refreshFrameInt, edit=True) + # Isolate view is requested by having objects in the set besides a + # camera. + if len(instance.data["setMembers"]) > 1: + preset["isolate"] = instance.data["setMembers"] + with maintained_time(): filename = preset.get("filename", "%TEMP%") From 0badfc0a5ace0cebc8252122f1e14b4a521a15c2 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 13 Aug 2020 11:22:07 +0200 Subject: [PATCH 068/813] adde unsaved changes dialog with 3 possible results --- pype/tools/config_setting/widgets/widgets.py | 37 ++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/pype/tools/config_setting/widgets/widgets.py b/pype/tools/config_setting/widgets/widgets.py index 34fdfde1a5..1d30a67e28 100644 --- a/pype/tools/config_setting/widgets/widgets.py +++ b/pype/tools/config_setting/widgets/widgets.py @@ -103,3 +103,40 @@ class ExpandingWidget(QtWidgets.QWidget): def resizeEvent(self, event): super(ExpandingWidget, self).resizeEvent(event) self.content_widget.updateGeometry() + + +class UnsavedChangesDialog(QtWidgets.QDialog): + message = "My message" + + def __init__(self, parent=None): + super().__init__(parent) + message_label = QtWidgets.QLabel(self.message) + + btns_widget = QtWidgets.QWidget(self) + btns_layout = QtWidgets.QHBoxLayout(btns_widget) + + btn_ok = QtWidgets.QPushButton("OK") + btn_ok.clicked.connect(self.on_ok_pressed) + btn_discard = QtWidgets.QPushButton("Discard changes") + btn_discard.clicked.connect(self.on_discard_pressed) + btn_cancel = QtWidgets.QPushButton("Cancel") + btn_cancel.clicked.connect(self.on_cancel_pressed) + + btns_layout.addWidget(btn_ok) + btns_layout.addWidget(btn_discard) + btns_layout.addWidget(btn_cancel) + + layout = QtWidgets.QVBoxLayout(self) + layout.addWidget(message_label) + layout.addWidget(btns_widget) + + self.state = None + + def on_cancel_pressed(self): + self.done(0) + + def on_ok_pressed(self): + self.done(1) + + def on_discard_pressed(self): + self.done(2) From 242c55716174fdc9482733098144aae14681db34 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 13 Aug 2020 11:24:30 +0200 Subject: [PATCH 069/813] modified dialog --- pype/tools/config_setting/widgets/widgets.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pype/tools/config_setting/widgets/widgets.py b/pype/tools/config_setting/widgets/widgets.py index 1d30a67e28..3d5528e17a 100644 --- a/pype/tools/config_setting/widgets/widgets.py +++ b/pype/tools/config_setting/widgets/widgets.py @@ -106,7 +106,7 @@ class ExpandingWidget(QtWidgets.QWidget): class UnsavedChangesDialog(QtWidgets.QDialog): - message = "My message" + message = "You have unsaved changes. What do you want to do with them?" def __init__(self, parent=None): super().__init__(parent) @@ -115,9 +115,9 @@ class UnsavedChangesDialog(QtWidgets.QDialog): btns_widget = QtWidgets.QWidget(self) btns_layout = QtWidgets.QHBoxLayout(btns_widget) - btn_ok = QtWidgets.QPushButton("OK") + btn_ok = QtWidgets.QPushButton("Save") btn_ok.clicked.connect(self.on_ok_pressed) - btn_discard = QtWidgets.QPushButton("Discard changes") + btn_discard = QtWidgets.QPushButton("Discard") btn_discard.clicked.connect(self.on_discard_pressed) btn_cancel = QtWidgets.QPushButton("Cancel") btn_cancel.clicked.connect(self.on_cancel_pressed) From 1eb5b1ed2b3cb4aff3cf6d6eb89382fabbafa76e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 13 Aug 2020 11:24:47 +0200 Subject: [PATCH 070/813] on project change is corrrect validation with dialog --- pype/tools/config_setting/widgets/base.py | 28 ++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/pype/tools/config_setting/widgets/base.py b/pype/tools/config_setting/widgets/base.py index 9413b07733..76fbad06c6 100644 --- a/pype/tools/config_setting/widgets/base.py +++ b/pype/tools/config_setting/widgets/base.py @@ -2,6 +2,7 @@ import os import json from Qt import QtWidgets, QtCore, QtGui from . import config +from .widgets import UnsavedChangesDialog from .lib import NOT_SET from avalon import io from queue import Queue @@ -217,16 +218,37 @@ class ProjectListWidget(QtWidgets.QWidget): if self.current_project == new_project_name: return + save_changes = False + change_project = False if self.validate_context_change(): + change_project = True + + else: + dialog = UnsavedChangesDialog(self) + result = dialog.exec_() + if result == 1: + save_changes = True + change_project = True + + elif result == 2: + change_project = True + + if save_changes: + self._parent._save() + + if change_project: self.select_project(new_project_name) self.current_project = new_project_name self.project_changed.emit() - return - - self.select_project(self.current_project) + else: + self.select_project(self.current_project) def validate_context_change(self): # TODO add check if project can be changed (is modified) + for item in self._parent.input_fields: + is_modified = item.child_modified + if is_modified: + return False return True def project_name(self): From 2b07663fee86e91ebf6d21ad4635eea0077f04ec Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 13 Aug 2020 11:41:56 +0200 Subject: [PATCH 071/813] added "is_file" key to schemas --- .../config_gui_schema/applications_gui_schema.json | 1 + .../config_gui_schema/ftrack_projects_gui_schema.json | 2 ++ .../config_setting/config_gui_schema/plugins_gui_schema.json | 1 + .../config_setting/config_gui_schema/test_project_schema.json | 1 + .../config_setting/config_gui_schema/tools_gui_schema.json | 1 + 5 files changed, 6 insertions(+) diff --git a/pype/tools/config_setting/config_gui_schema/applications_gui_schema.json b/pype/tools/config_setting/config_gui_schema/applications_gui_schema.json index 096964b5b8..5c20e630fa 100644 --- a/pype/tools/config_setting/config_gui_schema/applications_gui_schema.json +++ b/pype/tools/config_setting/config_gui_schema/applications_gui_schema.json @@ -2,6 +2,7 @@ "key": "applications", "type": "dict-expanding", "label": "Applications", + "is_file": true, "children": [ { "type": "dict-form", diff --git a/pype/tools/config_setting/config_gui_schema/ftrack_projects_gui_schema.json b/pype/tools/config_setting/config_gui_schema/ftrack_projects_gui_schema.json index 4a35fc9b61..ac696d18d0 100644 --- a/pype/tools/config_setting/config_gui_schema/ftrack_projects_gui_schema.json +++ b/pype/tools/config_setting/config_gui_schema/ftrack_projects_gui_schema.json @@ -8,6 +8,7 @@ "type": "dict-expanding", "label": "Status updates", "is_group": true, + "is_file": true, "children": [ { "key": "Ready", @@ -24,6 +25,7 @@ "type": "dict-expanding", "label": "Version status to Task status", "is_group": true, + "is_file": true, "children": [ { "key": "in progress", diff --git a/pype/tools/config_setting/config_gui_schema/plugins_gui_schema.json b/pype/tools/config_setting/config_gui_schema/plugins_gui_schema.json index 6c83fee172..1eaecbb8e5 100644 --- a/pype/tools/config_setting/config_gui_schema/plugins_gui_schema.json +++ b/pype/tools/config_setting/config_gui_schema/plugins_gui_schema.json @@ -13,6 +13,7 @@ "type": "dict-expanding", "label": "Publish plugins", "is_group": true, + "is_file": true, "children": [ { "key": "ValidateModelName", diff --git a/pype/tools/config_setting/config_gui_schema/test_project_schema.json b/pype/tools/config_setting/config_gui_schema/test_project_schema.json index e789b422a3..43c9a647f4 100644 --- a/pype/tools/config_setting/config_gui_schema/test_project_schema.json +++ b/pype/tools/config_setting/config_gui_schema/test_project_schema.json @@ -3,6 +3,7 @@ "type": "dict-expanding", "label": "Test inputs", "is_group": true, + "is_file": true, "children": [ { "key": "boolean", diff --git a/pype/tools/config_setting/config_gui_schema/tools_gui_schema.json b/pype/tools/config_setting/config_gui_schema/tools_gui_schema.json index 2f46ef0ec5..3507d14a36 100644 --- a/pype/tools/config_setting/config_gui_schema/tools_gui_schema.json +++ b/pype/tools/config_setting/config_gui_schema/tools_gui_schema.json @@ -2,6 +2,7 @@ "key": "tools", "type": "dict-expanding", "label": "Tools", + "is_file": true, "children": [ { "type": "dict-form", From 745e1cff968b70cf52dfd131c1def152b9136112 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 13 Aug 2020 12:09:29 +0200 Subject: [PATCH 072/813] moved gui creation to reset method --- pype/tools/config_setting/widgets/base.py | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/pype/tools/config_setting/widgets/base.py b/pype/tools/config_setting/widgets/base.py index 76fbad06c6..f36e21483e 100644 --- a/pype/tools/config_setting/widgets/base.py +++ b/pype/tools/config_setting/widgets/base.py @@ -88,11 +88,6 @@ class StudioWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.content_layout = content_layout self.content_widget = content_widget - values = {"studio": config.studio_presets()} - schema = config.gui_schema("studio_gui_schema") - self.keys = schema.get("keys", []) - self.add_children_gui(schema, values) - footer_widget = QtWidgets.QWidget() footer_layout = QtWidgets.QHBoxLayout(footer_widget) @@ -111,6 +106,20 @@ class StudioWidget(QtWidgets.QWidget, PypeConfigurationWidget): save_btn.clicked.connect(self._save) + self.reset() + + def reset(self): + if self.content_layout.count() != 0: + for widget in self.input_fields: + self.content_layout.removeWidget(widget) + widget.deleteLater() + self.input_fields.clear() + + values = {"studio": config.studio_presets()} + schema = config.gui_schema("studio_gui_schema") + self.keys = schema.get("keys", []) + self.add_children_gui(schema, values) + def _save(self): all_values = {} for item in self.input_fields: @@ -354,6 +363,9 @@ class ProjectWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.content_layout = content_layout self.content_widget = content_widget + self.reset() + + def reset(self): values = config.global_project_presets() schema = config.gui_schema("project_gui_schema") self.keys = schema.get("keys", []) From 20c6ef6a6c5648e70780d16345ad7abf276da18a Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 13 Aug 2020 12:37:47 +0200 Subject: [PATCH 073/813] separate gui schemas byt studio and project --- .../{ => projects_schema}/ftrack_projects_gui_schema.json | 0 .../{ => projects_schema}/plugins_gui_schema.json | 0 .../{ => projects_schema}/project_gui_schema.json | 0 .../{ => projects_schema}/test_project_schema.json | 0 .../{ => studio_schema}/applications_gui_schema.json | 0 .../config_gui_schema/{ => studio_schema}/studio_gui_schema.json | 0 .../config_gui_schema/{ => studio_schema}/tools_gui_schema.json | 0 7 files changed, 0 insertions(+), 0 deletions(-) rename pype/tools/config_setting/config_gui_schema/{ => projects_schema}/ftrack_projects_gui_schema.json (100%) rename pype/tools/config_setting/config_gui_schema/{ => projects_schema}/plugins_gui_schema.json (100%) rename pype/tools/config_setting/config_gui_schema/{ => projects_schema}/project_gui_schema.json (100%) rename pype/tools/config_setting/config_gui_schema/{ => projects_schema}/test_project_schema.json (100%) rename pype/tools/config_setting/config_gui_schema/{ => studio_schema}/applications_gui_schema.json (100%) rename pype/tools/config_setting/config_gui_schema/{ => studio_schema}/studio_gui_schema.json (100%) rename pype/tools/config_setting/config_gui_schema/{ => studio_schema}/tools_gui_schema.json (100%) diff --git a/pype/tools/config_setting/config_gui_schema/ftrack_projects_gui_schema.json b/pype/tools/config_setting/config_gui_schema/projects_schema/ftrack_projects_gui_schema.json similarity index 100% rename from pype/tools/config_setting/config_gui_schema/ftrack_projects_gui_schema.json rename to pype/tools/config_setting/config_gui_schema/projects_schema/ftrack_projects_gui_schema.json diff --git a/pype/tools/config_setting/config_gui_schema/plugins_gui_schema.json b/pype/tools/config_setting/config_gui_schema/projects_schema/plugins_gui_schema.json similarity index 100% rename from pype/tools/config_setting/config_gui_schema/plugins_gui_schema.json rename to pype/tools/config_setting/config_gui_schema/projects_schema/plugins_gui_schema.json diff --git a/pype/tools/config_setting/config_gui_schema/project_gui_schema.json b/pype/tools/config_setting/config_gui_schema/projects_schema/project_gui_schema.json similarity index 100% rename from pype/tools/config_setting/config_gui_schema/project_gui_schema.json rename to pype/tools/config_setting/config_gui_schema/projects_schema/project_gui_schema.json diff --git a/pype/tools/config_setting/config_gui_schema/test_project_schema.json b/pype/tools/config_setting/config_gui_schema/projects_schema/test_project_schema.json similarity index 100% rename from pype/tools/config_setting/config_gui_schema/test_project_schema.json rename to pype/tools/config_setting/config_gui_schema/projects_schema/test_project_schema.json diff --git a/pype/tools/config_setting/config_gui_schema/applications_gui_schema.json b/pype/tools/config_setting/config_gui_schema/studio_schema/applications_gui_schema.json similarity index 100% rename from pype/tools/config_setting/config_gui_schema/applications_gui_schema.json rename to pype/tools/config_setting/config_gui_schema/studio_schema/applications_gui_schema.json diff --git a/pype/tools/config_setting/config_gui_schema/studio_gui_schema.json b/pype/tools/config_setting/config_gui_schema/studio_schema/studio_gui_schema.json similarity index 100% rename from pype/tools/config_setting/config_gui_schema/studio_gui_schema.json rename to pype/tools/config_setting/config_gui_schema/studio_schema/studio_gui_schema.json diff --git a/pype/tools/config_setting/config_gui_schema/tools_gui_schema.json b/pype/tools/config_setting/config_gui_schema/studio_schema/tools_gui_schema.json similarity index 100% rename from pype/tools/config_setting/config_gui_schema/tools_gui_schema.json rename to pype/tools/config_setting/config_gui_schema/studio_schema/tools_gui_schema.json From 17c1587631a9052c3a350b88b990d42a6e4312a7 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 13 Aug 2020 12:38:25 +0200 Subject: [PATCH 074/813] gui_schema function also resolves inner schemas so inputs don't have to acrea about them --- pype/tools/config_setting/widgets/config.py | 56 +++++++++++++++++---- 1 file changed, 45 insertions(+), 11 deletions(-) diff --git a/pype/tools/config_setting/widgets/config.py b/pype/tools/config_setting/widgets/config.py index 58b1e03a25..5aaa0e20ee 100644 --- a/pype/tools/config_setting/widgets/config.py +++ b/pype/tools/config_setting/widgets/config.py @@ -220,19 +220,53 @@ def project_presets(project_name=None, **kwargs): return apply_overrides(global_presets, project_overrides) -def gui_schema(schema_name): - filename = schema_name + ".json" - schema_folder = os.path.join( +def replace_inner_schemas(schema_data, schema_collection): + if schema_data["type"] == "schema": + raise ValueError("First item in schema data can't be schema.") + + children = schema_data.get("children") + if not children: + return schema_data + + new_children = [] + for child in children: + if child["type"] != "schema": + new_child = replace_inner_schemas(child, schema_collection) + new_children.append(new_child) + continue + + for schema_name in child["children"]: + new_child = replace_inner_schemas( + schema_collection[schema_name], + schema_collection + ) + new_children.append(new_child) + + schema_data["children"] = new_children + return schema_data + + +def gui_schema(subfolder, main_schema_name): + subfolder, main_schema_name + dirpath = os.path.join( os.path.dirname(os.path.dirname(__file__)), "config_gui_schema", - filename + subfolder ) - with open(schema_folder, "r") as json_stream: - schema = json.load(json_stream) - return schema + loaded_schemas = {} + for filename in os.listdir(dirpath): + basename, ext = os.path.splitext(filename) + if ext != ".json": + continue -p1 = studio_presets(with_metadata=True) -p2 = studio_presets(with_metadata=False) -print(json.dumps(p1, indent=4)) -print(json.dumps(p2, indent=4)) + filepath = os.path.join(dirpath, filename) + with open(filepath, "r") as json_stream: + schema_data = json.load(json_stream) + loaded_schemas[basename] = schema_data + + main_schema = replace_inner_schemas( + loaded_schemas[main_schema_name], + loaded_schemas + ) + return main_schema From c5730f915aa014f826e159fed9cb80dd8d8ecc32 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 13 Aug 2020 12:38:42 +0200 Subject: [PATCH 075/813] project and studio widgets use new gui_schema function --- pype/tools/config_setting/widgets/base.py | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/pype/tools/config_setting/widgets/base.py b/pype/tools/config_setting/widgets/base.py index f36e21483e..134735974c 100644 --- a/pype/tools/config_setting/widgets/base.py +++ b/pype/tools/config_setting/widgets/base.py @@ -56,10 +56,6 @@ class PypeConfigurationWidget: class StudioWidget(QtWidgets.QWidget, PypeConfigurationWidget): - config_dir = os.path.join( - os.path.dirname(os.path.dirname(__file__)), - "config_gui_schema" - ) is_overidable = False is_overriden = False is_group = False @@ -116,7 +112,7 @@ class StudioWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.input_fields.clear() values = {"studio": config.studio_presets()} - schema = config.gui_schema("studio_gui_schema") + schema = config.gui_schema("studio_schema", "studio_gui_schema") self.keys = schema.get("keys", []) self.add_children_gui(schema, values) @@ -301,10 +297,6 @@ class ProjectListWidget(QtWidgets.QWidget): class ProjectWidget(QtWidgets.QWidget, PypeConfigurationWidget): - config_dir = os.path.join( - os.path.dirname(os.path.dirname(__file__)), - "config_gui_schema" - ) is_overriden = False is_group = False any_parent_is_group = False @@ -367,7 +359,7 @@ class ProjectWidget(QtWidgets.QWidget, PypeConfigurationWidget): def reset(self): values = config.global_project_presets() - schema = config.gui_schema("project_gui_schema") + schema = config.gui_schema("projects_schema", "project_gui_schema") self.keys = schema.get("keys", []) self.add_children_gui(schema, values) From ea433367ce44f9fdf4f03245ca1347aa37852482 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 13 Aug 2020 12:42:59 +0200 Subject: [PATCH 076/813] minor changes --- .../config_gui_schema/studio_schema/applications_gui_schema.json | 1 + .../config_gui_schema/studio_schema/studio_gui_schema.json | 1 + .../config_gui_schema/studio_schema/tools_gui_schema.json | 1 + 3 files changed, 3 insertions(+) diff --git a/pype/tools/config_setting/config_gui_schema/studio_schema/applications_gui_schema.json b/pype/tools/config_setting/config_gui_schema/studio_schema/applications_gui_schema.json index 5c20e630fa..12fbb3cc26 100644 --- a/pype/tools/config_setting/config_gui_schema/studio_schema/applications_gui_schema.json +++ b/pype/tools/config_setting/config_gui_schema/studio_schema/applications_gui_schema.json @@ -3,6 +3,7 @@ "type": "dict-expanding", "label": "Applications", "is_file": true, + "is_group": true, "children": [ { "type": "dict-form", diff --git a/pype/tools/config_setting/config_gui_schema/studio_schema/studio_gui_schema.json b/pype/tools/config_setting/config_gui_schema/studio_schema/studio_gui_schema.json index 1a49735b8a..ba017760f3 100644 --- a/pype/tools/config_setting/config_gui_schema/studio_schema/studio_gui_schema.json +++ b/pype/tools/config_setting/config_gui_schema/studio_schema/studio_gui_schema.json @@ -17,6 +17,7 @@ }, { "key": "muster", "type": "dict-invisible", + "is_group": true, "children": [{ "key": "templates_mapping", "label": "Muster", diff --git a/pype/tools/config_setting/config_gui_schema/studio_schema/tools_gui_schema.json b/pype/tools/config_setting/config_gui_schema/studio_schema/tools_gui_schema.json index 3507d14a36..4c905a3826 100644 --- a/pype/tools/config_setting/config_gui_schema/studio_schema/tools_gui_schema.json +++ b/pype/tools/config_setting/config_gui_schema/studio_schema/tools_gui_schema.json @@ -2,6 +2,7 @@ "key": "tools", "type": "dict-expanding", "label": "Tools", + "is_group": true, "is_file": true, "children": [ { From 7f39070df4f468ff6568d4b6f16aa86c807f1431 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 13 Aug 2020 13:12:34 +0200 Subject: [PATCH 077/813] enhance expanding dict states --- pype/tools/config_setting/widgets/inputs.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/pype/tools/config_setting/widgets/inputs.py b/pype/tools/config_setting/widgets/inputs.py index 839fe1e8a2..2c231e087a 100644 --- a/pype/tools/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/widgets/inputs.py @@ -1271,7 +1271,7 @@ class DictExpandWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.any_parent_is_group = any_parent_is_group - self.is_modified = False + self._is_modified = False self._is_overriden = False self.is_group = is_group @@ -1394,9 +1394,9 @@ class DictExpandWidget(QtWidgets.QWidget, PypeConfigurationWidget): if self.is_group: if self.is_overidable: self._is_overriden = True + # TODO update items if item is not None: - is_overriden = self.is_overriden for _item in self.input_fields: if _item is not item: _item.update_style() @@ -1416,12 +1416,7 @@ class DictExpandWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.style().polish(self) self._child_state = child_state - is_overriden = self.is_overriden - if child_modified and not is_overriden: - state = self.default_state - else: - state = self.style_state(is_overriden, child_modified) - + state = self.style_state(self.is_overriden, self.is_modified) if self._state == state: return @@ -1430,6 +1425,12 @@ class DictExpandWidget(QtWidgets.QWidget, PypeConfigurationWidget): self._state = state + @property + def is_modified(self): + if self.is_group: + return self.child_modified + return False + @property def child_modified(self): for input_field in self.input_fields: From de62bc42d2680fdc5151cc5857dc3214d9a612b3 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 13 Aug 2020 15:15:15 +0200 Subject: [PATCH 078/813] added schema validations --- .../studio_schema/studio_gui_schema.json | 1 + pype/tools/config_setting/widgets/config.py | 52 ++++++++++++++++++- 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/pype/tools/config_setting/config_gui_schema/studio_schema/studio_gui_schema.json b/pype/tools/config_setting/config_gui_schema/studio_schema/studio_gui_schema.json index ba017760f3..088c407804 100644 --- a/pype/tools/config_setting/config_gui_schema/studio_schema/studio_gui_schema.json +++ b/pype/tools/config_setting/config_gui_schema/studio_schema/studio_gui_schema.json @@ -21,6 +21,7 @@ "children": [{ "key": "templates_mapping", "label": "Muster", + "is_file": true, "type": "dict-modifiable", "object_type": "int" }] diff --git a/pype/tools/config_setting/widgets/config.py b/pype/tools/config_setting/widgets/config.py index 5aaa0e20ee..3604316131 100644 --- a/pype/tools/config_setting/widgets/config.py +++ b/pype/tools/config_setting/widgets/config.py @@ -132,8 +132,6 @@ def load_jsons_from_dir(path, *args, **kwargs): sub_keys.pop(0) base_len = len(path) + 1 - ext_len = len(".json") - for base, _directories, filenames in os.walk(path): for filename in filenames: basename, ext = os.path.splitext(filename) @@ -246,6 +244,55 @@ def replace_inner_schemas(schema_data, schema_collection): return schema_data +class ShemaMissingFileInfo(Exception): + def __init__(self, invalid): + full_path_keys = [] + for item in invalid: + full_path_keys.append("\"{}\"".format("/".join(item))) + + msg = ( + "Schema has missing definition of output file (\"is_file\" key)" + " for keys. [{}]" + ).format(", ".join(full_path_keys)) + super(ShemaMissingFileInfo, self).__init__(msg) + + +def validate_all_has_ending_file(schema_data, is_top=True): + if schema_data.get("is_file"): + return None + + children = schema_data.get("children") + if not children: + return [[schema_data["key"]]] + + invalid = [] + keyless = "key" not in schema_data + for child in children: + result = validate_all_has_ending_file(child, False) + if result is None: + continue + + if keyless: + invalid.extend(result) + else: + for item in result: + new_invalid = [schema_data["key"]] + new_invalid.extend(item) + invalid.append(new_invalid) + + if not invalid: + return None + + if not is_top: + return invalid + + raise ShemaMissingFileInfo(invalid) + + +def validate_schema(schema_data): + validate_all_has_ending_file(schema_data) + + def gui_schema(subfolder, main_schema_name): subfolder, main_schema_name dirpath = os.path.join( @@ -269,4 +316,5 @@ def gui_schema(subfolder, main_schema_name): loaded_schemas[main_schema_name], loaded_schemas ) + validate_schema(main_schema) return main_schema From e2fdb92a97296c10fb796113c6bcff4f7de93afa Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 13 Aug 2020 15:52:05 +0200 Subject: [PATCH 079/813] changed way of storing data --- pype/tools/config_setting/widgets/base.py | 59 ++++++++++++----------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/pype/tools/config_setting/widgets/base.py b/pype/tools/config_setting/widgets/base.py index 134735974c..830902f6bb 100644 --- a/pype/tools/config_setting/widgets/base.py +++ b/pype/tools/config_setting/widgets/base.py @@ -115,6 +115,7 @@ class StudioWidget(QtWidgets.QWidget, PypeConfigurationWidget): schema = config.gui_schema("studio_schema", "studio_gui_schema") self.keys = schema.get("keys", []) self.add_children_gui(schema, values) + self.schema = schema def _save(self): all_values = {} @@ -129,38 +130,38 @@ class StudioWidget(QtWidgets.QWidget, PypeConfigurationWidget): all_values = all_values["studio"] # Load studio data with metadata - config_with_metadata = config.studio_presets_with_metadata() + current_presets = config.studio_presets() - print(json.dumps(config_with_metadata, indent=4)) + print(json.dumps(current_presets, indent=4)) print(json.dumps(all_values, indent=4)) - per_file_values = {} - process_queue = Queue() - for _key, _values in all_values.items(): - process_queue.put(( - config.studio_presets_path, _key, config_with_metadata, _values - )) - - while not process_queue.empty(): - path, key, metadata, values = process_queue.get() - new_path = os.path.join(path, key) - # TODO this should not be - if key in metadata: - key_metadata = metadata[key] - - if key_metadata["type"] == "file": - new_path += ".json" - per_file_values[new_path] = values - continue - - for new_key, new_values in values.items(): - process_queue.put( - (new_path, new_key, key_metadata["value"], new_values) - ) - - for file_path, file_values in per_file_values.items(): - with open(file_path, "w") as file_stream: - json.dump(file_values, file_stream, indent=4) + # per_file_values = {} + # process_queue = Queue() + # for _key, _values in all_values.items(): + # process_queue.put(( + # config.studio_presets_path, _key, config_with_metadata, _values + # )) + # + # while not process_queue.empty(): + # path, key, metadata, values = process_queue.get() + # new_path = os.path.join(path, key) + # # TODO this should not be + # if key in metadata: + # key_metadata = metadata[key] + # + # if key_metadata["type"] == "file": + # new_path += ".json" + # per_file_values[new_path] = values + # continue + # + # for new_key, new_values in values.items(): + # process_queue.put( + # (new_path, new_key, key_metadata["value"], new_values) + # ) + # + # for file_path, file_values in per_file_values.items(): + # with open(file_path, "w") as file_stream: + # json.dump(file_values, file_stream, indent=4) def add_children_gui(self, child_configuration, values): item_type = child_configuration["type"] From cf971151b2fc7270a2e851bddb2f01fdc900b82d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 13 Aug 2020 18:00:51 +0200 Subject: [PATCH 080/813] studio can again safe changes --- pype/tools/config_setting/widgets/base.py | 55 ++++++++++----------- pype/tools/config_setting/widgets/config.py | 24 +++++++++ pype/tools/config_setting/widgets/inputs.py | 2 +- 3 files changed, 51 insertions(+), 30 deletions(-) diff --git a/pype/tools/config_setting/widgets/base.py b/pype/tools/config_setting/widgets/base.py index 830902f6bb..3ca3b910b0 100644 --- a/pype/tools/config_setting/widgets/base.py +++ b/pype/tools/config_setting/widgets/base.py @@ -1,5 +1,6 @@ import os import json +import copy from Qt import QtWidgets, QtCore, QtGui from . import config from .widgets import UnsavedChangesDialog @@ -132,36 +133,32 @@ class StudioWidget(QtWidgets.QWidget, PypeConfigurationWidget): # Load studio data with metadata current_presets = config.studio_presets() - print(json.dumps(current_presets, indent=4)) - print(json.dumps(all_values, indent=4)) + output = {} + keys_to_file = config.file_keys_from_schema(self.schema) + for key_sequence in keys_to_file: + key_sequence = key_sequence[1:] + subpath = "/".join(key_sequence) + ".json" + origin_values = current_presets + for key in key_sequence: + if key not in origin_values: + origin_values = {} + break + origin_values = origin_values[key] - # per_file_values = {} - # process_queue = Queue() - # for _key, _values in all_values.items(): - # process_queue.put(( - # config.studio_presets_path, _key, config_with_metadata, _values - # )) - # - # while not process_queue.empty(): - # path, key, metadata, values = process_queue.get() - # new_path = os.path.join(path, key) - # # TODO this should not be - # if key in metadata: - # key_metadata = metadata[key] - # - # if key_metadata["type"] == "file": - # new_path += ".json" - # per_file_values[new_path] = values - # continue - # - # for new_key, new_values in values.items(): - # process_queue.put( - # (new_path, new_key, key_metadata["value"], new_values) - # ) - # - # for file_path, file_values in per_file_values.items(): - # with open(file_path, "w") as file_stream: - # json.dump(file_values, file_stream, indent=4) + new_values = all_values + for key in key_sequence: + new_values = new_values[key] + origin_values.update(new_values) + + output_path = os.path.join( + config.studio_presets_path, subpath + ) + dirpath = os.path.dirname(output_path) + if not os.path.exists(dirpath): + os.makedirs(dirpath) + + with open(output_path, "w") as file_stream: + json.dump(origin_values, file_stream, indent=4) def add_children_gui(self, child_configuration, values): item_type = child_configuration["type"] diff --git a/pype/tools/config_setting/widgets/config.py b/pype/tools/config_setting/widgets/config.py index 3604316131..8319c3d51d 100644 --- a/pype/tools/config_setting/widgets/config.py +++ b/pype/tools/config_setting/widgets/config.py @@ -257,6 +257,27 @@ class ShemaMissingFileInfo(Exception): super(ShemaMissingFileInfo, self).__init__(msg) +def file_keys_from_schema(schema_data): + output = [] + keys = [] + key = schema_data.get("key") + if key: + keys.append(key) + + for child in schema_data["children"]: + if child.get("is_file"): + _keys = copy.deepcopy(keys) + _keys.append(child["key"]) + output.append(_keys) + continue + + for result in file_keys_from_schema(child): + _keys = copy.deepcopy(keys) + _keys.extend(result) + output.append(_keys) + return output + + def validate_all_has_ending_file(schema_data, is_top=True): if schema_data.get("is_file"): return None @@ -290,6 +311,9 @@ def validate_all_has_ending_file(schema_data, is_top=True): def validate_schema(schema_data): + # TODO validator for key uniquenes + # TODO validator that is_group key is not before is_file child + # TODO validator that is_group or is_file is not on child without key validate_all_has_ending_file(schema_data) diff --git a/pype/tools/config_setting/widgets/inputs.py b/pype/tools/config_setting/widgets/inputs.py index 2c231e087a..c4ec7a4347 100644 --- a/pype/tools/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/widgets/inputs.py @@ -16,7 +16,7 @@ class SchemeGroupHierarchyBug(Exception): if not msg: # TODO better message msg = "SCHEME BUG: Attribute `is_group` is mixed in the hierarchy" - super(SchemeGroupHierarchyBug, self).__init(msg) + super(SchemeGroupHierarchyBug, self).__init__(msg) class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): From e9f4d1989019fb3f3e159eae734c21627982722f Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 13 Aug 2020 17:08:46 +0100 Subject: [PATCH 081/813] Explicit optional isolate attribute. --- pype/plugins/maya/create/create_review.py | 2 ++ pype/plugins/maya/publish/extract_playblast.py | 2 +- pype/plugins/maya/publish/extract_thumbnail.py | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pype/plugins/maya/create/create_review.py b/pype/plugins/maya/create/create_review.py index 3e513032e1..6b153396d6 100644 --- a/pype/plugins/maya/create/create_review.py +++ b/pype/plugins/maya/create/create_review.py @@ -21,4 +21,6 @@ class CreateReview(avalon.maya.Creator): for key, value in animation_data.items(): data[key] = value + data["isolate"] = False + self.data = data diff --git a/pype/plugins/maya/publish/extract_playblast.py b/pype/plugins/maya/publish/extract_playblast.py index 3c9811d4c4..91849567e3 100644 --- a/pype/plugins/maya/publish/extract_playblast.py +++ b/pype/plugins/maya/publish/extract_playblast.py @@ -79,7 +79,7 @@ class ExtractPlayblast(pype.api.Extractor): # Isolate view is requested by having objects in the set besides a # camera. - if len(instance.data["setMembers"]) > 1: + if instance.data.get("isolate"): preset["isolate"] = instance.data["setMembers"] with maintained_time(): diff --git a/pype/plugins/maya/publish/extract_thumbnail.py b/pype/plugins/maya/publish/extract_thumbnail.py index 2edd19a559..524fc1e17c 100644 --- a/pype/plugins/maya/publish/extract_thumbnail.py +++ b/pype/plugins/maya/publish/extract_thumbnail.py @@ -79,7 +79,7 @@ class ExtractThumbnail(pype.api.Extractor): # Isolate view is requested by having objects in the set besides a # camera. - if len(instance.data["setMembers"]) > 1: + if instance.data.get("isolate"): preset["isolate"] = instance.data["setMembers"] with maintained_time(): From 8b7d565208bf5ea76db9330c7a43b6e6f5669930 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 13 Aug 2020 18:13:10 +0200 Subject: [PATCH 082/813] smaller changes --- .../config/studio_presets/global/applications.json | 8 ++++++-- .../config/studio_presets/muster/templates_mapping.json | 2 +- pype/tools/config_setting/widgets/base.py | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/pype/tools/config_setting/config/studio_presets/global/applications.json b/pype/tools/config_setting/config/studio_presets/global/applications.json index 35e399444c..21693e5fee 100644 --- a/pype/tools/config_setting/config/studio_presets/global/applications.json +++ b/pype/tools/config_setting/config/studio_presets/global/applications.json @@ -35,5 +35,9 @@ "resolve_16": true, "shell": true, "storyboardpro_7": true, - "unreal_4.21": true -} + "unreal_4.21": true, + "celaction_local": true, + "celaction_remote": true, + "houdini_16.5": false, + "unreal_4.24": true +} \ No newline at end of file diff --git a/pype/tools/config_setting/config/studio_presets/muster/templates_mapping.json b/pype/tools/config_setting/config/studio_presets/muster/templates_mapping.json index 4edab9077d..0c09113515 100644 --- a/pype/tools/config_setting/config/studio_presets/muster/templates_mapping.json +++ b/pype/tools/config_setting/config/studio_presets/muster/templates_mapping.json @@ -16,4 +16,4 @@ "vector": 4, "vray": 37, "ffmpeg": 48 -} +} \ No newline at end of file diff --git a/pype/tools/config_setting/widgets/base.py b/pype/tools/config_setting/widgets/base.py index 3ca3b910b0..14ff56aa2a 100644 --- a/pype/tools/config_setting/widgets/base.py +++ b/pype/tools/config_setting/widgets/base.py @@ -133,9 +133,9 @@ class StudioWidget(QtWidgets.QWidget, PypeConfigurationWidget): # Load studio data with metadata current_presets = config.studio_presets() - output = {} keys_to_file = config.file_keys_from_schema(self.schema) for key_sequence in keys_to_file: + # Skip first key key_sequence = key_sequence[1:] subpath = "/".join(key_sequence) + ".json" origin_values = current_presets From 507be4926965271d5ab0db6f078ba1918ad7b656 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Sat, 15 Aug 2020 00:09:04 +0200 Subject: [PATCH 083/813] tile rendering support in pype --- .../global/publish/submit_publish_job.py | 11 +- pype/plugins/maya/create/create_render.py | 2 + pype/plugins/maya/publish/collect_render.py | 2 + .../maya/publish/submit_maya_deadline.py | 284 +++++++++++++++++- 4 files changed, 280 insertions(+), 19 deletions(-) diff --git a/pype/plugins/global/publish/submit_publish_job.py b/pype/plugins/global/publish/submit_publish_job.py index 3053c80b11..d106175eb6 100644 --- a/pype/plugins/global/publish/submit_publish_job.py +++ b/pype/plugins/global/publish/submit_publish_job.py @@ -718,7 +718,8 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): "pixelAspect": data.get("pixelAspect", 1), "resolutionWidth": data.get("resolutionWidth", 1920), "resolutionHeight": data.get("resolutionHeight", 1080), - "multipartExr": data.get("multipartExr", False) + "multipartExr": data.get("multipartExr", False), + "jobBatchName": data.get("jobBatchName", "") } if "prerender" in instance.data["families"]: @@ -895,8 +896,12 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): # We still use data from it so lets fake it. # # Batch name reflect original scene name - render_job["Props"]["Batch"] = os.path.splitext(os.path.basename( - context.data.get("currentFile")))[0] + + if instance.data.get("assemblySubmissionJob"): + render_job["Props"]["Batch"] = instance.data.get("jobBatchName") + else: + render_job["Props"]["Batch"] = os.path.splitext( + os.path.basename(context.data.get("currentFile")))[0] # User is deadline user render_job["Props"]["User"] = context.data.get( "deadlineUser", getpass.getuser()) diff --git a/pype/plugins/maya/create/create_render.py b/pype/plugins/maya/create/create_render.py index 9e5f9310ae..5d68c5301a 100644 --- a/pype/plugins/maya/create/create_render.py +++ b/pype/plugins/maya/create/create_render.py @@ -185,6 +185,8 @@ class CreateRender(avalon.maya.Creator): self.data["useMayaBatch"] = False self.data["vrayScene"] = False self.data["tileRendering"] = False + self.data["tilesX"] = 2 + self.data["tilesY"] = 2 # Disable for now as this feature is not working yet # self.data["assScene"] = False diff --git a/pype/plugins/maya/publish/collect_render.py b/pype/plugins/maya/publish/collect_render.py index 5ca9392080..1db7b31c8c 100644 --- a/pype/plugins/maya/publish/collect_render.py +++ b/pype/plugins/maya/publish/collect_render.py @@ -243,6 +243,8 @@ class CollectMayaRender(pyblish.api.ContextPlugin): "resolutionHeight": cmds.getAttr("defaultResolution.height"), "pixelAspect": cmds.getAttr("defaultResolution.pixelAspect"), "tileRendering": render_instance.data.get("tileRendering") or False, # noqa: E501 + "tilesX": render_instance.data.get("tilesX") or 2, + "tilesY": render_instance.data.get("tilesY") or 2, "priority": render_instance.data.get("priority") } diff --git a/pype/plugins/maya/publish/submit_maya_deadline.py b/pype/plugins/maya/publish/submit_maya_deadline.py index 7fe20c779d..d6d4bd2910 100644 --- a/pype/plugins/maya/publish/submit_maya_deadline.py +++ b/pype/plugins/maya/publish/submit_maya_deadline.py @@ -16,11 +16,14 @@ Attributes: """ +from __future__ import print_function import os import json import getpass import copy import re +import hashlib +from datetime import datetime import clique import requests @@ -61,6 +64,91 @@ payload_skeleton = { } +def _format_tiles(filename, index, tiles_x, tiles_y, width, height, prefix): + """Generate tile entries for Deadline tile job. + + Returns two dictionaries - one that can be directly used in Deadline + job, second that can be used for Deadline Assembly job configuration + file. + + This will format tile names: + + Example:: + { + "OutputFilename0Tile0": "_tile_1x1_4x4_Main_beauty.1001.exr", + "OutputFilename0Tile1": "_tile_2x1_4x4_Main_beauty.1001.exr" + } + + And add tile prefixes like: + + Example:: + Image prefix is: + `maya///_` + + Result for tile 0 for 4x4 will be: + `maya///_tile_1x1_4x4__` + + Calculating coordinates is tricky as in Job they are defined as top, + left, bottom, right with zero being in top-left corner. But Assembler + configuration file takes tile coordinates as X, Y, Width and Height and + zero is bottom left corner. + + Args: + filename (str): Filename to process as tiles. + index (int): Index of that file if it is sequence. + tiles_x (int): Number of tiles in X. + tiles_y (int): Number if tikes in Y. + width (int): Width resolution of final image. + height (int): Height resolution of final image. + prefix (str): Image prefix. + + Returns: + (dict, dict): Tuple of two dictionaires - first can be used to + extend JobInfo, second has tiles x, y, width and height + used for assembler configuration. + + """ + tile = 0 + out = {"JobInfo": {}, "PluginInfo": {}} + cfg = {} + w_space = width / tiles_x + h_space = height / tiles_y + + for tile_x in range(1, tiles_x + 1): + for tile_y in range(1, tiles_y + 1): + tile_prefix = "_tile_{}x{}_{}x{}_".format( + tile_x, tile_y, + tiles_x, + tiles_y + ) + out_tile_index = "OutputFilename{}Tile{}".format( + str(index), tile + ) + new_filename = "{}/{}{}".format( + os.path.dirname(filename), + tile_prefix, + os.path.basename(filename) + ) + out["JobInfo"][out_tile_index] = new_filename + out["PluginInfo"]["RegionPrefix{}".format(str(tile))] = tile_prefix.join( # noqa: E501 + prefix.rsplit("/", 1)) + + out["PluginInfo"]["RegionTop{}".format(tile)] = int(height) - (tile_y * h_space) # noqa: E501 + out["PluginInfo"]["RegionBottom{}".format(tile)] = int(height) - ((tile_y - 1) * h_space) - 1 # noqa: E501 + out["PluginInfo"]["RegionLeft{}".format(tile)] = (tile_x - 1) * w_space # noqa: E501 + out["PluginInfo"]["RegionRight{}".format(tile)] = (tile_x * w_space) - 1 # noqa: E501 + + cfg["Tile{}".format(tile)] = new_filename + cfg["Tile{}Tile".format(tile)] = new_filename + cfg["Tile{}X".format(tile)] = (tile_x - 1) * w_space + cfg["Tile{}Y".format(tile)] = (tile_y - 1) * h_space + cfg["Tile{}Width".format(tile)] = tile_x * w_space + cfg["Tile{}Height".format(tile)] = tile_y * h_space + + tile += 1 + return out, cfg + + def get_renderer_variables(renderlayer, root): """Retrieve the extension which has been set in the VRay settings. @@ -164,6 +252,7 @@ class MayaSubmitDeadline(pyblish.api.InstancePlugin): optional = True use_published = True + tile_assembler_plugin = "DraftTileAssembler" def process(self, instance): """Plugin entry point.""" @@ -309,7 +398,7 @@ class MayaSubmitDeadline(pyblish.api.InstancePlugin): # Optional, enable double-click to preview rendered # frames from Deadline Monitor payload_skeleton["JobInfo"]["OutputDirectory0"] = \ - os.path.dirname(output_filename_0) + os.path.dirname(output_filename_0).replace("\\", "/") payload_skeleton["JobInfo"]["OutputFilename0"] = \ output_filename_0.replace("\\", "/") @@ -376,9 +465,8 @@ class MayaSubmitDeadline(pyblish.api.InstancePlugin): # Add list of expected files to job --------------------------------- exp = instance.data.get("expectedFiles") - - output_filenames = {} exp_index = 0 + output_filenames = {} if isinstance(exp[0], dict): # we have aovs and we need to iterate over them @@ -390,33 +478,202 @@ class MayaSubmitDeadline(pyblish.api.InstancePlugin): assert len(rem) == 1, ("Found multiple non related files " "to render, don't know what to do " "with them.") - payload['JobInfo']['OutputFilename' + str(exp_index)] = rem[0] # noqa: E501 output_file = rem[0] + if not instance.data.get("tileRendering"): + payload['JobInfo']['OutputFilename' + str(exp_index)] = output_file # noqa: E501 else: output_file = col[0].format('{head}{padding}{tail}') - payload['JobInfo']['OutputFilename' + str(exp_index)] = output_file # noqa: E501 - output_filenames[exp_index] = output_file + if not instance.data.get("tileRendering"): + payload['JobInfo']['OutputFilename' + str(exp_index)] = output_file # noqa: E501 + + output_filenames['OutputFilename' + str(exp_index)] = output_file # noqa: E501 exp_index += 1 else: - col, rem = clique.assemble(files) + col, rem = clique.assemble(exp) if not col and rem: # we couldn't find any collections but have # individual files. assert len(rem) == 1, ("Found multiple non related files " "to render, don't know what to do " "with them.") - payload['JobInfo']['OutputFilename' + str(exp_index)] = rem[0] # noqa: E501 + + output_file = rem[0] + if not instance.data.get("tileRendering"): + payload['JobInfo']['OutputFilename' + str(exp_index)] = output_file # noqa: E501 else: output_file = col[0].format('{head}{padding}{tail}') - payload['JobInfo']['OutputFilename' + str(exp_index)] = output_file # noqa: E501 + if not instance.data.get("tileRendering"): + payload['JobInfo']['OutputFilename' + str(exp_index)] = output_file # noqa: E501 + + output_filenames['OutputFilename' + str(exp_index)] = output_file plugin = payload["JobInfo"]["Plugin"] self.log.info("using render plugin : {}".format(plugin)) + # Store output dir for unified publisher (filesequence) + instance.data["outputDir"] = os.path.dirname(output_filename_0) + self.preflight_check(instance) - # Submit job to farm ------------------------------------------------ - if not instance.data.get("tileRendering"): + # Prepare tiles data ------------------------------------------------ + if instance.data.get("tileRendering"): + # if we have sequence of files, we need to create tile job for + # every frame + + payload["JobInfo"]["TileJob"] = True + payload["JobInfo"]["TileJobTilesInX"] = instance.data.get("tilesX") + payload["JobInfo"]["TileJobTilesInY"] = instance.data.get("tilesY") + payload["PluginInfo"]["ImageHeight"] = instance.data.get("resolutionHeight") # noqa: E501 + payload["PluginInfo"]["ImageWidth"] = instance.data.get("resolutionWidth") # noqa: E501 + payload["PluginInfo"]["RegionRendering"] = True + + assembly_payload = { + "AuxFiles": [], + "JobInfo": { + "BatchName": payload["JobInfo"]["BatchName"], + "Frames": 0, + "Name": "{} - Tile Assembly Job".format( + payload["JobInfo"]["Name"]), + "OutputDirectory0": + payload["JobInfo"]["OutputDirectory0"].replace( + "\\", "/"), + "Plugin": self.tile_assembler_plugin, + "MachineLimit": 1 + }, + "PluginInfo": { + "CleanupTiles": 1, + "ErrorOnMissing": True + } + } + assembly_payload["JobInfo"].update(output_filenames) + + frame_payloads = [] + assembly_payloads = {} + + R_FRAME_NUMBER = re.compile(r".+\.(?P[0-9]+)\..+") # noqa: N806, E501 + REPL_FRAME_NUMBER = re.compile(r"(.+\.)([0-9]+)(\..+)") # noqa: N806, E501 + + if isinstance(exp[0], dict): + # we have aovs and we need to iterate over them + # get files from `beauty` + files = exp[0].get("beauty") + if not files: + # if beauty doesn't exists, use first aov we found + files = exp[0].get(list(exp[0].keys())[0]) + else: + files = exp + + file_index = 1 + for file in files: + frame = re.search(R_FRAME_NUMBER, file).group("frame") + new_payload = copy.copy(payload) + new_payload["JobInfo"]["Name"] = \ + "{} (Frame {} - {} tiles)".format( + new_payload["JobInfo"]["Name"], + frame, + instance.data.get("tilesX") * instance.data.get("tilesY") # noqa: E501 + ) + new_payload["JobInfo"]["TileJobFrame"] = frame + + tiles_data = _format_tiles( + file, 0, + instance.data.get("tilesX"), + instance.data.get("tilesY"), + instance.data.get("resolutionWidth"), + instance.data.get("resolutionHeight"), + payload["PluginInfo"]["OutputFilePrefix"] + )[0] + new_payload["JobInfo"].update(tiles_data["JobInfo"]) + new_payload["PluginInfo"].update(tiles_data["PluginInfo"]) + + job_hash = hashlib.sha256("{}_{}".format(file_index, file)) + new_payload["JobInfo"]["ExtraInfo0"] = job_hash.hexdigest() + new_payload["JobInfo"]["ExtraInfo1"] = file + + frame_payloads.append(new_payload) + + new_assembly_payload = copy.copy(assembly_payload) + new_assembly_payload["JobInfo"]["OutputFilename0"] = re.sub( + REPL_FRAME_NUMBER, + "\\1{}\\3".format("#" * len(frame)), file) + + new_assembly_payload["JobInfo"]["ExtraInfo0"] = job_hash.hexdigest() # noqa: E501 + new_assembly_payload["JobInfo"]["ExtraInfo1"] = file + assembly_payloads[job_hash.hexdigest()] = new_assembly_payload + file_index += 1 + + self.log.info( + "Submitting tile job(s) [{}] ...".format(len(frame_payloads))) + + url = "{}/api/jobs".format(self._deadline_url) + tiles_count = instance.data.get("tilesX") * instance.data.get("tilesY") # noqa: E501 + + for tile_job in frame_payloads: + response = self._requests_post(url, json=tile_job) + if not response.ok: + raise Exception(response.text) + + job_id = response.json()["_id"] + hash = response.json()["Props"]["Ex0"] + file = response.json()["Props"]["Ex1"] + assembly_payloads[hash]["JobInfo"]["JobDependency0"] = job_id + + # write assembly job config files + now = datetime.now() + + config_file = os.path.join( + os.path.dirname(output_filename_0), + "{}_config_{}.txt".format( + os.path.splitext(file)[0], + now.strftime("%Y_%m_%d_%H_%M_%S") + ) + ) + + try: + if not os.path.isdir(os.path.dirname(config_file)): + os.makedirs(os.path.dirname(config_file)) + except OSError: + # directory is not available + self.log.warning( + "Path is unreachable: `{}`".format( + os.path.dirname(config_file))) + + with open(config_file, "w") as cf: + print("TileCount={}".format(tiles_count), file=cf) + print("ImageFileName={}".format(file), file=cf) + print("ImageWidth={}".format( + instance.data.get("resolutionWidth")), file=cf) + print("ImageHeight={}".format( + instance.data.get("resolutionHeight")), file=cf) + + tiles = _format_tiles( + file, 0, + instance.data.get("tilesX"), + instance.data.get("tilesY"), + instance.data.get("resolutionWidth"), + instance.data.get("resolutionHeight"), + payload["PluginInfo"]["OutputFilePrefix"] + )[1] + sorted(tiles) + for k, v in tiles.items(): + print("{}={}".format(k, v), file=cf) + + self.log.debug(json.dumps(assembly_payloads, + indent=4, sort_keys=True)) + self.log.info( + "Submitting assembly job(s) [{}] ...".format(len(assembly_payloads))) # noqa: E501 + url = "{}/api/jobs".format(self._deadline_url) + response = self._requests_post(url, json={ + "Jobs": list(assembly_payloads.values()), + "AuxFiles": [] + }) + if not response.ok: + raise Exception(response) + + instance.data["assemblySubmissionJob"] = assembly_payloads + instance.data["jobBatchName"] = payload["JobInfo"]["BatchName"] + else: + # Submit job to farm -------------------------------------------- self.log.info("Submitting ...") self.log.debug(json.dumps(payload, indent=4, sort_keys=True)) @@ -426,11 +683,6 @@ class MayaSubmitDeadline(pyblish.api.InstancePlugin): if not response.ok: raise Exception(response.text) instance.data["deadlineSubmissionJob"] = response.json() - else: - self.log.info("Skipping submission, tile rendering enabled.") - - # Store output dir for unified publisher (filesequence) - instance.data["outputDir"] = os.path.dirname(output_filename_0) def _get_maya_payload(self, data): payload = copy.deepcopy(payload_skeleton) From f52fe3beefcfba1bd0ef744d943aa044a4b83e96 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Mon, 17 Aug 2020 15:31:44 +0100 Subject: [PATCH 084/813] Add "isolate" to reviewable subset --- pype/plugins/maya/publish/collect_review.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pype/plugins/maya/publish/collect_review.py b/pype/plugins/maya/publish/collect_review.py index 063a854bd1..e16b6ae9f9 100644 --- a/pype/plugins/maya/publish/collect_review.py +++ b/pype/plugins/maya/publish/collect_review.py @@ -63,6 +63,7 @@ class CollectReview(pyblish.api.InstancePlugin): data['handles'] = instance.data.get('handles', None) data['step'] = instance.data['step'] data['fps'] = instance.data['fps'] + data["isolate"] = instance.data["isolate"] cmds.setAttr(str(instance) + '.active', 1) self.log.debug('data {}'.format(instance.context[i].data)) instance.context[i].data.update(data) From 71bfa3774a944a65201bf779bc5335d61b6c57ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Mon, 17 Aug 2020 16:50:29 +0200 Subject: [PATCH 085/813] submit each assembly job separately --- .../global/publish/submit_publish_job.py | 5 ++-- .../maya/publish/submit_maya_deadline.py | 30 +++++++++++-------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/pype/plugins/global/publish/submit_publish_job.py b/pype/plugins/global/publish/submit_publish_job.py index d106175eb6..1a3d0df1b3 100644 --- a/pype/plugins/global/publish/submit_publish_job.py +++ b/pype/plugins/global/publish/submit_publish_job.py @@ -897,8 +897,9 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): # # Batch name reflect original scene name - if instance.data.get("assemblySubmissionJob"): - render_job["Props"]["Batch"] = instance.data.get("jobBatchName") + if instance.data.get("assemblySubmissionJobs"): + render_job["Props"]["Batch"] = instance.data.get( + "jobBatchName") else: render_job["Props"]["Batch"] = os.path.splitext( os.path.basename(context.data.get("currentFile")))[0] diff --git a/pype/plugins/maya/publish/submit_maya_deadline.py b/pype/plugins/maya/publish/submit_maya_deadline.py index d6d4bd2910..afa5496455 100644 --- a/pype/plugins/maya/publish/submit_maya_deadline.py +++ b/pype/plugins/maya/publish/submit_maya_deadline.py @@ -638,6 +638,9 @@ class MayaSubmitDeadline(pyblish.api.InstancePlugin): "Path is unreachable: `{}`".format( os.path.dirname(config_file))) + # add config file as job auxFile + assembly_payloads[hash]["AuxFiles"] = [config_file] + with open(config_file, "w") as cf: print("TileCount={}".format(tiles_count), file=cf) print("ImageFileName={}".format(file), file=cf) @@ -658,20 +661,23 @@ class MayaSubmitDeadline(pyblish.api.InstancePlugin): for k, v in tiles.items(): print("{}={}".format(k, v), file=cf) - self.log.debug(json.dumps(assembly_payloads, - indent=4, sort_keys=True)) - self.log.info( - "Submitting assembly job(s) [{}] ...".format(len(assembly_payloads))) # noqa: E501 - url = "{}/api/jobs".format(self._deadline_url) - response = self._requests_post(url, json={ - "Jobs": list(assembly_payloads.values()), - "AuxFiles": [] - }) - if not response.ok: - raise Exception(response) + job_idx = 1 + instance.data["assemblySubmissionJobs"] = [] + for k, ass_job in assembly_payloads.items(): + self.log.info("submitting assembly job {} of {}".format( + job_idx, len(assembly_payloads) + )) + self.log.debug(json.dumps(ass_job, indent=4, sort_keys=True)) + response = self._requests_post(url, json=ass_job) + if not response.ok: + raise Exception(response.text) + + instance.data["assemblySubmissionJobs"].append(ass_job) + job_idx += 1 - instance.data["assemblySubmissionJob"] = assembly_payloads instance.data["jobBatchName"] = payload["JobInfo"]["BatchName"] + self.log.info("Setting batch name on instance: {}".format( + instance.data["jobBatchName"])) else: # Submit job to farm -------------------------------------------- self.log.info("Submitting ...") From 9d917f5fa42a9075089bb5154e8a2c6a6a102d75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Mon, 17 Aug 2020 17:20:36 +0200 Subject: [PATCH 086/813] delete unused tile validator --- .../validate_deadline_tile_submission.py | 69 ------------------- 1 file changed, 69 deletions(-) delete mode 100644 pype/plugins/maya/publish/validate_deadline_tile_submission.py diff --git a/pype/plugins/maya/publish/validate_deadline_tile_submission.py b/pype/plugins/maya/publish/validate_deadline_tile_submission.py deleted file mode 100644 index b0b995de3e..0000000000 --- a/pype/plugins/maya/publish/validate_deadline_tile_submission.py +++ /dev/null @@ -1,69 +0,0 @@ -# -*- coding: utf-8 -*- -"""Validate settings from Deadline Submitter. - -This is useful mainly for tile rendering, where jobs on farm are created by -submitter script from Maya. - -Unfortunately Deadline doesn't expose frame number for tiles job so that -cannot be validated, even if it is important setting. Also we cannot -determine if 'Region Rendering' (tile rendering) is enabled or not because -of the same thing. - -""" -import os - -from maya import mel -from maya import cmds - -import pyblish.api -from pype.hosts.maya import lib - - -class ValidateDeadlineTileSubmission(pyblish.api.InstancePlugin): - """Validate Deadline Submission settings are OK for tile rendering.""" - - label = "Validate Deadline Tile Submission" - order = pyblish.api.ValidatorOrder - hosts = ["maya"] - families = ["renderlayer"] - if not os.environ.get("DEADLINE_REST_URL"): - active = False - - def process(self, instance): - """Entry point.""" - # try if Deadline submitter was loaded - if mel.eval("exists SubmitJobToDeadline") == 0: - # if not, try to load it manually - try: - mel.eval("source DeadlineMayaClient;") - except RuntimeError: - raise AssertionError("Deadline Maya client cannot be loaded") - mel.eval("DeadlineMayaClient();") - assert mel.eval("exists SubmitJobToDeadline") == 1, ( - "Deadline Submission script cannot be initialized.") - if instance.data.get("tileRendering"): - job_name = cmds.getAttr("defaultRenderGlobals.deadlineJobName") - scene_name = os.path.splitext(os.path.basename( - instance.context.data.get("currentFile")))[0] - if job_name != scene_name: - self.log.warning(("Job submitted through Deadline submitter " - "has different name then current scene " - "{} / {}").format(job_name, scene_name)) - if cmds.getAttr("defaultRenderGlobals.deadlineTileSingleJob") == 1: - layer = instance.data['setMembers'] - anim_override = lib.get_attr_in_layer( - "defaultRenderGlobals.animation", layer=layer) - assert anim_override, ( - "Animation must be enabled in " - "Render Settings even when rendering single frame." - ) - - start_frame = cmds.getAttr("defaultRenderGlobals.startFrame") - end_frame = cmds.getAttr("defaultRenderGlobals.endFrame") - assert start_frame == end_frame, ( - "Start frame and end frame are not equals. When " - "'Submit All Tles As A Single Job' is selected, only " - "single frame is expected to be rendered. It must match " - "the one specified in Deadline Submitter under " - "'Region Rendering'" - ) From 1f55e22c1313cfc5122186b22b90d5d3befd651e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Mon, 17 Aug 2020 17:24:15 +0200 Subject: [PATCH 087/813] fix hound warning --- pype/plugins/maya/publish/submit_maya_deadline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/plugins/maya/publish/submit_maya_deadline.py b/pype/plugins/maya/publish/submit_maya_deadline.py index afa5496455..cf17de3445 100644 --- a/pype/plugins/maya/publish/submit_maya_deadline.py +++ b/pype/plugins/maya/publish/submit_maya_deadline.py @@ -663,7 +663,7 @@ class MayaSubmitDeadline(pyblish.api.InstancePlugin): job_idx = 1 instance.data["assemblySubmissionJobs"] = [] - for k, ass_job in assembly_payloads.items(): + for _k, ass_job in assembly_payloads.items(): self.log.info("submitting assembly job {} of {}".format( job_idx, len(assembly_payloads) )) From c65ab39d330d58009b73c2fecadeb4ce442e8853 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Mon, 17 Aug 2020 21:46:48 +0200 Subject: [PATCH 088/813] fix prefix path for tiles --- pype/plugins/maya/publish/submit_maya_deadline.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pype/plugins/maya/publish/submit_maya_deadline.py b/pype/plugins/maya/publish/submit_maya_deadline.py index cf17de3445..34e4432aa5 100644 --- a/pype/plugins/maya/publish/submit_maya_deadline.py +++ b/pype/plugins/maya/publish/submit_maya_deadline.py @@ -130,8 +130,8 @@ def _format_tiles(filename, index, tiles_x, tiles_y, width, height, prefix): os.path.basename(filename) ) out["JobInfo"][out_tile_index] = new_filename - out["PluginInfo"]["RegionPrefix{}".format(str(tile))] = tile_prefix.join( # noqa: E501 - prefix.rsplit("/", 1)) + out["PluginInfo"]["RegionPrefix{}".format(str(tile))] = \ + "/{}".format(tile_prefix).join(prefix.rsplit("/", 1)) out["PluginInfo"]["RegionTop{}".format(tile)] = int(height) - (tile_y * h_space) # noqa: E501 out["PluginInfo"]["RegionBottom{}".format(tile)] = int(height) - ((tile_y - 1) * h_space) - 1 # noqa: E501 @@ -252,7 +252,7 @@ class MayaSubmitDeadline(pyblish.api.InstancePlugin): optional = True use_published = True - tile_assembler_plugin = "DraftTileAssembler" + tile_assembler_plugin = "PypeTileAssembler" def process(self, instance): """Plugin entry point.""" From 63ae53a8d2ebd0e4da081a5a13e9a1e4590cce9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Tue, 18 Aug 2020 14:49:30 +0200 Subject: [PATCH 089/813] fix job naming for multiple frames --- pype/plugins/maya/publish/submit_maya_deadline.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pype/plugins/maya/publish/submit_maya_deadline.py b/pype/plugins/maya/publish/submit_maya_deadline.py index 34e4432aa5..ca796d0a1c 100644 --- a/pype/plugins/maya/publish/submit_maya_deadline.py +++ b/pype/plugins/maya/publish/submit_maya_deadline.py @@ -566,13 +566,16 @@ class MayaSubmitDeadline(pyblish.api.InstancePlugin): file_index = 1 for file in files: frame = re.search(R_FRAME_NUMBER, file).group("frame") - new_payload = copy.copy(payload) + new_payload = copy.deepcopy(payload) new_payload["JobInfo"]["Name"] = \ "{} (Frame {} - {} tiles)".format( - new_payload["JobInfo"]["Name"], + payload["JobInfo"]["Name"], frame, instance.data.get("tilesX") * instance.data.get("tilesY") # noqa: E501 ) + self.log.info( + "... preparing job {}".format( + new_payload["JobInfo"]["Name"])) new_payload["JobInfo"]["TileJobFrame"] = frame tiles_data = _format_tiles( @@ -592,7 +595,7 @@ class MayaSubmitDeadline(pyblish.api.InstancePlugin): frame_payloads.append(new_payload) - new_assembly_payload = copy.copy(assembly_payload) + new_assembly_payload = copy.deepcopy(assembly_payload) new_assembly_payload["JobInfo"]["OutputFilename0"] = re.sub( REPL_FRAME_NUMBER, "\\1{}\\3".format("#" * len(frame)), file) From 42a19156f49fdfb184c6b2686b7feb24c387317a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Tue, 18 Aug 2020 15:30:32 +0200 Subject: [PATCH 090/813] fix assembly job names --- pype/plugins/maya/publish/submit_maya_deadline.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pype/plugins/maya/publish/submit_maya_deadline.py b/pype/plugins/maya/publish/submit_maya_deadline.py index ca796d0a1c..eeb6472850 100644 --- a/pype/plugins/maya/publish/submit_maya_deadline.py +++ b/pype/plugins/maya/publish/submit_maya_deadline.py @@ -596,6 +596,10 @@ class MayaSubmitDeadline(pyblish.api.InstancePlugin): frame_payloads.append(new_payload) new_assembly_payload = copy.deepcopy(assembly_payload) + new_assembly_payload["JobInfo"]["Name"] = \ + "{} (Frame {})".format( + assembly_payload["JobInfo"]["Name"], + frame) new_assembly_payload["JobInfo"]["OutputFilename0"] = re.sub( REPL_FRAME_NUMBER, "\\1{}\\3".format("#" * len(frame)), file) From 866bc150085f3eeb82f6042f9866f130de95b400 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 18 Aug 2020 15:53:38 +0200 Subject: [PATCH 091/813] reraise launcher window if is already shown but in back --- pype/modules/avalon_apps/avalon_app.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pype/modules/avalon_apps/avalon_app.py b/pype/modules/avalon_apps/avalon_app.py index 34fbc5c5ae..7ed651f82b 100644 --- a/pype/modules/avalon_apps/avalon_app.py +++ b/pype/modules/avalon_apps/avalon_app.py @@ -48,6 +48,8 @@ class AvalonApps: def show_launcher(self): # if app_launcher don't exist create it/otherwise only show main window self.app_launcher.show() + self.app_launcher.raise_() + self.app_launcher.activateWindow() def show_library_loader(self): libraryloader.show( From 41c101695238e3667c6392e155a5996dd335f85f Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 18 Aug 2020 18:03:35 +0200 Subject: [PATCH 092/813] Working implementation of collect_instances\n extract_review, etract_image --- .../clients/photoshop_client.py | 102 ++++++++++++++++++ .../websocket_server/hosts/photoshop.py | 34 ++++++ .../websocket_server/websocket_server.py | 35 +++--- .../photoshop/publish/collect_instances.py | 23 ++-- .../photoshop/publish/extract_image.py | 43 +++++--- .../photoshop/publish/extract_review.py | 62 +++++++---- .../photoshop/publish/extract_save_scene.py | 5 +- 7 files changed, 240 insertions(+), 64 deletions(-) create mode 100644 pype/modules/websocket_server/clients/photoshop_client.py create mode 100644 pype/modules/websocket_server/hosts/photoshop.py diff --git a/pype/modules/websocket_server/clients/photoshop_client.py b/pype/modules/websocket_server/clients/photoshop_client.py new file mode 100644 index 0000000000..0090c10db2 --- /dev/null +++ b/pype/modules/websocket_server/clients/photoshop_client.py @@ -0,0 +1,102 @@ +from pype.modules.websocket_server import WebSocketServer +""" + Stub handling connection from server to client. + Used anywhere solution is calling client methods. +""" +import json +from collections import namedtuple + +class PhotoshopClientStub(): + + def __init__(self): + self.websocketserver = WebSocketServer.get_instance() + self.client = self.websocketserver.get_client() + + def read(self, layer): + layers_data = {} + res = self.websocketserver.call(self.client.call('Photoshop.read')) + try: + layers_data = json.loads(res) + except json.decoder.JSONDecodeError: + pass + + return layers_data.get(str(layer.id)) + + def get_layers(self): + """ + Returns JSON document with all(?) layers in active document. + + :return: + Format of tuple: { 'id':'123', + 'name': 'My Layer 1', + 'type': 'GUIDE'|'FG'|'BG'|'OBJ' + 'visible': 'true'|'false' + """ + layers = {} + res = self.websocketserver.call(self.client.call + ('Photoshop.get_layers')) + print("get_layers:: {}".format(res)) + try: + layers_data = json.loads(res) + except json.decoder.JSONDecodeError: + raise ValueError("Received broken JSON {}".format(res)) + ret = [] + # convert to namedtuple to use dot donation + for d in layers_data: + ret.append(namedtuple('Layer', d.keys())(*d.values())) + + return ret + + def get_layers_in_layers(self, layers): + """ + Return all layers that belong to layers (might be groups). + :param layers: + :return: + """ + all_layers = self.get_layers() + print("get_layers_in_layers {}".format(layers)) + print("get_layers_in_layers len {}".format(len(layers))) + print("get_layers_in_layers type {}".format(type(layers))) + ret = [] + layer_ids = [lay.id for lay in layers] + layer_group_ids = [ll.groupId for ll in layers if ll.group] + for layer in all_layers: + if layer.groupId in layer_group_ids: # all from group + ret.append(layer) + if layer.id in layer_ids: + ret.append(layer) + + return ret + + + def select_layers(self, layers): + layer_ids = [layer.id for layer in layers] + + res = self.websocketserver.call(self.client.call + ('Photoshop.get_layers', + layers=layer_ids) + ) + + def get_active_document_name(self): + res = self.websocketserver.call(self.client.call + ('Photoshop.get_active_document_name')) + + return res + + def set_visible(self, layer_id, visibility): + print("set_visible {}, {}".format(layer_id, visibility)) + res = self.websocketserver.call(self.client.call + ('Photoshop.set_visible', + layer_id=layer_id, + visibility=visibility)) + + def saveAs(self, image_path, ext, as_copy): + res = self.websocketserver.call(self.client.call + ('Photoshop.saveAs', + image_path=image_path, + ext=ext, + as_copy=as_copy)) + + def close(self): + self.client.close() + diff --git a/pype/modules/websocket_server/hosts/photoshop.py b/pype/modules/websocket_server/hosts/photoshop.py new file mode 100644 index 0000000000..9092530e48 --- /dev/null +++ b/pype/modules/websocket_server/hosts/photoshop.py @@ -0,0 +1,34 @@ +import asyncio + +from pype.api import Logger +from wsrpc_aiohttp import WebSocketRoute + +log = Logger().get_logger("WebsocketServer") + + +class Photoshop(WebSocketRoute): + """ + One route, mimicking external application (like Harmony, etc). + All functions could be called from client. + 'do_notify' function calls function on the client - mimicking + notification after long running job on the server or similar + """ + instance = None + + def init(self, **kwargs): + # Python __init__ must be return "self". + # This method might return anything. + log.debug("someone called Photoshop route") + self.instance = self + return kwargs + + # server functions + async def ping(self): + log.debug("someone called Photoshop route ping") + + # This method calls function on the client side + # client functions + + async def read(self): + log.debug("photoshop.read client calls server server calls Photo client") + return await self.socket.call('Photoshop.read') diff --git a/pype/modules/websocket_server/websocket_server.py b/pype/modules/websocket_server/websocket_server.py index 9d0d01d156..f9be7c88a9 100644 --- a/pype/modules/websocket_server/websocket_server.py +++ b/pype/modules/websocket_server/websocket_server.py @@ -80,19 +80,26 @@ class WebSocketServer(): module = importlib.import_module(module_name) cls = getattr(module, class_name) WebSocketAsync.add_route(class_name, cls) - self.handlers[class_name] = cls() # TODO refactor sys.path.pop() def call(self, func): log.debug("websocket.call {}".format(func)) - return self.websocket_thread.call_async(func) + future = asyncio.run_coroutine_threadsafe(func, + self.websocket_thread.loop) + result = future.result() + return result - def task_finished(self, task): - print("task finished {}".format(task.result)) - print("client socket {}".format(self.client.client.socket)) + def get_client(self): + """ + Return first connected client to WebSocket + TODO implement selection by Route + :return: client + """ + clients = WebSocketAsync.get_clients() + key = list(clients.keys())[0] + client = clients.get(key) - def get_routes(self): - WebSocketAsync.get_routes() + return client @staticmethod def get_instance(): @@ -176,20 +183,6 @@ class WebsocketServerThread(threading.Thread): self.module.thread_stopped() log.info("Websocket server stopped") - def call_async(self, func): - # log.debug("call async") - # print("call aysnc") - # log.debug("my loop {}".format(self.loop)) - # task = self.loop.create_task(func) - # print("waitning") - # log.debug("waiting for task {}".format(func)) - # self.loop.run_until_complete(task) - # log.debug("returned value {}".format(task.result)) - # return task.result - task = self.loop.create_task(func) - task.add_done_callback(self.module.task_finished) - self.tasks.append(task) - async def start_server(self): """ Starts runner and TCPsite """ self.runner = web.AppRunner(self.module.app) diff --git a/pype/plugins/photoshop/publish/collect_instances.py b/pype/plugins/photoshop/publish/collect_instances.py index 4937f2a1e4..cc7341f384 100644 --- a/pype/plugins/photoshop/publish/collect_instances.py +++ b/pype/plugins/photoshop/publish/collect_instances.py @@ -4,6 +4,7 @@ from avalon import photoshop import pyblish.api +from pype.modules.websocket_server.clients.photoshop_client import PhotoshopClientStub class CollectInstances(pyblish.api.ContextPlugin): """Gather instances by LayerSet and file metadata @@ -27,8 +28,13 @@ class CollectInstances(pyblish.api.ContextPlugin): # can be. pythoncom.CoInitialize() - for layer in photoshop.get_layers_in_document(): - layer_data = photoshop.read(layer) + from datetime import datetime + start = datetime.now() + # for timing + photoshop_client = PhotoshopClientStub() + layers = photoshop_client.get_layers() + for layer in layers: + layer_data = photoshop_client.read(layer) # Skip layers without metadata. if layer_data is None: @@ -38,18 +44,19 @@ class CollectInstances(pyblish.api.ContextPlugin): if "container" in layer_data["id"]: continue - child_layers = [*layer.Layers] - if not child_layers: - self.log.info("%s skipped, it was empty." % layer.Name) - continue + # child_layers = [*layer.Layers] + # self.log.debug("child_layers {}".format(child_layers)) + # if not child_layers: + # self.log.info("%s skipped, it was empty." % layer.Name) + # continue - instance = context.create_instance(layer.Name) + instance = context.create_instance(layer.name) instance.append(layer) instance.data.update(layer_data) instance.data["families"] = self.families_mapping[ layer_data["family"] ] - instance.data["publish"] = layer.Visible + instance.data["publish"] = layer.visible # Produce diagnostic message for any graphical # user interface interested in visualising it. diff --git a/pype/plugins/photoshop/publish/extract_image.py b/pype/plugins/photoshop/publish/extract_image.py index 6dfccdc4f2..0451308ef1 100644 --- a/pype/plugins/photoshop/publish/extract_image.py +++ b/pype/plugins/photoshop/publish/extract_image.py @@ -3,6 +3,8 @@ import os import pype.api from avalon import photoshop +from pype.modules.websocket_server.clients.photoshop_client import \ + PhotoshopClientStub class ExtractImage(pype.api.Extractor): """Produce a flattened image file from instance @@ -20,36 +22,49 @@ class ExtractImage(pype.api.Extractor): staging_dir = self.staging_dir(instance) self.log.info("Outputting image to {}".format(staging_dir)) + layers = [] + for image_instance in instance.context: + if image_instance.data["family"] != "image": + continue + layers.append(image_instance[0]) + # Perform extraction + photoshop_client = PhotoshopClientStub() files = {} with photoshop.maintained_selection(): self.log.info("Extracting %s" % str(list(instance))) with photoshop.maintained_visibility(): # Hide all other layers. - extract_ids = [ - x.id for x in photoshop.get_layers_in_layers([instance[0]]) - ] - for layer in photoshop.get_layers_in_document(): - if layer.id not in extract_ids: - layer.Visible = False + extract_ids = set([ll.id for ll in photoshop_client. + get_layers_in_layers(layers)]) - save_options = {} + for layer in photoshop_client.get_layers(): + # limit unnecessary calls to client + if layer.visible and layer.id not in extract_ids: + photoshop_client.set_visible(layer.id, + False) + if not layer.visible and layer.id in extract_ids: + photoshop_client.set_visible(layer.id, + True) + + save_options = [] if "png" in self.formats: - save_options["png"] = photoshop.com_objects.PNGSaveOptions() + save_options.append('png') if "jpg" in self.formats: - save_options["jpg"] = photoshop.com_objects.JPEGSaveOptions() + save_options.append('jpg') file_basename = os.path.splitext( - photoshop.app().ActiveDocument.Name + photoshop_client.get_active_document_name() )[0] - for extension, save_option in save_options.items(): + for extension in save_options: _filename = "{}.{}".format(file_basename, extension) files[extension] = _filename full_filename = os.path.join(staging_dir, _filename) - photoshop.app().ActiveDocument.SaveAs( - full_filename, save_option, True - ) + photoshop_client.saveAs(full_filename, + extension, + True) + representations = [] for extension, filename in files.items(): diff --git a/pype/plugins/photoshop/publish/extract_review.py b/pype/plugins/photoshop/publish/extract_review.py index 078ee53899..1c3aeaffb5 100644 --- a/pype/plugins/photoshop/publish/extract_review.py +++ b/pype/plugins/photoshop/publish/extract_review.py @@ -4,6 +4,10 @@ import pype.api import pype.lib from avalon import photoshop +from datetime import datetime +from pype.modules.websocket_server.clients.photoshop_client import \ + PhotoshopClientStub + class ExtractReview(pype.api.Extractor): """Produce a flattened image file from all instances.""" @@ -13,10 +17,12 @@ class ExtractReview(pype.api.Extractor): families = ["review"] def process(self, instance): - + start = datetime.now() staging_dir = self.staging_dir(instance) self.log.info("Outputting image to {}".format(staging_dir)) + photoshop_client = PhotoshopClientStub() + layers = [] for image_instance in instance.context: if image_instance.data["family"] != "image": @@ -25,26 +31,39 @@ class ExtractReview(pype.api.Extractor): # Perform extraction output_image = "{}.jpg".format( - os.path.splitext(photoshop.app().ActiveDocument.Name)[0] + os.path.splitext(photoshop_client.get_active_document_name())[0] ) output_image_path = os.path.join(staging_dir, output_image) + self.log.info( + "first part took {}".format(datetime.now() - start)) with photoshop.maintained_visibility(): # Hide all other layers. - extract_ids = [ - x.id for x in photoshop.get_layers_in_layers(layers) - ] - for layer in photoshop.get_layers_in_document(): - if layer.id in extract_ids: - layer.Visible = True - else: - layer.Visible = False + start = datetime.now() + extract_ids = set([ll.id for ll in photoshop_client. + get_layers_in_layers(layers)]) + self.log.info("extract_ids {}".format(extract_ids)) - photoshop.app().ActiveDocument.SaveAs( - output_image_path, - photoshop.com_objects.JPEGSaveOptions(), - True - ) + for layer in photoshop_client.get_layers(): + # limit unnecessary calls to client + if layer.visible and layer.id not in extract_ids: + photoshop_client.set_visible(layer.id, + False) + if not layer.visible and layer.id in extract_ids: + photoshop_client.set_visible(layer.id, + True) + self.log.info( + "get_layers_in_layers took {}".format(datetime.now() - start)) + start = datetime.now() + + self.log.info("output_image_path {}".format(output_image_path)) + photoshop_client.saveAs(output_image_path, + 'jpg', + True) + self.log.info( + "saveAs {} took {}".format('JPG', datetime.now() - start)) + + start = datetime.now() ffmpeg_path = pype.lib.get_ffmpeg_tool_path("ffmpeg") instance.data["representations"].append({ @@ -65,7 +84,8 @@ class ExtractReview(pype.api.Extractor): thumbnail_path ] output = pype.lib._subprocess(args) - + self.log.info( + "thumbnail {} took {}".format('JPG', datetime.now() - start)) self.log.debug(output) instance.data["representations"].append({ @@ -75,7 +95,7 @@ class ExtractReview(pype.api.Extractor): "stagingDir": staging_dir, "tags": ["thumbnail"] }) - + start = datetime.now() # Generate mov. mov_path = os.path.join(staging_dir, "review.mov") args = [ @@ -86,9 +106,10 @@ class ExtractReview(pype.api.Extractor): mov_path ] output = pype.lib._subprocess(args) - + self.log.info( + "review {} took {}".format('JPG', datetime.now() - start)) self.log.debug(output) - + start = datetime.now() instance.data["representations"].append({ "name": "mov", "ext": "mov", @@ -105,5 +126,6 @@ class ExtractReview(pype.api.Extractor): instance.data["frameStart"] = 1 instance.data["frameEnd"] = 1 instance.data["fps"] = 25 - + self.log.info( + "end {} took {}".format('JPG', datetime.now() - start)) self.log.info(f"Extracted {instance} to {staging_dir}") diff --git a/pype/plugins/photoshop/publish/extract_save_scene.py b/pype/plugins/photoshop/publish/extract_save_scene.py index b3d4f0e447..e2068501db 100644 --- a/pype/plugins/photoshop/publish/extract_save_scene.py +++ b/pype/plugins/photoshop/publish/extract_save_scene.py @@ -1,7 +1,7 @@ import pype.api from avalon import photoshop - +from datetime import datetime class ExtractSaveScene(pype.api.Extractor): """Save scene before extraction.""" @@ -11,4 +11,7 @@ class ExtractSaveScene(pype.api.Extractor): families = ["workfile"] def process(self, instance): + start = datetime.now() photoshop.app().ActiveDocument.Save() + self.log.info( + "ExtractSaveScene took {}".format(datetime.now() - start)) From 7e65e5c830a63cf05aff5438cbcd1f18bfd57ec2 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 19 Aug 2020 12:06:59 +0200 Subject: [PATCH 093/813] basic overrides values getting implemented --- pype/tools/config_setting/widgets/base.py | 61 +++++++++++- pype/tools/config_setting/widgets/inputs.py | 101 +++++++++++++++++--- 2 files changed, 150 insertions(+), 12 deletions(-) diff --git a/pype/tools/config_setting/widgets/base.py b/pype/tools/config_setting/widgets/base.py index 14ff56aa2a..c131966f6c 100644 --- a/pype/tools/config_setting/widgets/base.py +++ b/pype/tools/config_setting/widgets/base.py @@ -304,6 +304,7 @@ class ProjectWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.is_overidable = False self.ignore_value_changes = False + self.project_name = None self.input_fields = [] @@ -373,7 +374,6 @@ class ProjectWidget(QtWidgets.QWidget, PypeConfigurationWidget): def _on_project_change(self): project_name = self.project_list_widget.project_name() - if project_name is None: overrides = None self.is_overidable = False @@ -381,12 +381,28 @@ class ProjectWidget(QtWidgets.QWidget, PypeConfigurationWidget): overrides = config.project_preset_overrides(project_name) self.is_overidable = True + self.project_name = project_name self.ignore_value_changes = True for item in self.input_fields: item.apply_overrides(overrides) self.ignore_value_changes = False def _save(self): + if self.project_name is None: + self._save_defaults() + else: + self._save_overrides() + + def _save_overrides(self): + output = {} + for item in self.input_fields: + value = item.overrides() + if value is not NOT_SET: + output.update(value) + + print(json.dumps(output, indent=4)) + + def _save_defaults(self): output = {} for item in self.input_fields: output.update(item.config_value()) @@ -396,3 +412,46 @@ class ProjectWidget(QtWidgets.QWidget, PypeConfigurationWidget): output = _output print(json.dumps(output, indent=4)) + return + + # TODO check implementation copied from studio + all_values = {} + for item in self.input_fields: + all_values.update(item.config_value()) + + for key in reversed(self.keys): + _all_values = {key: all_values} + all_values = _all_values + + # Skip first key + all_values = all_values["studio"] + + # Load studio data with metadata + current_presets = config.studio_presets() + + keys_to_file = config.file_keys_from_schema(self.schema) + for key_sequence in keys_to_file: + # Skip first key + key_sequence = key_sequence[1:] + subpath = "/".join(key_sequence) + ".json" + origin_values = current_presets + for key in key_sequence: + if key not in origin_values: + origin_values = {} + break + origin_values = origin_values[key] + + new_values = all_values + for key in key_sequence: + new_values = new_values[key] + origin_values.update(new_values) + + output_path = os.path.join( + config.studio_presets_path, subpath + ) + dirpath = os.path.dirname(output_path) + if not os.path.exists(dirpath): + os.makedirs(dirpath) + + with open(output_path, "w") as file_stream: + json.dump(origin_values, file_stream, indent=4) diff --git a/pype/tools/config_setting/widgets/inputs.py b/pype/tools/config_setting/widgets/inputs.py index c4ec7a4347..6c6c31408e 100644 --- a/pype/tools/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/widgets/inputs.py @@ -177,6 +177,11 @@ class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): def config_value(self): return {self.key: self.item_value()} + def overrides(self): + if not self.is_overriden: + return NOT_SET + return self.config_value() + class IntegerWidget(QtWidgets.QWidget, PypeConfigurationWidget): value_changed = QtCore.Signal(object) @@ -321,6 +326,11 @@ class IntegerWidget(QtWidgets.QWidget, PypeConfigurationWidget): def config_value(self): return {self.key: self.item_value()} + def overrides(self): + if not self.is_overriden: + return NOT_SET + return self.config_value() + class FloatWidget(QtWidgets.QWidget, PypeConfigurationWidget): value_changed = QtCore.Signal(object) @@ -473,6 +483,11 @@ class FloatWidget(QtWidgets.QWidget, PypeConfigurationWidget): def config_value(self): return {self.key: self.item_value()} + def overrides(self): + if not self.is_overriden: + return NOT_SET + return self.config_value() + class TextSingleLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): value_changed = QtCore.Signal(object) @@ -617,6 +632,11 @@ class TextSingleLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): def config_value(self): return {self.key: self.item_value()} + def overrides(self): + if not self.is_overriden: + return NOT_SET + return self.config_value() + class TextMultiLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): value_changed = QtCore.Signal(object) @@ -757,6 +777,11 @@ class TextMultiLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): def config_value(self): return {self.key: self.item_value()} + def overrides(self): + if not self.is_overriden: + return NOT_SET + return self.config_value() + class RawJsonInput(QtWidgets.QPlainTextEdit): tab_length = 4 @@ -955,6 +980,11 @@ class RawJsonWidget(QtWidgets.QWidget, PypeConfigurationWidget): def config_value(self): return {self.key: self.item_value()} + def overrides(self): + if not self.is_overriden: + return NOT_SET + return self.config_value() + class TextListItem(QtWidgets.QWidget, PypeConfigurationWidget): _btn_size = 20 @@ -1248,6 +1278,11 @@ class TextListWidget(QtWidgets.QWidget, PypeConfigurationWidget): def config_value(self): return {self.key: self.item_value()} + def overrides(self): + if not self.is_overriden: + return NOT_SET + return self.config_value() + class DictExpandWidget(QtWidgets.QWidget, PypeConfigurationWidget): value_changed = QtCore.Signal(object) @@ -1473,6 +1508,18 @@ class DictExpandWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.input_fields.append(item) return item + def overrides(self): + if not self.is_overriden and not self.child_overriden: + return NOT_SET + + values = {} + for input_field in self.input_fields: + value = input_field.overrides() + if value is NOT_SET: + continue + values.update(value) + return {self.key: values} + class DictInvisible(QtWidgets.QWidget, PypeConfigurationWidget): # TODO is not overridable by itself @@ -1612,6 +1659,18 @@ class DictInvisible(QtWidgets.QWidget, PypeConfigurationWidget): ) self.update_style() + def overrides(self): + if not self.is_overriden and not self.child_overriden: + return NOT_SET + + values = {} + for input_field in self.input_fields: + value = input_field.overrides() + if value is NOT_SET: + continue + values.update(value) + return {self.key: values} + class DictFormWidget(QtWidgets.QWidget): value_changed = QtCore.Signal(object) @@ -1628,7 +1687,7 @@ class DictFormWidget(QtWidgets.QWidget): self.any_parent_is_group = any_parent_is_group self.is_modified = False - self.is_overriden = False + self._is_overriden = False self.is_group = False super(DictFormWidget, self).__init__(parent) @@ -1646,13 +1705,9 @@ class DictFormWidget(QtWidgets.QWidget): return self.value_changed.emit(self) - def item_value(self): - output = {} - for input_field in self.input_fields.values(): - # TODO maybe merge instead of update should be used - # NOTE merge is custom function which merges 2 dicts - output.update(input_field.config_value()) - return output + @property + def is_overriden(self): + return self._parent.is_overriden @property def child_modified(self): @@ -1676,9 +1731,6 @@ class DictFormWidget(QtWidgets.QWidget): def ignore_value_changes(self): return self._parent.ignore_value_changes - def config_value(self): - return self.item_value() - def add_children_gui(self, child_configuration, values): item_type = child_configuration["type"] key = child_configuration["key"] @@ -1697,6 +1749,28 @@ class DictFormWidget(QtWidgets.QWidget): self.input_fields[key] = item return item + def item_value(self): + output = {} + for input_field in self.input_fields.values(): + # TODO maybe merge instead of update should be used + # NOTE merge is custom function which merges 2 dicts + output.update(input_field.config_value()) + return output + + def config_value(self): + return self.item_value() + + def overrides(self): + if not self.is_overiden and not self.child_overriden: + return NOT_SET + + values = {} + for input_field in self.input_fields: + value = input_field.overrides() + if value is not NOT_SET: + values.update(value) + return values + class ModifiableDictItem(QtWidgets.QWidget, PypeConfigurationWidget): _btn_size = 20 @@ -2064,6 +2138,11 @@ class ModifiableDict(ExpandingWidget, PypeConfigurationWidget): def config_value(self): return {self.key: self.item_value()} + def overrides(self): + if not self.is_overiden: + return NOT_SET + return self.config_value() + TypeToKlass.types["boolean"] = BooleanWidget TypeToKlass.types["text-singleline"] = TextSingleLineWidget From a9f146e2fca896a8558c3c8d65d6df1a0f3af3ec Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 19 Aug 2020 12:54:42 +0200 Subject: [PATCH 094/813] Fixed fullName, implemented imprint --- .../clients/photoshop_client.py | 73 ++++++++++++++++--- .../photoshop/publish/collect_current_file.py | 6 +- .../photoshop/publish/collect_instances.py | 6 ++ .../photoshop/publish/extract_save_scene.py | 6 +- .../photoshop/publish/increment_workfile.py | 5 +- .../publish/validate_instance_asset.py | 11 ++- .../photoshop/publish/validate_naming.py | 9 ++- 7 files changed, 96 insertions(+), 20 deletions(-) diff --git a/pype/modules/websocket_server/clients/photoshop_client.py b/pype/modules/websocket_server/clients/photoshop_client.py index 0090c10db2..6d3615e1a4 100644 --- a/pype/modules/websocket_server/clients/photoshop_client.py +++ b/pype/modules/websocket_server/clients/photoshop_client.py @@ -13,15 +13,34 @@ class PhotoshopClientStub(): self.client = self.websocketserver.get_client() def read(self, layer): - layers_data = {} - res = self.websocketserver.call(self.client.call('Photoshop.read')) - try: - layers_data = json.loads(res) - except json.decoder.JSONDecodeError: - pass + layers_data = self._get_layers_metadata() return layers_data.get(str(layer.id)) + def imprint(self, layer, data): + layers_data = self._get_layers_metadata() + # json.dumps writes integer values in a dictionary to string, so + # anticipating it here. + if str(layer.id) in layers_data: + layers_data[str(layer.id)].update(data) + else: + layers_data[str(layer.id)] = data + + # Ensure only valid ids are stored. + layer_ids = [layer.id for layer in self.get_layers()] + cleaned_data = {} + + for id in layers_data: + if int(id) in layer_ids: + cleaned_data[id] = layers_data[id] + + payload = json.dumps(cleaned_data, indent=4) + + res = self.websocketserver.call(self.client.call + ('Photoshop.imprint', + payload=payload) + ) + def get_layers(self): """ Returns JSON document with all(?) layers in active document. @@ -77,18 +96,34 @@ class PhotoshopClientStub(): layers=layer_ids) ) + def get_active_document_full_name(self): + """ + Returns full name with path of active document via ws call + :return: full path with name + """ + res = self.websocketserver.call( + self.client.call('Photoshop.get_active_document_full_name')) + + return res + def get_active_document_name(self): + """ + Returns just a name of active document via ws call + :return: file name + """ res = self.websocketserver.call(self.client.call ('Photoshop.get_active_document_name')) return res - def set_visible(self, layer_id, visibility): - print("set_visible {}, {}".format(layer_id, visibility)) + def save(self): + """ + Saves active document + :return: None + """ res = self.websocketserver.call(self.client.call - ('Photoshop.set_visible', - layer_id=layer_id, - visibility=visibility)) + ('Photoshop.save')) + def saveAs(self, image_path, ext, as_copy): res = self.websocketserver.call(self.client.call @@ -97,6 +132,22 @@ class PhotoshopClientStub(): ext=ext, as_copy=as_copy)) + def set_visible(self, layer_id, visibility): + print("set_visible {}, {}".format(layer_id, visibility)) + res = self.websocketserver.call(self.client.call + ('Photoshop.set_visible', + layer_id=layer_id, + visibility=visibility)) + + def _get_layers_metadata(self): + layers_data = {} + res = self.websocketserver.call(self.client.call('Photoshop.read')) + try: + layers_data = json.loads(res) + except json.decoder.JSONDecodeError: + pass + return layers_data + def close(self): self.client.close() diff --git a/pype/plugins/photoshop/publish/collect_current_file.py b/pype/plugins/photoshop/publish/collect_current_file.py index 4308588559..bb81718bcc 100644 --- a/pype/plugins/photoshop/publish/collect_current_file.py +++ b/pype/plugins/photoshop/publish/collect_current_file.py @@ -3,6 +3,9 @@ import os import pyblish.api from avalon import photoshop +from pype.modules.websocket_server.clients.photoshop_client import \ + PhotoshopClientStub + class CollectCurrentFile(pyblish.api.ContextPlugin): """Inject the current working file into context""" @@ -12,6 +15,7 @@ class CollectCurrentFile(pyblish.api.ContextPlugin): hosts = ["photoshop"] def process(self, context): + photoshop_client = PhotoshopClientStub() context.data["currentFile"] = os.path.normpath( - photoshop.app().ActiveDocument.FullName + photoshop_client.get_active_document_full_name() ).replace("\\", "/") diff --git a/pype/plugins/photoshop/publish/collect_instances.py b/pype/plugins/photoshop/publish/collect_instances.py index cc7341f384..d94adde00b 100644 --- a/pype/plugins/photoshop/publish/collect_instances.py +++ b/pype/plugins/photoshop/publish/collect_instances.py @@ -33,8 +33,14 @@ class CollectInstances(pyblish.api.ContextPlugin): # for timing photoshop_client = PhotoshopClientStub() layers = photoshop_client.get_layers() + for layer in layers: layer_data = photoshop_client.read(layer) + self.log.info("layer_data {}".format(layer_data)) + + photoshop_client.imprint(layer, layer_data) + new_layer_data = photoshop_client.read(layer) + assert layer_data == new_layer_data # Skip layers without metadata. if layer_data is None: diff --git a/pype/plugins/photoshop/publish/extract_save_scene.py b/pype/plugins/photoshop/publish/extract_save_scene.py index e2068501db..ea7bdda9af 100644 --- a/pype/plugins/photoshop/publish/extract_save_scene.py +++ b/pype/plugins/photoshop/publish/extract_save_scene.py @@ -1,6 +1,9 @@ import pype.api from avalon import photoshop +from pype.modules.websocket_server.clients.photoshop_client import \ + PhotoshopClientStub + from datetime import datetime class ExtractSaveScene(pype.api.Extractor): """Save scene before extraction.""" @@ -11,7 +14,8 @@ class ExtractSaveScene(pype.api.Extractor): families = ["workfile"] def process(self, instance): + photoshop_client = PhotoshopClientStub() start = datetime.now() - photoshop.app().ActiveDocument.Save() + photoshop_client.save() self.log.info( "ExtractSaveScene took {}".format(datetime.now() - start)) diff --git a/pype/plugins/photoshop/publish/increment_workfile.py b/pype/plugins/photoshop/publish/increment_workfile.py index ba9ab8606a..0ae7e9772f 100644 --- a/pype/plugins/photoshop/publish/increment_workfile.py +++ b/pype/plugins/photoshop/publish/increment_workfile.py @@ -3,6 +3,8 @@ from pype.action import get_errored_plugins_from_data from pype.lib import version_up from avalon import photoshop +from pype.modules.websocket_server.clients.photoshop_client import \ + PhotoshopClientStub class IncrementWorkfile(pyblish.api.InstancePlugin): """Increment the current workfile. @@ -24,6 +26,7 @@ class IncrementWorkfile(pyblish.api.InstancePlugin): ) scene_path = version_up(instance.context.data["currentFile"]) - photoshop.app().ActiveDocument.SaveAs(scene_path) + photoshop_client = PhotoshopClientStub() + photoshop_client.saveAs(scene_path, 'psd', True) self.log.info("Incremented workfile to: {}".format(scene_path)) diff --git a/pype/plugins/photoshop/publish/validate_instance_asset.py b/pype/plugins/photoshop/publish/validate_instance_asset.py index ab1d02269f..6a0a408878 100644 --- a/pype/plugins/photoshop/publish/validate_instance_asset.py +++ b/pype/plugins/photoshop/publish/validate_instance_asset.py @@ -4,6 +4,8 @@ import pyblish.api import pype.api from avalon import photoshop +from pype.modules.websocket_server.clients.photoshop_client import \ + PhotoshopClientStub class ValidateInstanceAssetRepair(pyblish.api.Action): """Repair the instance asset.""" @@ -23,11 +25,14 @@ class ValidateInstanceAssetRepair(pyblish.api.Action): # Apply pyblish.logic to get the instances for the plug-in instances = pyblish.api.instances_by_plugin(failed, plugin) - + photoshop_client = PhotoshopClientStub() for instance in instances: - data = photoshop.read(instance[0]) + self.log.info("validate_instance_asset instance[0] {}".format(instance[0])) + self.log.info("validate_instance_asset instance {}".format(instance)) + data = photoshop_client.read(instance[0]) + data["asset"] = os.environ["AVALON_ASSET"] - photoshop.imprint(instance[0], data) + photoshop_client.imprint(instance[0], data) class ValidateInstanceAsset(pyblish.api.InstancePlugin): diff --git a/pype/plugins/photoshop/publish/validate_naming.py b/pype/plugins/photoshop/publish/validate_naming.py index 51e00da352..7734a0e5a0 100644 --- a/pype/plugins/photoshop/publish/validate_naming.py +++ b/pype/plugins/photoshop/publish/validate_naming.py @@ -2,6 +2,8 @@ import pyblish.api import pype.api from avalon import photoshop +from pype.modules.websocket_server.clients.photoshop_client import \ + PhotoshopClientStub class ValidateNamingRepair(pyblish.api.Action): """Repair the instance asset.""" @@ -21,13 +23,14 @@ class ValidateNamingRepair(pyblish.api.Action): # Apply pyblish.logic to get the instances for the plug-in instances = pyblish.api.instances_by_plugin(failed, plugin) - + photoshop_client = PhotoshopClientStub() for instance in instances: + self.log.info("validate_naming instance {}".format(instance)) name = instance.data["name"].replace(" ", "_") instance[0].Name = name - data = photoshop.read(instance[0]) + data = photoshop_client.read(instance[0]) data["subset"] = "image" + name - photoshop.imprint(instance[0], data) + photoshop_client.imprint(instance[0], data) return True From 76992b0629c6e99a0fb68e2084516c933bcd6dbe Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 19 Aug 2020 15:13:41 +0200 Subject: [PATCH 095/813] overrides are collected with metadata info --- pype/tools/config_setting/widgets/__init__.py | 2 +- pype/tools/config_setting/widgets/base.py | 17 +- pype/tools/config_setting/widgets/inputs.py | 1050 ++++++++--------- pype/tools/config_setting/widgets/lib.py | 11 +- 4 files changed, 537 insertions(+), 543 deletions(-) diff --git a/pype/tools/config_setting/widgets/__init__.py b/pype/tools/config_setting/widgets/__init__.py index b295759a36..9fbce6e1cf 100644 --- a/pype/tools/config_setting/widgets/__init__.py +++ b/pype/tools/config_setting/widgets/__init__.py @@ -1,4 +1,4 @@ -from .lib import CustomNone, NOT_SET +from .lib import NOT_SET, AS_WIDGET, METADATA_KEY from .base import * diff --git a/pype/tools/config_setting/widgets/base.py b/pype/tools/config_setting/widgets/base.py index c131966f6c..156f1f80e4 100644 --- a/pype/tools/config_setting/widgets/base.py +++ b/pype/tools/config_setting/widgets/base.py @@ -1,12 +1,10 @@ import os import json -import copy from Qt import QtWidgets, QtCore, QtGui from . import config from .widgets import UnsavedChangesDialog -from .lib import NOT_SET +from .lib import NOT_SET, METADATA_KEY from avalon import io -from queue import Queue class TypeToKlass: @@ -394,12 +392,19 @@ class ProjectWidget(QtWidgets.QWidget, PypeConfigurationWidget): self._save_overrides() def _save_overrides(self): - output = {} + data = {} + groups = [] for item in self.input_fields: - value = item.overrides() + value, is_group = item.overrides() if value is not NOT_SET: - output.update(value) + data.update(value) + if is_group: + groups.extend(value.keys()) + + if groups: + data[METADATA_KEY] = {"groups": groups} + output = convert_to_override(data) print(json.dumps(output, indent=4)) def _save_defaults(self): diff --git a/pype/tools/config_setting/widgets/inputs.py b/pype/tools/config_setting/widgets/inputs.py index 6c6c31408e..bb2d76fc71 100644 --- a/pype/tools/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/widgets/inputs.py @@ -8,7 +8,7 @@ from .widgets import ( ModifiedIntSpinBox, ModifiedFloatSpinBox ) -from .lib import NOT_SET, AS_WIDGET +from .lib import NOT_SET, AS_WIDGET, METADATA_KEY class SchemeGroupHierarchyBug(Exception): @@ -19,7 +19,14 @@ class SchemeGroupHierarchyBug(Exception): super(SchemeGroupHierarchyBug, self).__init__(msg) -class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): +class InputWidget: + def overrides(self): + if not self.is_overriden: + return NOT_SET, False + return self.config_value(), self.is_group + + +class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget, InputWidget): value_changed = QtCore.Signal(object) def __init__( @@ -177,13 +184,8 @@ class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget): def config_value(self): return {self.key: self.item_value()} - def overrides(self): - if not self.is_overriden: - return NOT_SET - return self.config_value() - -class IntegerWidget(QtWidgets.QWidget, PypeConfigurationWidget): +class IntegerWidget(QtWidgets.QWidget, PypeConfigurationWidget, InputWidget): value_changed = QtCore.Signal(object) def __init__( @@ -326,13 +328,8 @@ class IntegerWidget(QtWidgets.QWidget, PypeConfigurationWidget): def config_value(self): return {self.key: self.item_value()} - def overrides(self): - if not self.is_overriden: - return NOT_SET - return self.config_value() - -class FloatWidget(QtWidgets.QWidget, PypeConfigurationWidget): +class FloatWidget(QtWidgets.QWidget, PypeConfigurationWidget, InputWidget): value_changed = QtCore.Signal(object) def __init__( @@ -483,13 +480,8 @@ class FloatWidget(QtWidgets.QWidget, PypeConfigurationWidget): def config_value(self): return {self.key: self.item_value()} - def overrides(self): - if not self.is_overriden: - return NOT_SET - return self.config_value() - -class TextSingleLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): +class TextSingleLineWidget(QtWidgets.QWidget, PypeConfigurationWidget, InputWidget): value_changed = QtCore.Signal(object) def __init__( @@ -632,13 +624,8 @@ class TextSingleLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): def config_value(self): return {self.key: self.item_value()} - def overrides(self): - if not self.is_overriden: - return NOT_SET - return self.config_value() - -class TextMultiLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): +class TextMultiLineWidget(QtWidgets.QWidget, PypeConfigurationWidget, InputWidget): value_changed = QtCore.Signal(object) def __init__( @@ -777,11 +764,6 @@ class TextMultiLineWidget(QtWidgets.QWidget, PypeConfigurationWidget): def config_value(self): return {self.key: self.item_value()} - def overrides(self): - if not self.is_overriden: - return NOT_SET - return self.config_value() - class RawJsonInput(QtWidgets.QPlainTextEdit): tab_length = 4 @@ -840,7 +822,7 @@ class RawJsonInput(QtWidgets.QPlainTextEdit): self.update_style(is_valid) -class RawJsonWidget(QtWidgets.QWidget, PypeConfigurationWidget): +class RawJsonWidget(QtWidgets.QWidget, PypeConfigurationWidget, InputWidget): value_changed = QtCore.Signal(object) def __init__( @@ -980,11 +962,6 @@ class RawJsonWidget(QtWidgets.QWidget, PypeConfigurationWidget): def config_value(self): return {self.key: self.item_value()} - def overrides(self): - if not self.is_overriden: - return NOT_SET - return self.config_value() - class TextListItem(QtWidgets.QWidget, PypeConfigurationWidget): _btn_size = 20 @@ -1146,7 +1123,7 @@ class TextListSubWidget(QtWidgets.QWidget, PypeConfigurationWidget): return {self.key: self.item_value()} -class TextListWidget(QtWidgets.QWidget, PypeConfigurationWidget): +class TextListWidget(QtWidgets.QWidget, PypeConfigurationWidget, InputWidget): value_changed = QtCore.Signal(object) def __init__( @@ -1278,499 +1255,6 @@ class TextListWidget(QtWidgets.QWidget, PypeConfigurationWidget): def config_value(self): return {self.key: self.item_value()} - def overrides(self): - if not self.is_overriden: - return NOT_SET - return self.config_value() - - -class DictExpandWidget(QtWidgets.QWidget, PypeConfigurationWidget): - value_changed = QtCore.Signal(object) - - def __init__( - self, input_data, values, parent_keys, parent, label_widget=None - ): - if values is AS_WIDGET: - raise TypeError("Can't use \"{}\" as widget item.".format( - self.__class__.__name__ - )) - self._parent = parent - - any_parent_is_group = parent.is_group - if not any_parent_is_group: - any_parent_is_group = parent.any_parent_is_group - - is_group = input_data.get("is_group", False) - if is_group and any_parent_is_group: - raise SchemeGroupHierarchyBug() - - self.any_parent_is_group = any_parent_is_group - - self._is_modified = False - self._is_overriden = False - self.is_group = is_group - - self._state = None - self._child_state = None - - super(DictExpandWidget, self).__init__(parent) - self.setObjectName("DictExpandWidget") - top_part = ClickableWidget(parent=self) - - button_size = QtCore.QSize(5, 5) - button_toggle = QtWidgets.QToolButton(parent=top_part) - button_toggle.setProperty("btn-type", "expand-toggle") - button_toggle.setIconSize(button_size) - button_toggle.setArrowType(QtCore.Qt.RightArrow) - button_toggle.setCheckable(True) - button_toggle.setChecked(False) - - label = input_data["label"] - button_toggle_text = QtWidgets.QLabel(label, parent=top_part) - button_toggle_text.setObjectName("ExpandLabel") - - layout = QtWidgets.QHBoxLayout(top_part) - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(5) - layout.addWidget(button_toggle) - layout.addWidget(button_toggle_text) - top_part.setLayout(layout) - - main_layout = QtWidgets.QVBoxLayout(self) - main_layout.setContentsMargins(9, 9, 9, 9) - - content_widget = QtWidgets.QWidget(self) - content_widget.setVisible(False) - - content_layout = QtWidgets.QVBoxLayout(content_widget) - content_layout.setContentsMargins(3, 3, 3, 3) - - main_layout.addWidget(top_part) - main_layout.addWidget(content_widget) - self.setLayout(main_layout) - - self.setAttribute(QtCore.Qt.WA_StyledBackground) - - self.top_part = top_part - self.button_toggle = button_toggle - self.button_toggle_text = button_toggle_text - - self.content_widget = content_widget - self.content_layout = content_layout - - self.top_part.clicked.connect(self._top_part_clicked) - self.button_toggle.clicked.connect(self.toggle_content) - - self.input_fields = [] - - self.key = input_data["key"] - keys = list(parent_keys) - keys.append(self.key) - self.keys = keys - - for child_data in input_data.get("children", []): - self.add_children_gui(child_data, values) - - def _top_part_clicked(self): - self.toggle_content(not self.button_toggle.isChecked()) - - def toggle_content(self, *args): - if len(args) > 0: - checked = args[0] - else: - checked = self.button_toggle.isChecked() - arrow_type = QtCore.Qt.RightArrow - if checked: - arrow_type = QtCore.Qt.DownArrow - self.button_toggle.setChecked(checked) - self.button_toggle.setArrowType(arrow_type) - self.content_widget.setVisible(checked) - self.parent().updateGeometry() - - def resizeEvent(self, event): - super(DictExpandWidget, self).resizeEvent(event) - self.content_widget.updateGeometry() - - @property - def is_overriden(self): - return self._is_overriden or self._parent.is_overriden - - @property - def ignore_value_changes(self): - return self._parent.ignore_value_changes - - def apply_overrides(self, override_value): - # Make sure this is set to False - self._is_overriden = False - self._state = None - self._child_state = None - for item in self.input_fields: - if override_value is None: - child_value = None - else: - child_value = override_value.get(item.key) - - item.apply_overrides(child_value) - - self._is_overriden = ( - self.is_group - and self.is_overidable - and ( - override_value is not None - or self.child_overriden - ) - ) - self.update_style() - - def _on_value_change(self, item=None): - if self.ignore_value_changes: - return - - if self.is_group: - if self.is_overidable: - self._is_overriden = True - - # TODO update items - if item is not None: - for _item in self.input_fields: - if _item is not item: - _item.update_style() - - self.value_changed.emit(self) - - self.update_style() - - def update_style(self, is_overriden=None): - child_modified = self.child_modified - child_state = self.style_state(self.child_overriden, child_modified) - if child_state: - child_state = "child-{}".format(child_state) - - if child_state != self._child_state: - self.setProperty("state", child_state) - self.style().polish(self) - self._child_state = child_state - - state = self.style_state(self.is_overriden, self.is_modified) - if self._state == state: - return - - self.button_toggle_text.setProperty("state", state) - self.button_toggle_text.style().polish(self.button_toggle_text) - - self._state = state - - @property - def is_modified(self): - if self.is_group: - return self.child_modified - return False - - @property - def child_modified(self): - for input_field in self.input_fields: - if input_field.child_modified: - return True - return False - - @property - def child_overriden(self): - for input_field in self.input_fields: - if input_field.child_overriden: - return True - return False - - def item_value(self): - output = {} - for input_field in self.input_fields: - # TODO maybe merge instead of update should be used - # NOTE merge is custom function which merges 2 dicts - output.update(input_field.config_value()) - return output - - def config_value(self): - return {self.key: self.item_value()} - - @property - def is_overidable(self): - return self._parent.is_overidable - - def add_children_gui(self, child_configuration, values): - item_type = child_configuration["type"] - klass = TypeToKlass.types.get(item_type) - - item = klass( - child_configuration, values, self.keys, self - ) - item.value_changed.connect(self._on_value_change) - self.content_layout.addWidget(item) - - self.input_fields.append(item) - return item - - def overrides(self): - if not self.is_overriden and not self.child_overriden: - return NOT_SET - - values = {} - for input_field in self.input_fields: - value = input_field.overrides() - if value is NOT_SET: - continue - values.update(value) - return {self.key: values} - - -class DictInvisible(QtWidgets.QWidget, PypeConfigurationWidget): - # TODO is not overridable by itself - value_changed = QtCore.Signal(object) - - def __init__( - self, input_data, values, parent_keys, parent, label_widget=None - ): - self._parent = parent - - any_parent_is_group = parent.is_group - if not any_parent_is_group: - any_parent_is_group = parent.any_parent_is_group - - is_group = input_data.get("is_group", False) - if is_group and any_parent_is_group: - raise SchemeGroupHierarchyBug() - - self.any_parent_is_group = any_parent_is_group - - self._is_overriden = False - self.is_modified = False - self.is_group = is_group - - super(DictInvisible, self).__init__(parent) - self.setObjectName("DictInvisible") - - self.setAttribute(QtCore.Qt.WA_StyledBackground) - - layout = QtWidgets.QVBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(5) - - self.input_fields = [] - - if "key" not in input_data: - print(json.dumps(input_data, indent=4)) - - self.key = input_data["key"] - self.keys = list(parent_keys) - self.keys.append(self.key) - - for child_data in input_data.get("children", []): - self.add_children_gui(child_data, values) - - def update_style(self, *args, **kwargs): - return - - @property - def is_overriden(self): - return self._is_overriden or self._parent.is_overriden - - @property - def is_overidable(self): - return self._parent.is_overidable - - @property - def child_modified(self): - for input_field in self.input_fields: - if input_field.child_modified: - return True - return False - - @property - def child_overriden(self): - for input_field in self.input_fields: - if input_field.child_overriden: - return True - return False - - @property - def ignore_value_changes(self): - return self._parent.ignore_value_changes - - def item_value(self): - output = {} - for input_field in self.input_fields: - # TODO maybe merge instead of update should be used - # NOTE merge is custom function which merges 2 dicts - output.update(input_field.config_value()) - return output - - def config_value(self): - return {self.key: self.item_value()} - - def add_children_gui(self, child_configuration, values): - item_type = child_configuration["type"] - if item_type == "schema": - for _schema in child_configuration["children"]: - children = config.gui_schema(_schema) - self.add_children_gui(children, values) - return - - klass = TypeToKlass.types.get(item_type) - item = klass( - child_configuration, values, self.keys, self - ) - self.layout().addWidget(item) - - item.value_changed.connect(self._on_value_change) - - self.input_fields.append(item) - return item - - def _on_value_change(self, item=None): - if self.ignore_value_changes: - return - - if self.is_group: - if self.is_overidable: - self._is_overriden = True - # TODO update items - if item is not None: - is_overriden = self.is_overriden - for _item in self.input_fields: - if _item is not item: - _item.update_style(is_overriden) - - self.value_changed.emit(self) - - def apply_overrides(self, override_value): - self._is_overriden = False - for item in self.input_fields: - if override_value is None: - child_value = None - else: - child_value = override_value.get(item.key) - item.apply_overrides(child_value) - - self._is_overriden = ( - self.is_group - and self.is_overidable - and ( - override_value is not None - or self.child_overriden - ) - ) - self.update_style() - - def overrides(self): - if not self.is_overriden and not self.child_overriden: - return NOT_SET - - values = {} - for input_field in self.input_fields: - value = input_field.overrides() - if value is NOT_SET: - continue - values.update(value) - return {self.key: values} - - -class DictFormWidget(QtWidgets.QWidget): - value_changed = QtCore.Signal(object) - - def __init__( - self, input_data, values, parent_keys, parent, label_widget=None - ): - self._parent = parent - - any_parent_is_group = parent.is_group - if not any_parent_is_group: - any_parent_is_group = parent.any_parent_is_group - - self.any_parent_is_group = any_parent_is_group - - self.is_modified = False - self._is_overriden = False - self.is_group = False - - super(DictFormWidget, self).__init__(parent) - - self.input_fields = {} - self.content_layout = QtWidgets.QFormLayout(self) - - self.keys = list(parent_keys) - - for child_data in input_data.get("children", []): - self.add_children_gui(child_data, values) - - def _on_value_change(self, item=None): - if self.ignore_value_changes: - return - self.value_changed.emit(self) - - @property - def is_overriden(self): - return self._parent.is_overriden - - @property - def child_modified(self): - for input_field in self.input_fields.values(): - if input_field.child_modified: - return True - return False - - @property - def child_overriden(self): - for input_field in self.input_fields.values(): - if input_field.child_overriden: - return True - return False - - @property - def is_overidable(self): - return self._parent.is_overidable - - @property - def ignore_value_changes(self): - return self._parent.ignore_value_changes - - def add_children_gui(self, child_configuration, values): - item_type = child_configuration["type"] - key = child_configuration["key"] - # Pop label to not be set in child - label = child_configuration["label"] - - klass = TypeToKlass.types.get(item_type) - - label_widget = QtWidgets.QLabel(label) - - item = klass( - child_configuration, values, self.keys, self, label_widget - ) - item.value_changed.connect(self._on_value_change) - self.content_layout.addRow(label_widget, item) - self.input_fields[key] = item - return item - - def item_value(self): - output = {} - for input_field in self.input_fields.values(): - # TODO maybe merge instead of update should be used - # NOTE merge is custom function which merges 2 dicts - output.update(input_field.config_value()) - return output - - def config_value(self): - return self.item_value() - - def overrides(self): - if not self.is_overiden and not self.child_overriden: - return NOT_SET - - values = {} - for input_field in self.input_fields: - value = input_field.overrides() - if value is not NOT_SET: - values.update(value) - return values - class ModifiableDictItem(QtWidgets.QWidget, PypeConfigurationWidget): _btn_size = 20 @@ -2008,7 +1492,7 @@ class ModifiableDictSubWidget(QtWidgets.QWidget, PypeConfigurationWidget): return output -class ModifiableDict(ExpandingWidget, PypeConfigurationWidget): +class ModifiableDict(ExpandingWidget, PypeConfigurationWidget, InputWidget): # Should be used only for dictionary with one datatype as value # TODO this is actually input field (do not care if is group or not) value_changed = QtCore.Signal(object) @@ -2138,10 +1622,506 @@ class ModifiableDict(ExpandingWidget, PypeConfigurationWidget): def config_value(self): return {self.key: self.item_value()} + +class DictExpandWidget(QtWidgets.QWidget, PypeConfigurationWidget): + value_changed = QtCore.Signal(object) + + def __init__( + self, input_data, values, parent_keys, parent, label_widget=None + ): + if values is AS_WIDGET: + raise TypeError("Can't use \"{}\" as widget item.".format( + self.__class__.__name__ + )) + self._parent = parent + + any_parent_is_group = parent.is_group + if not any_parent_is_group: + any_parent_is_group = parent.any_parent_is_group + + is_group = input_data.get("is_group", False) + if is_group and any_parent_is_group: + raise SchemeGroupHierarchyBug() + + self.any_parent_is_group = any_parent_is_group + + self._is_modified = False + self._is_overriden = False + self.is_group = is_group + + self._state = None + self._child_state = None + + super(DictExpandWidget, self).__init__(parent) + self.setObjectName("DictExpandWidget") + top_part = ClickableWidget(parent=self) + + button_size = QtCore.QSize(5, 5) + button_toggle = QtWidgets.QToolButton(parent=top_part) + button_toggle.setProperty("btn-type", "expand-toggle") + button_toggle.setIconSize(button_size) + button_toggle.setArrowType(QtCore.Qt.RightArrow) + button_toggle.setCheckable(True) + button_toggle.setChecked(False) + + label = input_data["label"] + button_toggle_text = QtWidgets.QLabel(label, parent=top_part) + button_toggle_text.setObjectName("ExpandLabel") + + layout = QtWidgets.QHBoxLayout(top_part) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(5) + layout.addWidget(button_toggle) + layout.addWidget(button_toggle_text) + top_part.setLayout(layout) + + main_layout = QtWidgets.QVBoxLayout(self) + main_layout.setContentsMargins(9, 9, 9, 9) + + content_widget = QtWidgets.QWidget(self) + content_widget.setVisible(False) + + content_layout = QtWidgets.QVBoxLayout(content_widget) + content_layout.setContentsMargins(3, 3, 3, 3) + + main_layout.addWidget(top_part) + main_layout.addWidget(content_widget) + self.setLayout(main_layout) + + self.setAttribute(QtCore.Qt.WA_StyledBackground) + + self.top_part = top_part + self.button_toggle = button_toggle + self.button_toggle_text = button_toggle_text + + self.content_widget = content_widget + self.content_layout = content_layout + + self.top_part.clicked.connect(self._top_part_clicked) + self.button_toggle.clicked.connect(self.toggle_content) + + self.input_fields = [] + + self.key = input_data["key"] + keys = list(parent_keys) + keys.append(self.key) + self.keys = keys + + for child_data in input_data.get("children", []): + self.add_children_gui(child_data, values) + + def _top_part_clicked(self): + self.toggle_content(not self.button_toggle.isChecked()) + + def toggle_content(self, *args): + if len(args) > 0: + checked = args[0] + else: + checked = self.button_toggle.isChecked() + arrow_type = QtCore.Qt.RightArrow + if checked: + arrow_type = QtCore.Qt.DownArrow + self.button_toggle.setChecked(checked) + self.button_toggle.setArrowType(arrow_type) + self.content_widget.setVisible(checked) + self.parent().updateGeometry() + + def resizeEvent(self, event): + super(DictExpandWidget, self).resizeEvent(event) + self.content_widget.updateGeometry() + + @property + def is_overriden(self): + return self._is_overriden or self._parent.is_overriden + + @property + def ignore_value_changes(self): + return self._parent.ignore_value_changes + + def apply_overrides(self, override_value): + # Make sure this is set to False + self._is_overriden = False + self._state = None + self._child_state = None + for item in self.input_fields: + if override_value is None: + child_value = None + else: + child_value = override_value.get(item.key) + + item.apply_overrides(child_value) + + self._is_overriden = ( + self.is_group + and self.is_overidable + and ( + override_value is not None + or self.child_overriden + ) + ) + self.update_style() + + def _on_value_change(self, item=None): + if self.ignore_value_changes: + return + + if self.is_group: + if self.is_overidable: + self._is_overriden = True + + # TODO update items + if item is not None: + for _item in self.input_fields: + if _item is not item: + _item.update_style() + + self.value_changed.emit(self) + + self.update_style() + + def update_style(self, is_overriden=None): + child_modified = self.child_modified + child_state = self.style_state(self.child_overriden, child_modified) + if child_state: + child_state = "child-{}".format(child_state) + + if child_state != self._child_state: + self.setProperty("state", child_state) + self.style().polish(self) + self._child_state = child_state + + state = self.style_state(self.is_overriden, self.is_modified) + if self._state == state: + return + + self.button_toggle_text.setProperty("state", state) + self.button_toggle_text.style().polish(self.button_toggle_text) + + self._state = state + + @property + def is_modified(self): + if self.is_group: + return self.child_modified + return False + + @property + def child_modified(self): + for input_field in self.input_fields: + if input_field.child_modified: + return True + return False + + @property + def child_overriden(self): + for input_field in self.input_fields: + if input_field.child_overriden: + return True + return False + + def item_value(self): + output = {} + for input_field in self.input_fields: + # TODO maybe merge instead of update should be used + # NOTE merge is custom function which merges 2 dicts + output.update(input_field.config_value()) + return output + + def config_value(self): + return {self.key: self.item_value()} + + @property + def is_overidable(self): + return self._parent.is_overidable + + def add_children_gui(self, child_configuration, values): + item_type = child_configuration["type"] + klass = TypeToKlass.types.get(item_type) + + item = klass( + child_configuration, values, self.keys, self + ) + item.value_changed.connect(self._on_value_change) + self.content_layout.addWidget(item) + + self.input_fields.append(item) + return item + def overrides(self): - if not self.is_overiden: - return NOT_SET - return self.config_value() + 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(values.keys()) + if groups: + values[METADATA_KEY] = {"groups": groups} + return {self.key: values}, self.is_group + + +class DictInvisible(QtWidgets.QWidget, PypeConfigurationWidget): + # TODO is not overridable by itself + value_changed = QtCore.Signal(object) + + def __init__( + self, input_data, values, parent_keys, parent, label_widget=None + ): + self._parent = parent + + any_parent_is_group = parent.is_group + if not any_parent_is_group: + any_parent_is_group = parent.any_parent_is_group + + is_group = input_data.get("is_group", False) + if is_group and any_parent_is_group: + raise SchemeGroupHierarchyBug() + + self.any_parent_is_group = any_parent_is_group + + self._is_overriden = False + self.is_modified = False + self.is_group = is_group + + super(DictInvisible, self).__init__(parent) + self.setObjectName("DictInvisible") + + self.setAttribute(QtCore.Qt.WA_StyledBackground) + + layout = QtWidgets.QVBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(5) + + self.input_fields = [] + + if "key" not in input_data: + print(json.dumps(input_data, indent=4)) + + self.key = input_data["key"] + self.keys = list(parent_keys) + self.keys.append(self.key) + + for child_data in input_data.get("children", []): + self.add_children_gui(child_data, values) + + def update_style(self, *args, **kwargs): + return + + @property + def is_overriden(self): + return self._is_overriden or self._parent.is_overriden + + @property + def is_overidable(self): + return self._parent.is_overidable + + @property + def child_modified(self): + for input_field in self.input_fields: + if input_field.child_modified: + return True + return False + + @property + def child_overriden(self): + for input_field in self.input_fields: + if input_field.child_overriden: + return True + return False + + @property + def ignore_value_changes(self): + return self._parent.ignore_value_changes + + def item_value(self): + output = {} + for input_field in self.input_fields: + # TODO maybe merge instead of update should be used + # NOTE merge is custom function which merges 2 dicts + output.update(input_field.config_value()) + return output + + def config_value(self): + return {self.key: self.item_value()} + + def add_children_gui(self, child_configuration, values): + item_type = child_configuration["type"] + if item_type == "schema": + for _schema in child_configuration["children"]: + children = config.gui_schema(_schema) + self.add_children_gui(children, values) + return + + klass = TypeToKlass.types.get(item_type) + item = klass( + child_configuration, values, self.keys, self + ) + self.layout().addWidget(item) + + item.value_changed.connect(self._on_value_change) + + self.input_fields.append(item) + return item + + def _on_value_change(self, item=None): + if self.ignore_value_changes: + return + + if self.is_group: + if self.is_overidable: + self._is_overriden = True + # TODO update items + if item is not None: + is_overriden = self.is_overriden + for _item in self.input_fields: + if _item is not item: + _item.update_style(is_overriden) + + self.value_changed.emit(self) + + def apply_overrides(self, override_value): + self._is_overriden = False + for item in self.input_fields: + if override_value is None: + child_value = None + else: + child_value = override_value.get(item.key) + item.apply_overrides(child_value) + + self._is_overriden = ( + self.is_group + and self.is_overidable + and ( + override_value is not None + or self.child_overriden + ) + ) + self.update_style() + + 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(values.keys()) + if groups: + values[METADATA_KEY] = {"groups": groups} + return {self.key: values}, self.is_group + + +class DictFormWidget(QtWidgets.QWidget): + value_changed = QtCore.Signal(object) + + def __init__( + self, input_data, values, parent_keys, parent, label_widget=None + ): + self._parent = parent + + any_parent_is_group = parent.is_group + if not any_parent_is_group: + any_parent_is_group = parent.any_parent_is_group + + self.any_parent_is_group = any_parent_is_group + + self.is_modified = False + self._is_overriden = False + self.is_group = False + + super(DictFormWidget, self).__init__(parent) + + self.input_fields = {} + self.content_layout = QtWidgets.QFormLayout(self) + + self.keys = list(parent_keys) + + for child_data in input_data.get("children", []): + self.add_children_gui(child_data, values) + + def _on_value_change(self, item=None): + if self.ignore_value_changes: + return + self.value_changed.emit(self) + + @property + def is_overriden(self): + return self._parent.is_overriden + + @property + def child_modified(self): + for input_field in self.input_fields.values(): + if input_field.child_modified: + return True + return False + + @property + def child_overriden(self): + for input_field in self.input_fields.values(): + if input_field.child_overriden: + return True + return False + + @property + def is_overidable(self): + return self._parent.is_overidable + + @property + def ignore_value_changes(self): + return self._parent.ignore_value_changes + + def add_children_gui(self, child_configuration, values): + item_type = child_configuration["type"] + key = child_configuration["key"] + # Pop label to not be set in child + label = child_configuration["label"] + + klass = TypeToKlass.types.get(item_type) + + label_widget = QtWidgets.QLabel(label) + + item = klass( + child_configuration, values, self.keys, self, label_widget + ) + item.value_changed.connect(self._on_value_change) + self.content_layout.addRow(label_widget, item) + self.input_fields[key] = item + return item + + def item_value(self): + output = {} + for input_field in self.input_fields.values(): + # TODO maybe merge instead of update should be used + # NOTE merge is custom function which merges 2 dicts + output.update(input_field.config_value()) + return output + + def config_value(self): + return self.item_value() + + 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(values.keys()) + if groups: + values[METADATA_KEY] = {"groups": groups} + return {self.key: values}, self.is_group TypeToKlass.types["boolean"] = BooleanWidget diff --git a/pype/tools/config_setting/widgets/lib.py b/pype/tools/config_setting/widgets/lib.py index ac0a353d53..bf6d2d0fbd 100644 --- a/pype/tools/config_setting/widgets/lib.py +++ b/pype/tools/config_setting/widgets/lib.py @@ -41,4 +41,13 @@ class CustomNone: NOT_SET = CustomNone() -AS_WIDGET = CustomNone() +AS_WIDGET = type("AS_WIDGET", (), {}) +METADATA_KEY = type("METADATA_KEY", (), {}) + + +def convert_gui_data_to_overrides(data): + pass + + +def convert_overrides_to_gui_data(data): + pass From a200be829b7beb35b21fbe9c10b5466ae0a9750e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Wed, 19 Aug 2020 16:10:41 +0200 Subject: [PATCH 096/813] tile support for separate AOVs and better dep handling --- .../global/publish/submit_publish_job.py | 26 ++++---------- .../maya/publish/submit_maya_deadline.py | 35 ++++++++++++++----- 2 files changed, 34 insertions(+), 27 deletions(-) diff --git a/pype/plugins/global/publish/submit_publish_job.py b/pype/plugins/global/publish/submit_publish_job.py index 1a3d0df1b3..d2f38c328b 100644 --- a/pype/plugins/global/publish/submit_publish_job.py +++ b/pype/plugins/global/publish/submit_publish_job.py @@ -275,26 +275,14 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): # Mandatory for Deadline, may be empty "AuxFiles": [], } - """ - In this part we will add file dependencies instead of job dependencies. - This way we don't need to take care of tile assembly job, getting its - id or name. We expect it to produce specific file with specific name - and we are just waiting for them. - """ + + # add assembly jobs as dependencies if instance.data.get("tileRendering"): - self.log.info("Adding tile assembly results as dependencies...") - asset_index = 0 - for inst in instances: - for represenation in inst.get("representations", []): - if isinstance(represenation["files"], (list, tuple)): - for file in represenation["files"]: - dependency = os.path.join(output_dir, file) - payload["JobInfo"]["AssetDependency{}".format(asset_index)] = dependency # noqa: E501 - else: - dependency = os.path.join( - output_dir, represenation["files"]) - payload["JobInfo"]["AssetDependency{}".format(asset_index)] = dependency # noqa: E501 - asset_index += 1 + self.log.info("Adding tile assembly jobs as dependencies...") + job_index = 0 + for assembly_id in instance.data.get("assemblySubmissionJobs"): + payload["JobInfo"]["JobDependency{}".format(job_index)] = assembly_id # noqa: E501 + job_index += 1 else: payload["JobInfo"]["JobDependency0"] = job["_id"] diff --git a/pype/plugins/maya/publish/submit_maya_deadline.py b/pype/plugins/maya/publish/submit_maya_deadline.py index eeb6472850..5baea9d82b 100644 --- a/pype/plugins/maya/publish/submit_maya_deadline.py +++ b/pype/plugins/maya/publish/submit_maya_deadline.py @@ -24,6 +24,7 @@ import copy import re import hashlib from datetime import datetime +import itertools import clique import requests @@ -548,7 +549,7 @@ class MayaSubmitDeadline(pyblish.api.InstancePlugin): assembly_payload["JobInfo"].update(output_filenames) frame_payloads = [] - assembly_payloads = {} + assembly_payloads = [] R_FRAME_NUMBER = re.compile(r".+\.(?P[0-9]+)\..+") # noqa: N806, E501 REPL_FRAME_NUMBER = re.compile(r"(.+\.)([0-9]+)(\..+)") # noqa: N806, E501 @@ -557,11 +558,19 @@ class MayaSubmitDeadline(pyblish.api.InstancePlugin): # we have aovs and we need to iterate over them # get files from `beauty` files = exp[0].get("beauty") + # assembly files are used for assembly jobs as we need to put + # together all AOVs + assembly_files = list( + itertools.chain.from_iterable( + [f for _, f in exp[0].items()])) if not files: # if beauty doesn't exists, use first aov we found files = exp[0].get(list(exp[0].keys())[0]) else: files = exp + assembly_files = files + + frame_jobs = {} file_index = 1 for file in files: @@ -590,11 +599,16 @@ class MayaSubmitDeadline(pyblish.api.InstancePlugin): new_payload["PluginInfo"].update(tiles_data["PluginInfo"]) job_hash = hashlib.sha256("{}_{}".format(file_index, file)) + frame_jobs[frame] = job_hash.hexdigest() new_payload["JobInfo"]["ExtraInfo0"] = job_hash.hexdigest() new_payload["JobInfo"]["ExtraInfo1"] = file frame_payloads.append(new_payload) + file_index += 1 + file_index = 1 + for file in assembly_files: + frame = re.search(R_FRAME_NUMBER, file).group("frame") new_assembly_payload = copy.deepcopy(assembly_payload) new_assembly_payload["JobInfo"]["Name"] = \ "{} (Frame {})".format( @@ -604,9 +618,9 @@ class MayaSubmitDeadline(pyblish.api.InstancePlugin): REPL_FRAME_NUMBER, "\\1{}\\3".format("#" * len(frame)), file) - new_assembly_payload["JobInfo"]["ExtraInfo0"] = job_hash.hexdigest() # noqa: E501 + new_assembly_payload["JobInfo"]["ExtraInfo0"] = frame_jobs[frame] # noqa: E501 new_assembly_payload["JobInfo"]["ExtraInfo1"] = file - assembly_payloads[job_hash.hexdigest()] = new_assembly_payload + assembly_payloads.append(new_assembly_payload) file_index += 1 self.log.info( @@ -622,9 +636,13 @@ class MayaSubmitDeadline(pyblish.api.InstancePlugin): job_id = response.json()["_id"] hash = response.json()["Props"]["Ex0"] - file = response.json()["Props"]["Ex1"] - assembly_payloads[hash]["JobInfo"]["JobDependency0"] = job_id + for assembly_job in assembly_payloads: + if assembly_job["JobInfo"]["ExtraInfo0"] == hash: + assembly_job["JobInfo"]["JobDependency0"] = job_id + + for assembly_job in assembly_payloads: + file = assembly_job["JobInfo"]["ExtraInfo1"] # write assembly job config files now = datetime.now() @@ -646,7 +664,7 @@ class MayaSubmitDeadline(pyblish.api.InstancePlugin): os.path.dirname(config_file))) # add config file as job auxFile - assembly_payloads[hash]["AuxFiles"] = [config_file] + assembly_job["AuxFiles"] = [config_file] with open(config_file, "w") as cf: print("TileCount={}".format(tiles_count), file=cf) @@ -670,7 +688,7 @@ class MayaSubmitDeadline(pyblish.api.InstancePlugin): job_idx = 1 instance.data["assemblySubmissionJobs"] = [] - for _k, ass_job in assembly_payloads.items(): + for ass_job in assembly_payloads: self.log.info("submitting assembly job {} of {}".format( job_idx, len(assembly_payloads) )) @@ -679,7 +697,8 @@ class MayaSubmitDeadline(pyblish.api.InstancePlugin): if not response.ok: raise Exception(response.text) - instance.data["assemblySubmissionJobs"].append(ass_job) + instance.data["assemblySubmissionJobs"].append( + response.json()["_id"]) job_idx += 1 instance.data["jobBatchName"] = payload["JobInfo"]["BatchName"] From a2c8e8a088b8964ed9e9c5873388ca462dcaef94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Wed, 19 Aug 2020 16:31:41 +0200 Subject: [PATCH 097/813] assembly job priority --- pype/plugins/maya/publish/submit_maya_deadline.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pype/plugins/maya/publish/submit_maya_deadline.py b/pype/plugins/maya/publish/submit_maya_deadline.py index 5baea9d82b..d9ee7f9646 100644 --- a/pype/plugins/maya/publish/submit_maya_deadline.py +++ b/pype/plugins/maya/publish/submit_maya_deadline.py @@ -547,6 +547,8 @@ class MayaSubmitDeadline(pyblish.api.InstancePlugin): } } assembly_payload["JobInfo"].update(output_filenames) + assembly_payload["JobInfo"]["Priority"] = self._instance.data.get( + "priority", 50) frame_payloads = [] assembly_payloads = [] From f1d5ef05aae4a94a3cbf1877537a7b97b091b03a Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 19 Aug 2020 16:47:25 +0200 Subject: [PATCH 098/813] added conversion from override gui data to regular override and back --- pype/tools/config_setting/widgets/base.py | 4 +- pype/tools/config_setting/widgets/config.py | 11 +-- pype/tools/config_setting/widgets/inputs.py | 6 +- pype/tools/config_setting/widgets/lib.py | 80 +++++++++++---------- 4 files changed, 52 insertions(+), 49 deletions(-) diff --git a/pype/tools/config_setting/widgets/base.py b/pype/tools/config_setting/widgets/base.py index 156f1f80e4..bdacb60559 100644 --- a/pype/tools/config_setting/widgets/base.py +++ b/pype/tools/config_setting/widgets/base.py @@ -3,7 +3,7 @@ import json from Qt import QtWidgets, QtCore, QtGui from . import config from .widgets import UnsavedChangesDialog -from .lib import NOT_SET, METADATA_KEY +from .lib import NOT_SET, METADATA_KEY, convert_gui_data_to_overrides from avalon import io @@ -404,7 +404,7 @@ class ProjectWidget(QtWidgets.QWidget, PypeConfigurationWidget): if groups: data[METADATA_KEY] = {"groups": groups} - output = convert_to_override(data) + output = convert_gui_data_to_overrides(data) print(json.dumps(output, indent=4)) def _save_defaults(self): diff --git a/pype/tools/config_setting/widgets/config.py b/pype/tools/config_setting/widgets/config.py index 8319c3d51d..62a3adb782 100644 --- a/pype/tools/config_setting/widgets/config.py +++ b/pype/tools/config_setting/widgets/config.py @@ -23,8 +23,9 @@ project_presets_path = os.path.normpath( ) first_run = False -OVERRIDE_KEY = "__overriden__" -POP_KEY = "__popkey__" +OVERRIDEN_KEY = "__overriden_keys__" +# TODO key popping not implemented yet +POP_KEY = "__pop_key__" def load_json(fpath): @@ -178,8 +179,8 @@ def project_preset_overrides(project_name, **kwargs): def merge_overrides(global_dict, override_dict): - if OVERRIDE_KEY in override_dict: - _override = override_dict.pop(OVERRIDE_KEY) + if OVERRIDEN_KEY in override_dict: + _override = override_dict.pop(OVERRIDEN_KEY) if _override: return override_dict @@ -187,7 +188,7 @@ def merge_overrides(global_dict, override_dict): if value == POP_KEY: global_dict.pop(key) - elif key == OVERRIDE_KEY: + elif key == OVERRIDEN_KEY: continue elif key not in global_dict: diff --git a/pype/tools/config_setting/widgets/inputs.py b/pype/tools/config_setting/widgets/inputs.py index bb2d76fc71..1840572cfb 100644 --- a/pype/tools/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/widgets/inputs.py @@ -1858,7 +1858,7 @@ class DictExpandWidget(QtWidgets.QWidget, PypeConfigurationWidget): if value is not NOT_SET: values.update(value) if is_group: - groups.extend(values.keys()) + groups.extend(value.keys()) if groups: values[METADATA_KEY] = {"groups": groups} return {self.key: values}, self.is_group @@ -2013,7 +2013,7 @@ class DictInvisible(QtWidgets.QWidget, PypeConfigurationWidget): if value is not NOT_SET: values.update(value) if is_group: - groups.extend(values.keys()) + groups.extend(value.keys()) if groups: values[METADATA_KEY] = {"groups": groups} return {self.key: values}, self.is_group @@ -2118,7 +2118,7 @@ class DictFormWidget(QtWidgets.QWidget): if value is not NOT_SET: values.update(value) if is_group: - groups.extend(values.keys()) + groups.extend(value.keys()) if groups: values[METADATA_KEY] = {"groups": groups} return {self.key: values}, self.is_group diff --git a/pype/tools/config_setting/widgets/lib.py b/pype/tools/config_setting/widgets/lib.py index bf6d2d0fbd..fd3f45b590 100644 --- a/pype/tools/config_setting/widgets/lib.py +++ b/pype/tools/config_setting/widgets/lib.py @@ -1,53 +1,55 @@ -import uuid +from .config import OVERRIDEN_KEY class CustomNone: - """Created object can be used as custom None (not equal to None). - - WARNING: Multiple created objects are not equal either. - Exmple: - >>> a = CustomNone() - >>> a == None - False - >>> b = CustomNone() - >>> a == b - False - >>> a == a - True - """ - - def __init__(self): - """Create uuid as identifier for custom None.""" - self.identifier = str(uuid.uuid4()) - + """Created object can be used as custom None (not equal to None).""" def __bool__(self): """Return False (like default None).""" return False - def __eq__(self, other): - """Equality is compared by identifier value.""" - if type(other) == type(self): - if other.identifier == self.identifier: - return True - return False - - def __str__(self): - """Return value of identifier when converted to string.""" - return "".format(str(self.identifier)) - - def __repr__(self): - """Representation of custom None.""" - return "".format(str(self.identifier)) - NOT_SET = CustomNone() AS_WIDGET = type("AS_WIDGET", (), {}) + METADATA_KEY = type("METADATA_KEY", (), {}) - -def convert_gui_data_to_overrides(data): - pass +OVERRIDE_VERSION = 1 -def convert_overrides_to_gui_data(data): - pass +def convert_gui_data_to_overrides(data, first=True): + if not data or not isinstance(data, dict): + return data + + output = {} + if first: + output["__override_version__"] = OVERRIDE_VERSION + + if METADATA_KEY in data: + metadata = data.pop(METADATA_KEY) + for key, value in metadata.items(): + if key == "groups": + print("**", value) + output[OVERRIDEN_KEY] = value + else: + KeyError("Unknown metadata key \"{}\"".format(key)) + + for key, value in data.items(): + output[key] = convert_gui_data_to_overrides(value, False) + return output + + +def convert_overrides_to_gui_data(data, first=True): + if not data or not isinstance(data, dict): + return data + + output = {} + if OVERRIDEN_KEY in data: + groups = data.pop(OVERRIDEN_KEY) + if METADATA_KEY not in output: + output[METADATA_KEY] = {} + output[METADATA_KEY]["groups"] = groups + + for key, value in data.items(): + output[key] = convert_overrides_to_gui_data(value, False) + + return output From c161a637433bf8b7b0b211dced4c4a99c919ad47 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 19 Aug 2020 18:05:03 +0100 Subject: [PATCH 099/813] Fix alembic settings being reset when updating reference. --- pype/hosts/maya/plugin.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/pype/hosts/maya/plugin.py b/pype/hosts/maya/plugin.py index ed244d56df..3b002eed10 100644 --- a/pype/hosts/maya/plugin.py +++ b/pype/hosts/maya/plugin.py @@ -174,6 +174,18 @@ class ReferenceLoader(api.Loader): assert os.path.exists(path), "%s does not exist." % path + # Need to save alembic settings and reapply, cause referencing resets + # them to incoming data. + alembic_attrs = ["speed", "offset", "cycleType"] + alembic_data = {} + if representation["name"] == "abc": + alembic_node = cmds.ls( + cmds.sets(node, query=True), type="AlembicNode" + )[0] + for attr in alembic_attrs: + node_attr = "{}.{}".format(alembic_node, attr) + alembic_data[attr] = cmds.getAttr(node_attr) + try: content = cmds.file(path, loadReference=reference_node, @@ -195,6 +207,21 @@ class ReferenceLoader(api.Loader): self.log.warning("Ignoring file read error:\n%s", exc) + # Reapply alembic settings. + if representation["name"] == "abc": + alembic_node = None + for member in cmds.sets(node, query=True): + shapes = cmds.listRelatives(member, shapes=True) + if shapes: + nodes = cmds.listConnections(shapes[0], type="AlembicNode") + if nodes: + alembic_node = nodes[0] + break + + for attr in alembic_attrs: + value = alembic_data[attr] + cmds.setAttr("{}.{}".format(alembic_node, attr), value) + # Fix PLN-40 for older containers created with Avalon that had the # `.verticesOnlySet` set to True. if cmds.getAttr("{}.verticesOnlySet".format(node)): From fe3cfc24422580c99f837c6bf02de029e80d54a9 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 19 Aug 2020 20:50:14 +0200 Subject: [PATCH 100/813] Added group_selected_layers, get_selected_layers, import_smart_object, replace_smart_object Fixed imprint for performance --- .../clients/photoshop_client.py | 157 ++++++++++++++---- .../websocket_server/websocket_server.py | 6 +- pype/plugins/photoshop/load/load_image.py | 9 +- .../photoshop/publish/collect_instances.py | 6 +- 4 files changed, 138 insertions(+), 40 deletions(-) diff --git a/pype/modules/websocket_server/clients/photoshop_client.py b/pype/modules/websocket_server/clients/photoshop_client.py index 6d3615e1a4..eea297954f 100644 --- a/pype/modules/websocket_server/clients/photoshop_client.py +++ b/pype/modules/websocket_server/clients/photoshop_client.py @@ -6,18 +6,38 @@ from pype.modules.websocket_server import WebSocketServer import json from collections import namedtuple + class PhotoshopClientStub(): + """ + Stub for calling function on client (Photoshop js) side. + Expects that client is already connected (started when avalon menu + is opened). + """ def __init__(self): self.websocketserver = WebSocketServer.get_instance() self.client = self.websocketserver.get_client() def read(self, layer): + """ + Parses layer metadata from Headline field of active document + :param layer: + :return: + """ layers_data = self._get_layers_metadata() return layers_data.get(str(layer.id)) - def imprint(self, layer, data): + def imprint(self, layer, data, all_layers=None): + """ + Save layer metadata to Headline field of active document + :param layer: Layer("id": XXX, "name":'YYY') + :param data: json representation for single layer + :param all_layers: - for performance, could be + injected for usage in loop, if not, single call will be + triggered + :return: None + """ layers_data = self._get_layers_metadata() # json.dumps writes integer values in a dictionary to string, so # anticipating it here. @@ -27,7 +47,9 @@ class PhotoshopClientStub(): layers_data[str(layer.id)] = data # Ensure only valid ids are stored. - layer_ids = [layer.id for layer in self.get_layers()] + if not all_layers: + all_layers = self.get_layers() + layer_ids = [layer.id for layer in all_layers] cleaned_data = {} for id in layers_data: @@ -36,10 +58,9 @@ class PhotoshopClientStub(): payload = json.dumps(cleaned_data, indent=4) - res = self.websocketserver.call(self.client.call - ('Photoshop.imprint', - payload=payload) - ) + self.websocketserver.call(self.client.call + ('Photoshop.imprint', payload=payload) + ) def get_layers(self): """ @@ -51,20 +72,10 @@ class PhotoshopClientStub(): 'type': 'GUIDE'|'FG'|'BG'|'OBJ' 'visible': 'true'|'false' """ - layers = {} res = self.websocketserver.call(self.client.call ('Photoshop.get_layers')) - print("get_layers:: {}".format(res)) - try: - layers_data = json.loads(res) - except json.decoder.JSONDecodeError: - raise ValueError("Received broken JSON {}".format(res)) - ret = [] - # convert to namedtuple to use dot donation - for d in layers_data: - ret.append(namedtuple('Layer', d.keys())(*d.values())) - return ret + return self._to_records(res) def get_layers_in_layers(self, layers): """ @@ -87,14 +98,35 @@ class PhotoshopClientStub(): return ret + def group_selected_layers(self): + """ + Group selected layers into new layer + :return: + """ + self.websocketserver.call(self.client.call + ('Photoshop.group_selected_layers')) + + def get_selected_layers(self): + """ + Get a list of actually selected layers + :return: + """ + res = self.websocketserver.call(self.client.call + ('Photoshop.get_selected_layers')) + return self._to_records(res) def select_layers(self, layers): + """ + Selecte specified layers in Photoshop + :param layers: + :return: None + """ layer_ids = [layer.id for layer in layers] - res = self.websocketserver.call(self.client.call - ('Photoshop.get_layers', - layers=layer_ids) - ) + self.websocketserver.call(self.client.call + ('Photoshop.get_layers', + layers=layer_ids) + ) def get_active_document_full_name(self): """ @@ -121,25 +153,41 @@ class PhotoshopClientStub(): Saves active document :return: None """ - res = self.websocketserver.call(self.client.call - ('Photoshop.save')) - + self.websocketserver.call(self.client.call + ('Photoshop.save')) def saveAs(self, image_path, ext, as_copy): - res = self.websocketserver.call(self.client.call - ('Photoshop.saveAs', - image_path=image_path, - ext=ext, - as_copy=as_copy)) + """ + Saves active document to psd (copy) or png or jpg + :param image_path: full local path + :param ext: + :param as_copy: + :return: None + """ + self.websocketserver.call(self.client.call + ('Photoshop.saveAs', + image_path=image_path, + ext=ext, + as_copy=as_copy)) def set_visible(self, layer_id, visibility): - print("set_visible {}, {}".format(layer_id, visibility)) - res = self.websocketserver.call(self.client.call - ('Photoshop.set_visible', - layer_id=layer_id, - visibility=visibility)) + """ + Set layer with 'layer_id' to 'visibility' + :param layer_id: + :param visibility: + :return: None + """ + self.websocketserver.call(self.client.call + ('Photoshop.set_visible', + layer_id=layer_id, + visibility=visibility)) def _get_layers_metadata(self): + """ + Reads layers metadata from Headline from active document in PS. + (Headline accessible by File > File Info) + :return: - json documents + """ layers_data = {} res = self.websocketserver.call(self.client.call('Photoshop.read')) try: @@ -148,6 +196,47 @@ class PhotoshopClientStub(): pass return layers_data + def import_smart_object(self, path): + """ + Import the file at `path` as a smart object to active document. + + Args: + path (str): File path to import. + """ + + def replace_smart_object(self, layer, path): + """ + Replace the smart object `layer` with file at `path` + + Args: + layer (namedTuple): Layer("id":XX, "name":"YY"..). + path (str): File to import. + """ + self.websocketserver.call(self.client.call + ('Photoshop.replace_smart_object', + layer=layer, + path=path)) + def close(self): self.client.close() + def _to_records(self, res): + """ + Converts string json representation into list of named tuples for + dot notation access to work. + :return: + :param res: - json representation + """ + try: + layers_data = json.loads(res) + except json.decoder.JSONDecodeError: + raise ValueError("Received broken JSON {}".format(res)) + ret = [] + # convert to namedtuple to use dot donation + for d in layers_data: + ret.append(namedtuple('Layer', d.keys())(*d.values())) + return ret + + + + diff --git a/pype/modules/websocket_server/websocket_server.py b/pype/modules/websocket_server/websocket_server.py index f9be7c88a9..02fde4d56a 100644 --- a/pype/modules/websocket_server/websocket_server.py +++ b/pype/modules/websocket_server/websocket_server.py @@ -96,8 +96,10 @@ class WebSocketServer(): :return: client """ clients = WebSocketAsync.get_clients() - key = list(clients.keys())[0] - client = clients.get(key) + client = None + if len(clients) > 0: + key = list(clients.keys())[0] + client = clients.get(key) return client diff --git a/pype/plugins/photoshop/load/load_image.py b/pype/plugins/photoshop/load/load_image.py index 18efe750d5..0e437b15ba 100644 --- a/pype/plugins/photoshop/load/load_image.py +++ b/pype/plugins/photoshop/load/load_image.py @@ -1,5 +1,10 @@ from avalon import api, photoshop +from pype.modules.websocket_server.clients.photoshop_client \ + import PhotoshopClientStub + +photoshopClient = PhotoshopClientStub() + class ImageLoader(api.Loader): """Load images @@ -28,11 +33,11 @@ class ImageLoader(api.Loader): layer = container.pop("layer") with photoshop.maintained_selection(): - photoshop.replace_smart_object( + photoshopClient.replace_smart_object( layer, api.get_representation_path(representation) ) - photoshop.imprint( + photoshopClient.imprint( layer, {"representation": str(representation["_id"])} ) diff --git a/pype/plugins/photoshop/publish/collect_instances.py b/pype/plugins/photoshop/publish/collect_instances.py index d94adde00b..f2d1c141fd 100644 --- a/pype/plugins/photoshop/publish/collect_instances.py +++ b/pype/plugins/photoshop/publish/collect_instances.py @@ -4,7 +4,9 @@ from avalon import photoshop import pyblish.api -from pype.modules.websocket_server.clients.photoshop_client import PhotoshopClientStub +from pype.modules.websocket_server.clients.photoshop_client \ + import PhotoshopClientStub + class CollectInstances(pyblish.api.ContextPlugin): """Gather instances by LayerSet and file metadata @@ -38,7 +40,7 @@ class CollectInstances(pyblish.api.ContextPlugin): layer_data = photoshop_client.read(layer) self.log.info("layer_data {}".format(layer_data)) - photoshop_client.imprint(layer, layer_data) + photoshop_client.imprint(layer, layer_data, layers) new_layer_data = photoshop_client.read(layer) assert layer_data == new_layer_data From 180035731b329fe617460389bebdc995e79260ab Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 19 Aug 2020 21:10:51 +0200 Subject: [PATCH 101/813] Hound --- .../websocket_server/hosts/photoshop.py | 5 ++-- .../websocket_server/websocket_server.py | 2 +- pype/plugins/photoshop/load/load_image.py | 2 +- .../photoshop/publish/collect_current_file.py | 3 +- .../photoshop/publish/collect_instances.py | 7 +---- .../photoshop/publish/extract_image.py | 4 +-- .../photoshop/publish/extract_review.py | 28 ++----------------- .../photoshop/publish/extract_save_scene.py | 7 ++--- .../photoshop/publish/increment_workfile.py | 4 +-- .../publish/validate_instance_asset.py | 2 -- .../photoshop/publish/validate_naming.py | 3 +- 11 files changed, 17 insertions(+), 50 deletions(-) diff --git a/pype/modules/websocket_server/hosts/photoshop.py b/pype/modules/websocket_server/hosts/photoshop.py index 9092530e48..c63f66865e 100644 --- a/pype/modules/websocket_server/hosts/photoshop.py +++ b/pype/modules/websocket_server/hosts/photoshop.py @@ -1,5 +1,3 @@ -import asyncio - from pype.api import Logger from wsrpc_aiohttp import WebSocketRoute @@ -30,5 +28,6 @@ class Photoshop(WebSocketRoute): # client functions async def read(self): - log.debug("photoshop.read client calls server server calls Photo client") + log.debug("photoshop.read client calls server server calls " + "Photo client") return await self.socket.call('Photoshop.read') diff --git a/pype/modules/websocket_server/websocket_server.py b/pype/modules/websocket_server/websocket_server.py index 02fde4d56a..6b730d4eb3 100644 --- a/pype/modules/websocket_server/websocket_server.py +++ b/pype/modules/websocket_server/websocket_server.py @@ -105,7 +105,7 @@ class WebSocketServer(): @staticmethod def get_instance(): - if WebSocketServer._instance == None: + if WebSocketServer._instance is None: WebSocketServer() return WebSocketServer._instance diff --git a/pype/plugins/photoshop/load/load_image.py b/pype/plugins/photoshop/load/load_image.py index 0e437b15ba..a24280553c 100644 --- a/pype/plugins/photoshop/load/load_image.py +++ b/pype/plugins/photoshop/load/load_image.py @@ -1,7 +1,7 @@ from avalon import api, photoshop from pype.modules.websocket_server.clients.photoshop_client \ - import PhotoshopClientStub + import PhotoshopClientStub photoshopClient = PhotoshopClientStub() diff --git a/pype/plugins/photoshop/publish/collect_current_file.py b/pype/plugins/photoshop/publish/collect_current_file.py index bb81718bcc..604ce97f89 100644 --- a/pype/plugins/photoshop/publish/collect_current_file.py +++ b/pype/plugins/photoshop/publish/collect_current_file.py @@ -1,10 +1,9 @@ import os import pyblish.api -from avalon import photoshop from pype.modules.websocket_server.clients.photoshop_client import \ - PhotoshopClientStub + PhotoshopClientStub class CollectCurrentFile(pyblish.api.ContextPlugin): diff --git a/pype/plugins/photoshop/publish/collect_instances.py b/pype/plugins/photoshop/publish/collect_instances.py index f2d1c141fd..82a0c35311 100644 --- a/pype/plugins/photoshop/publish/collect_instances.py +++ b/pype/plugins/photoshop/publish/collect_instances.py @@ -1,11 +1,9 @@ import pythoncom -from avalon import photoshop - import pyblish.api from pype.modules.websocket_server.clients.photoshop_client \ - import PhotoshopClientStub + import PhotoshopClientStub class CollectInstances(pyblish.api.ContextPlugin): @@ -30,9 +28,6 @@ class CollectInstances(pyblish.api.ContextPlugin): # can be. pythoncom.CoInitialize() - from datetime import datetime - start = datetime.now() - # for timing photoshop_client = PhotoshopClientStub() layers = photoshop_client.get_layers() diff --git a/pype/plugins/photoshop/publish/extract_image.py b/pype/plugins/photoshop/publish/extract_image.py index 0451308ef1..4cbac38ce7 100644 --- a/pype/plugins/photoshop/publish/extract_image.py +++ b/pype/plugins/photoshop/publish/extract_image.py @@ -4,7 +4,8 @@ import pype.api from avalon import photoshop from pype.modules.websocket_server.clients.photoshop_client import \ - PhotoshopClientStub + PhotoshopClientStub + class ExtractImage(pype.api.Extractor): """Produce a flattened image file from instance @@ -65,7 +66,6 @@ class ExtractImage(pype.api.Extractor): extension, True) - representations = [] for extension, filename in files.items(): representations.append({ diff --git a/pype/plugins/photoshop/publish/extract_review.py b/pype/plugins/photoshop/publish/extract_review.py index 1c3aeaffb5..1e8d726720 100644 --- a/pype/plugins/photoshop/publish/extract_review.py +++ b/pype/plugins/photoshop/publish/extract_review.py @@ -4,9 +4,8 @@ import pype.api import pype.lib from avalon import photoshop -from datetime import datetime from pype.modules.websocket_server.clients.photoshop_client import \ - PhotoshopClientStub + PhotoshopClientStub class ExtractReview(pype.api.Extractor): @@ -17,7 +16,6 @@ class ExtractReview(pype.api.Extractor): families = ["review"] def process(self, instance): - start = datetime.now() staging_dir = self.staging_dir(instance) self.log.info("Outputting image to {}".format(staging_dir)) @@ -34,14 +32,10 @@ class ExtractReview(pype.api.Extractor): os.path.splitext(photoshop_client.get_active_document_name())[0] ) output_image_path = os.path.join(staging_dir, output_image) - self.log.info( - "first part took {}".format(datetime.now() - start)) with photoshop.maintained_visibility(): # Hide all other layers. - start = datetime.now() extract_ids = set([ll.id for ll in photoshop_client. - get_layers_in_layers(layers)]) - self.log.info("extract_ids {}".format(extract_ids)) + get_layers_in_layers(layers)]) for layer in photoshop_client.get_layers(): # limit unnecessary calls to client @@ -51,19 +45,11 @@ class ExtractReview(pype.api.Extractor): if not layer.visible and layer.id in extract_ids: photoshop_client.set_visible(layer.id, True) - self.log.info( - "get_layers_in_layers took {}".format(datetime.now() - start)) - start = datetime.now() - - self.log.info("output_image_path {}".format(output_image_path)) photoshop_client.saveAs(output_image_path, 'jpg', True) - self.log.info( - "saveAs {} took {}".format('JPG', datetime.now() - start)) - start = datetime.now() ffmpeg_path = pype.lib.get_ffmpeg_tool_path("ffmpeg") instance.data["representations"].append({ @@ -84,9 +70,6 @@ class ExtractReview(pype.api.Extractor): thumbnail_path ] output = pype.lib._subprocess(args) - self.log.info( - "thumbnail {} took {}".format('JPG', datetime.now() - start)) - self.log.debug(output) instance.data["representations"].append({ "name": "thumbnail", @@ -95,7 +78,6 @@ class ExtractReview(pype.api.Extractor): "stagingDir": staging_dir, "tags": ["thumbnail"] }) - start = datetime.now() # Generate mov. mov_path = os.path.join(staging_dir, "review.mov") args = [ @@ -106,10 +88,7 @@ class ExtractReview(pype.api.Extractor): mov_path ] output = pype.lib._subprocess(args) - self.log.info( - "review {} took {}".format('JPG', datetime.now() - start)) self.log.debug(output) - start = datetime.now() instance.data["representations"].append({ "name": "mov", "ext": "mov", @@ -126,6 +105,5 @@ class ExtractReview(pype.api.Extractor): instance.data["frameStart"] = 1 instance.data["frameEnd"] = 1 instance.data["fps"] = 25 - self.log.info( - "end {} took {}".format('JPG', datetime.now() - start)) + self.log.info(f"Extracted {instance} to {staging_dir}") diff --git a/pype/plugins/photoshop/publish/extract_save_scene.py b/pype/plugins/photoshop/publish/extract_save_scene.py index ea7bdda9af..3357a05f24 100644 --- a/pype/plugins/photoshop/publish/extract_save_scene.py +++ b/pype/plugins/photoshop/publish/extract_save_scene.py @@ -2,9 +2,9 @@ import pype.api from avalon import photoshop from pype.modules.websocket_server.clients.photoshop_client import \ - PhotoshopClientStub + PhotoshopClientStub + -from datetime import datetime class ExtractSaveScene(pype.api.Extractor): """Save scene before extraction.""" @@ -15,7 +15,4 @@ class ExtractSaveScene(pype.api.Extractor): def process(self, instance): photoshop_client = PhotoshopClientStub() - start = datetime.now() photoshop_client.save() - self.log.info( - "ExtractSaveScene took {}".format(datetime.now() - start)) diff --git a/pype/plugins/photoshop/publish/increment_workfile.py b/pype/plugins/photoshop/publish/increment_workfile.py index 0ae7e9772f..4298eb8e77 100644 --- a/pype/plugins/photoshop/publish/increment_workfile.py +++ b/pype/plugins/photoshop/publish/increment_workfile.py @@ -1,10 +1,10 @@ import pyblish.api from pype.action import get_errored_plugins_from_data from pype.lib import version_up -from avalon import photoshop from pype.modules.websocket_server.clients.photoshop_client import \ - PhotoshopClientStub + PhotoshopClientStub + class IncrementWorkfile(pyblish.api.InstancePlugin): """Increment the current workfile. diff --git a/pype/plugins/photoshop/publish/validate_instance_asset.py b/pype/plugins/photoshop/publish/validate_instance_asset.py index 6a0a408878..4bbea69eb4 100644 --- a/pype/plugins/photoshop/publish/validate_instance_asset.py +++ b/pype/plugins/photoshop/publish/validate_instance_asset.py @@ -27,8 +27,6 @@ class ValidateInstanceAssetRepair(pyblish.api.Action): instances = pyblish.api.instances_by_plugin(failed, plugin) photoshop_client = PhotoshopClientStub() for instance in instances: - self.log.info("validate_instance_asset instance[0] {}".format(instance[0])) - self.log.info("validate_instance_asset instance {}".format(instance)) data = photoshop_client.read(instance[0]) data["asset"] = os.environ["AVALON_ASSET"] diff --git a/pype/plugins/photoshop/publish/validate_naming.py b/pype/plugins/photoshop/publish/validate_naming.py index 7734a0e5a0..ba8a3e997e 100644 --- a/pype/plugins/photoshop/publish/validate_naming.py +++ b/pype/plugins/photoshop/publish/validate_naming.py @@ -3,7 +3,8 @@ import pype.api from avalon import photoshop from pype.modules.websocket_server.clients.photoshop_client import \ - PhotoshopClientStub + PhotoshopClientStub + class ValidateNamingRepair(pyblish.api.Action): """Repair the instance asset.""" From 91e65b1f15a3b7b5f795d0e6fbbbc6755a957391 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 20 Aug 2020 00:09:28 +0100 Subject: [PATCH 102/813] Safer alembic node search. --- pype/hosts/maya/plugin.py | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/pype/hosts/maya/plugin.py b/pype/hosts/maya/plugin.py index 3b002eed10..a5c57f1ab8 100644 --- a/pype/hosts/maya/plugin.py +++ b/pype/hosts/maya/plugin.py @@ -179,12 +179,19 @@ class ReferenceLoader(api.Loader): alembic_attrs = ["speed", "offset", "cycleType"] alembic_data = {} if representation["name"] == "abc": - alembic_node = cmds.ls( - cmds.sets(node, query=True), type="AlembicNode" - )[0] - for attr in alembic_attrs: - node_attr = "{}.{}".format(alembic_node, attr) - alembic_data[attr] = cmds.getAttr(node_attr) + alembic_nodes = cmds.ls( + "{}:*".format(members[0].split(":")[0]), type="AlembicNode" + ) + if alembic_nodes: + for attr in alembic_attrs: + node_attr = "{}.{}".format(alembic_nodes[0], attr) + alembic_data[attr] = cmds.getAttr(node_attr) + else: + cmds.warning( + "No alembic nodes found in {}".format( + cmds.ls("{}:*".format(members[0].split(":")[0])) + ) + ) try: content = cmds.file(path, @@ -209,18 +216,13 @@ class ReferenceLoader(api.Loader): # Reapply alembic settings. if representation["name"] == "abc": - alembic_node = None - for member in cmds.sets(node, query=True): - shapes = cmds.listRelatives(member, shapes=True) - if shapes: - nodes = cmds.listConnections(shapes[0], type="AlembicNode") - if nodes: - alembic_node = nodes[0] - break - - for attr in alembic_attrs: - value = alembic_data[attr] - cmds.setAttr("{}.{}".format(alembic_node, attr), value) + alembic_nodes = cmds.ls( + "{}:*".format(members[0].split(":")[0]), type="AlembicNode" + ) + if alembic_nodes: + for attr in alembic_attrs: + value = alembic_data[attr] + cmds.setAttr("{}.{}".format(alembic_nodes[0], attr), value) # Fix PLN-40 for older containers created with Avalon that had the # `.verticesOnlySet` set to True. From eb74ea9253a42e33e1f034bec2c1d83829e39834 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 20 Aug 2020 10:03:59 +0200 Subject: [PATCH 103/813] get rid of metadata loading --- pype/tools/config_setting/widgets/config.py | 36 +++------------------ 1 file changed, 5 insertions(+), 31 deletions(-) diff --git a/pype/tools/config_setting/widgets/config.py b/pype/tools/config_setting/widgets/config.py index 62a3adb782..8d9d4fa1d2 100644 --- a/pype/tools/config_setting/widgets/config.py +++ b/pype/tools/config_setting/widgets/config.py @@ -86,31 +86,16 @@ def load_json(fpath): return {} -def subkey_merge(_dict, value, keys, with_metadata=False): +def subkey_merge(_dict, value, keys): key = keys.pop(0) if not keys: - if with_metadata: - _dict[key] = {"type": "file", "value": value} - else: - _dict[key] = value + _dict[key] = value return _dict if key not in _dict: - if with_metadata: - _dict[key] = {"type": "folder", "value": {}} - else: - _dict[key] = {} + _dict[key] = {} + _dict[key] = subkey_merge(_dict[key], value, keys) - if with_metadata: - sub_dict = _dict[key]["value"] - else: - sub_dict = _dict[key] - - _value = subkey_merge(sub_dict, value, keys, with_metadata) - if with_metadata: - _dict[key]["value"] = _value - else: - _dict[key] = _value return _dict @@ -122,7 +107,6 @@ def load_jsons_from_dir(path, *args, **kwargs): # TODO warning return output - with_metadata = kwargs.get("with_metadata") sub_keys = list(kwargs.pop("subkeys", args)) for sub_key in tuple(sub_keys): _path = os.path.join(path, sub_key) @@ -143,7 +127,7 @@ def load_jsons_from_dir(path, *args, **kwargs): # dict_path = os.path.join(base[base_len:], basename) # dict_keys = dict_path.split(os.path.sep) dict_keys = base[base_len:].split(os.path.sep) + [basename] - output = subkey_merge(output, value, dict_keys, with_metadata) + output = subkey_merge(output, value, dict_keys) for sub_key in sub_keys: output = output[sub_key] @@ -158,16 +142,6 @@ def global_project_presets(**kwargs): return load_jsons_from_dir(project_presets_path, **kwargs) -def studio_presets_with_metadata(*args, **kwargs): - kwargs["with_metadata"] = True - return load_jsons_from_dir(studio_presets_path, *args, **kwargs) - - -def global_project_presets_with_metadata(**kwargs): - kwargs["with_metadata"] = True - return load_jsons_from_dir(project_presets_path, **kwargs) - - def project_preset_overrides(project_name, **kwargs): project_configs_path = os.environ.get("PYPE_PROJECT_CONFIGS") if project_name and project_configs_path: From de9aca5f8f3ecdb3e635079c4cbd37c79b1c91a7 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 20 Aug 2020 10:52:18 +0200 Subject: [PATCH 104/813] Speedup of collect_instances.py --- .../clients/photoshop_client.py | 25 +++++++++++-------- .../photoshop/publish/collect_instances.py | 14 ++++------- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/pype/modules/websocket_server/clients/photoshop_client.py b/pype/modules/websocket_server/clients/photoshop_client.py index eea297954f..bf72c1bc5a 100644 --- a/pype/modules/websocket_server/clients/photoshop_client.py +++ b/pype/modules/websocket_server/clients/photoshop_client.py @@ -18,17 +18,19 @@ class PhotoshopClientStub(): self.websocketserver = WebSocketServer.get_instance() self.client = self.websocketserver.get_client() - def read(self, layer): + def read(self, layer, layers_meta=None): """ Parses layer metadata from Headline field of active document - :param layer: + :param layer: Layer("id": XXX, "name":'YYY') @@ -38,13 +40,14 @@ class PhotoshopClientStub(): triggered :return: None """ - layers_data = self._get_layers_metadata() + if not layers_meta: + layers_meta = self._get_layers_metadata() # json.dumps writes integer values in a dictionary to string, so # anticipating it here. - if str(layer.id) in layers_data: - layers_data[str(layer.id)].update(data) + if str(layer.id) in layers_meta and layers_meta[str(layer.id)]: + layers_meta[str(layer.id)].update(data) else: - layers_data[str(layer.id)] = data + layers_meta[str(layer.id)] = data # Ensure only valid ids are stored. if not all_layers: @@ -52,9 +55,9 @@ class PhotoshopClientStub(): layer_ids = [layer.id for layer in all_layers] cleaned_data = {} - for id in layers_data: + for id in layers_meta: if int(id) in layer_ids: - cleaned_data[id] = layers_data[id] + cleaned_data[id] = layers_meta[id] payload = json.dumps(cleaned_data, indent=4) diff --git a/pype/plugins/photoshop/publish/collect_instances.py b/pype/plugins/photoshop/publish/collect_instances.py index 82a0c35311..47584272d2 100644 --- a/pype/plugins/photoshop/publish/collect_instances.py +++ b/pype/plugins/photoshop/publish/collect_instances.py @@ -2,8 +2,9 @@ import pythoncom import pyblish.api -from pype.modules.websocket_server.clients.photoshop_client \ - import PhotoshopClientStub +from pype.modules.websocket_server.clients.photoshop_client import ( + PhotoshopClientStub +) class CollectInstances(pyblish.api.ContextPlugin): @@ -30,14 +31,9 @@ class CollectInstances(pyblish.api.ContextPlugin): photoshop_client = PhotoshopClientStub() layers = photoshop_client.get_layers() - + layers_meta = photoshop_client._get_layers_metadata() for layer in layers: - layer_data = photoshop_client.read(layer) - self.log.info("layer_data {}".format(layer_data)) - - photoshop_client.imprint(layer, layer_data, layers) - new_layer_data = photoshop_client.read(layer) - assert layer_data == new_layer_data + layer_data = photoshop_client.read(layer, layers_meta) # Skip layers without metadata. if layer_data is None: From 7868d8e0165cdf8e98ae61df1fb636b16ce5129a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Thu, 20 Aug 2020 13:22:17 +0200 Subject: [PATCH 105/813] allow thumbnails from single frame renders --- pype/plugins/global/publish/extract_jpeg.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pype/plugins/global/publish/extract_jpeg.py b/pype/plugins/global/publish/extract_jpeg.py index ae74370b06..2ec97759db 100644 --- a/pype/plugins/global/publish/extract_jpeg.py +++ b/pype/plugins/global/publish/extract_jpeg.py @@ -48,7 +48,9 @@ class ExtractJpegEXR(pyblish.api.InstancePlugin): continue if not isinstance(repre['files'], (list, tuple)): - continue + input_file = repre['files'] + else: + input_file = repre['files'][0] stagingdir = os.path.normpath(repre.get("stagingDir")) input_file = repre['files'][0] From 5df17ed2939be3d780477527cd758630adeb0319 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Thu, 20 Aug 2020 13:34:37 +0200 Subject: [PATCH 106/813] delete forgotten line --- pype/plugins/global/publish/extract_jpeg.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pype/plugins/global/publish/extract_jpeg.py b/pype/plugins/global/publish/extract_jpeg.py index 2ec97759db..333c2ec852 100644 --- a/pype/plugins/global/publish/extract_jpeg.py +++ b/pype/plugins/global/publish/extract_jpeg.py @@ -53,7 +53,6 @@ class ExtractJpegEXR(pyblish.api.InstancePlugin): input_file = repre['files'][0] stagingdir = os.path.normpath(repre.get("stagingDir")) - input_file = repre['files'][0] # input_file = ( # collections[0].format('{head}{padding}{tail}') % start From 3a69b9956d6d9ca82a99edb77c8ad52fa6b11084 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 20 Aug 2020 15:15:10 +0200 Subject: [PATCH 107/813] moved resources to widgets folder --- .../{ => widgets}/resources/__init__.py | 0 .../{ => widgets}/resources/edit.svg | 0 .../{ => widgets}/resources/file.png | Bin .../{ => widgets}/resources/files.png | Bin .../{ => widgets}/resources/houdini.png | Bin .../{ => widgets}/resources/image_file.png | Bin .../{ => widgets}/resources/image_files.png | Bin .../{ => widgets}/resources/information.svg | 0 .../{ => widgets}/resources/maya.png | Bin .../{ => widgets}/resources/menu.png | Bin .../{ => widgets}/resources/menu_disabled.png | Bin .../{ => widgets}/resources/menu_hover.png | Bin .../{ => widgets}/resources/menu_pressed.png | Bin .../{ => widgets}/resources/menu_pressed_hover.png | Bin .../{ => widgets}/resources/nuke.png | Bin .../{ => widgets}/resources/premiere.png | Bin .../{ => widgets}/resources/trash.png | Bin .../{ => widgets}/resources/trash_disabled.png | Bin .../{ => widgets}/resources/trash_hover.png | Bin .../{ => widgets}/resources/trash_pressed.png | Bin .../{ => widgets}/resources/trash_pressed_hover.png | Bin .../{ => widgets}/resources/video_file.png | Bin 22 files changed, 0 insertions(+), 0 deletions(-) rename pype/tools/standalonepublish/{ => widgets}/resources/__init__.py (100%) rename pype/tools/standalonepublish/{ => widgets}/resources/edit.svg (100%) rename pype/tools/standalonepublish/{ => widgets}/resources/file.png (100%) rename pype/tools/standalonepublish/{ => widgets}/resources/files.png (100%) rename pype/tools/standalonepublish/{ => widgets}/resources/houdini.png (100%) rename pype/tools/standalonepublish/{ => widgets}/resources/image_file.png (100%) rename pype/tools/standalonepublish/{ => widgets}/resources/image_files.png (100%) rename pype/tools/standalonepublish/{ => widgets}/resources/information.svg (100%) rename pype/tools/standalonepublish/{ => widgets}/resources/maya.png (100%) rename pype/tools/standalonepublish/{ => widgets}/resources/menu.png (100%) rename pype/tools/standalonepublish/{ => widgets}/resources/menu_disabled.png (100%) rename pype/tools/standalonepublish/{ => widgets}/resources/menu_hover.png (100%) rename pype/tools/standalonepublish/{ => widgets}/resources/menu_pressed.png (100%) rename pype/tools/standalonepublish/{ => widgets}/resources/menu_pressed_hover.png (100%) rename pype/tools/standalonepublish/{ => widgets}/resources/nuke.png (100%) rename pype/tools/standalonepublish/{ => widgets}/resources/premiere.png (100%) rename pype/tools/standalonepublish/{ => widgets}/resources/trash.png (100%) rename pype/tools/standalonepublish/{ => widgets}/resources/trash_disabled.png (100%) rename pype/tools/standalonepublish/{ => widgets}/resources/trash_hover.png (100%) rename pype/tools/standalonepublish/{ => widgets}/resources/trash_pressed.png (100%) rename pype/tools/standalonepublish/{ => widgets}/resources/trash_pressed_hover.png (100%) rename pype/tools/standalonepublish/{ => widgets}/resources/video_file.png (100%) diff --git a/pype/tools/standalonepublish/resources/__init__.py b/pype/tools/standalonepublish/widgets/resources/__init__.py similarity index 100% rename from pype/tools/standalonepublish/resources/__init__.py rename to pype/tools/standalonepublish/widgets/resources/__init__.py diff --git a/pype/tools/standalonepublish/resources/edit.svg b/pype/tools/standalonepublish/widgets/resources/edit.svg similarity index 100% rename from pype/tools/standalonepublish/resources/edit.svg rename to pype/tools/standalonepublish/widgets/resources/edit.svg diff --git a/pype/tools/standalonepublish/resources/file.png b/pype/tools/standalonepublish/widgets/resources/file.png similarity index 100% rename from pype/tools/standalonepublish/resources/file.png rename to pype/tools/standalonepublish/widgets/resources/file.png diff --git a/pype/tools/standalonepublish/resources/files.png b/pype/tools/standalonepublish/widgets/resources/files.png similarity index 100% rename from pype/tools/standalonepublish/resources/files.png rename to pype/tools/standalonepublish/widgets/resources/files.png diff --git a/pype/tools/standalonepublish/resources/houdini.png b/pype/tools/standalonepublish/widgets/resources/houdini.png similarity index 100% rename from pype/tools/standalonepublish/resources/houdini.png rename to pype/tools/standalonepublish/widgets/resources/houdini.png diff --git a/pype/tools/standalonepublish/resources/image_file.png b/pype/tools/standalonepublish/widgets/resources/image_file.png similarity index 100% rename from pype/tools/standalonepublish/resources/image_file.png rename to pype/tools/standalonepublish/widgets/resources/image_file.png diff --git a/pype/tools/standalonepublish/resources/image_files.png b/pype/tools/standalonepublish/widgets/resources/image_files.png similarity index 100% rename from pype/tools/standalonepublish/resources/image_files.png rename to pype/tools/standalonepublish/widgets/resources/image_files.png diff --git a/pype/tools/standalonepublish/resources/information.svg b/pype/tools/standalonepublish/widgets/resources/information.svg similarity index 100% rename from pype/tools/standalonepublish/resources/information.svg rename to pype/tools/standalonepublish/widgets/resources/information.svg diff --git a/pype/tools/standalonepublish/resources/maya.png b/pype/tools/standalonepublish/widgets/resources/maya.png similarity index 100% rename from pype/tools/standalonepublish/resources/maya.png rename to pype/tools/standalonepublish/widgets/resources/maya.png diff --git a/pype/tools/standalonepublish/resources/menu.png b/pype/tools/standalonepublish/widgets/resources/menu.png similarity index 100% rename from pype/tools/standalonepublish/resources/menu.png rename to pype/tools/standalonepublish/widgets/resources/menu.png diff --git a/pype/tools/standalonepublish/resources/menu_disabled.png b/pype/tools/standalonepublish/widgets/resources/menu_disabled.png similarity index 100% rename from pype/tools/standalonepublish/resources/menu_disabled.png rename to pype/tools/standalonepublish/widgets/resources/menu_disabled.png diff --git a/pype/tools/standalonepublish/resources/menu_hover.png b/pype/tools/standalonepublish/widgets/resources/menu_hover.png similarity index 100% rename from pype/tools/standalonepublish/resources/menu_hover.png rename to pype/tools/standalonepublish/widgets/resources/menu_hover.png diff --git a/pype/tools/standalonepublish/resources/menu_pressed.png b/pype/tools/standalonepublish/widgets/resources/menu_pressed.png similarity index 100% rename from pype/tools/standalonepublish/resources/menu_pressed.png rename to pype/tools/standalonepublish/widgets/resources/menu_pressed.png diff --git a/pype/tools/standalonepublish/resources/menu_pressed_hover.png b/pype/tools/standalonepublish/widgets/resources/menu_pressed_hover.png similarity index 100% rename from pype/tools/standalonepublish/resources/menu_pressed_hover.png rename to pype/tools/standalonepublish/widgets/resources/menu_pressed_hover.png diff --git a/pype/tools/standalonepublish/resources/nuke.png b/pype/tools/standalonepublish/widgets/resources/nuke.png similarity index 100% rename from pype/tools/standalonepublish/resources/nuke.png rename to pype/tools/standalonepublish/widgets/resources/nuke.png diff --git a/pype/tools/standalonepublish/resources/premiere.png b/pype/tools/standalonepublish/widgets/resources/premiere.png similarity index 100% rename from pype/tools/standalonepublish/resources/premiere.png rename to pype/tools/standalonepublish/widgets/resources/premiere.png diff --git a/pype/tools/standalonepublish/resources/trash.png b/pype/tools/standalonepublish/widgets/resources/trash.png similarity index 100% rename from pype/tools/standalonepublish/resources/trash.png rename to pype/tools/standalonepublish/widgets/resources/trash.png diff --git a/pype/tools/standalonepublish/resources/trash_disabled.png b/pype/tools/standalonepublish/widgets/resources/trash_disabled.png similarity index 100% rename from pype/tools/standalonepublish/resources/trash_disabled.png rename to pype/tools/standalonepublish/widgets/resources/trash_disabled.png diff --git a/pype/tools/standalonepublish/resources/trash_hover.png b/pype/tools/standalonepublish/widgets/resources/trash_hover.png similarity index 100% rename from pype/tools/standalonepublish/resources/trash_hover.png rename to pype/tools/standalonepublish/widgets/resources/trash_hover.png diff --git a/pype/tools/standalonepublish/resources/trash_pressed.png b/pype/tools/standalonepublish/widgets/resources/trash_pressed.png similarity index 100% rename from pype/tools/standalonepublish/resources/trash_pressed.png rename to pype/tools/standalonepublish/widgets/resources/trash_pressed.png diff --git a/pype/tools/standalonepublish/resources/trash_pressed_hover.png b/pype/tools/standalonepublish/widgets/resources/trash_pressed_hover.png similarity index 100% rename from pype/tools/standalonepublish/resources/trash_pressed_hover.png rename to pype/tools/standalonepublish/widgets/resources/trash_pressed_hover.png diff --git a/pype/tools/standalonepublish/resources/video_file.png b/pype/tools/standalonepublish/widgets/resources/video_file.png similarity index 100% rename from pype/tools/standalonepublish/resources/video_file.png rename to pype/tools/standalonepublish/widgets/resources/video_file.png From 1e101c1fbc408e7fe77809dca3385b14e16fdea6 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 20 Aug 2020 15:15:28 +0200 Subject: [PATCH 108/813] PngFactory do not create Qt object on init --- .../widgets/widget_component_item.py | 52 +++++++++++-------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/pype/tools/standalonepublish/widgets/widget_component_item.py b/pype/tools/standalonepublish/widgets/widget_component_item.py index dd838075e3..3850d68b96 100644 --- a/pype/tools/standalonepublish/widgets/widget_component_item.py +++ b/pype/tools/standalonepublish/widgets/widget_component_item.py @@ -1,6 +1,6 @@ import os from Qt import QtCore, QtGui, QtWidgets -from pype.resources import get_resource +from .resources import get_resource from avalon import style @@ -353,27 +353,37 @@ class LightingButton(QtWidgets.QPushButton): class PngFactory: - png_names = { - "trash": { - "normal": QtGui.QIcon(get_resource("trash.png")), - "hover": QtGui.QIcon(get_resource("trash_hover.png")), - "pressed": QtGui.QIcon(get_resource("trash_pressed.png")), - "pressed_hover": QtGui.QIcon( - get_resource("trash_pressed_hover.png") - ), - "disabled": QtGui.QIcon(get_resource("trash_disabled.png")) - }, + png_names = None - "menu": { - "normal": QtGui.QIcon(get_resource("menu.png")), - "hover": QtGui.QIcon(get_resource("menu_hover.png")), - "pressed": QtGui.QIcon(get_resource("menu_pressed.png")), - "pressed_hover": QtGui.QIcon( - get_resource("menu_pressed_hover.png") - ), - "disabled": QtGui.QIcon(get_resource("menu_disabled.png")) + @classmethod + def init(cls): + cls.png_names = { + "trash": { + "normal": QtGui.QIcon(get_resource("trash.png")), + "hover": QtGui.QIcon(get_resource("trash_hover.png")), + "pressed": QtGui.QIcon(get_resource("trash_pressed.png")), + "pressed_hover": QtGui.QIcon( + get_resource("trash_pressed_hover.png") + ), + "disabled": QtGui.QIcon(get_resource("trash_disabled.png")) + }, + + "menu": { + "normal": QtGui.QIcon(get_resource("menu.png")), + "hover": QtGui.QIcon(get_resource("menu_hover.png")), + "pressed": QtGui.QIcon(get_resource("menu_pressed.png")), + "pressed_hover": QtGui.QIcon( + get_resource("menu_pressed_hover.png") + ), + "disabled": QtGui.QIcon(get_resource("menu_disabled.png")) + } } - } + + @classmethod + def get_png(cls, name): + if cls.png_names is None: + cls.init() + return cls.png_names.get(name) class PngButton(QtWidgets.QPushButton): @@ -406,7 +416,7 @@ class PngButton(QtWidgets.QPushButton): png_dict = {} if name: - png_dict = PngFactory.png_names.get(name) or {} + png_dict = PngFactory.get_png(name) or {} if not png_dict: print(( "WARNING: There is not set icon with name \"{}\"" From e551039c124cfe82252479dcf6b506aac17cd31d Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 20 Aug 2020 15:42:43 +0200 Subject: [PATCH 109/813] Speedup of collect_instances.py --- .../websocket_server/clients/photoshop_client.py | 13 ++++++++----- pype/plugins/photoshop/publish/extract_review.py | 7 ++++--- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/pype/modules/websocket_server/clients/photoshop_client.py b/pype/modules/websocket_server/clients/photoshop_client.py index bf72c1bc5a..00e5355786 100644 --- a/pype/modules/websocket_server/clients/photoshop_client.py +++ b/pype/modules/websocket_server/clients/photoshop_client.py @@ -91,14 +91,17 @@ class PhotoshopClientStub(): print("get_layers_in_layers len {}".format(len(layers))) print("get_layers_in_layers type {}".format(type(layers))) ret = [] - layer_ids = [lay.id for lay in layers] - layer_group_ids = [ll.groupId for ll in layers if ll.group] + parent_ids = set([lay.id for lay in layers]) + print("parent_ids ".format(parent_ids)) for layer in all_layers: - if layer.groupId in layer_group_ids: # all from group + print("layer {}".format(layer)) + parents = set(layer.parents) + print("parents {}".format(layer)) + if len(parent_ids & parents) > 0: ret.append(layer) - if layer.id in layer_ids: + if layer.id in parent_ids: ret.append(layer) - + print("ret {}".format(ret)) return ret def group_selected_layers(self): diff --git a/pype/plugins/photoshop/publish/extract_review.py b/pype/plugins/photoshop/publish/extract_review.py index 1e8d726720..806b59341b 100644 --- a/pype/plugins/photoshop/publish/extract_review.py +++ b/pype/plugins/photoshop/publish/extract_review.py @@ -4,8 +4,9 @@ import pype.api import pype.lib from avalon import photoshop -from pype.modules.websocket_server.clients.photoshop_client import \ - PhotoshopClientStub +from pype.modules.websocket_server.clients.photoshop_client import ( + PhotoshopClientStub +) class ExtractReview(pype.api.Extractor): @@ -36,7 +37,7 @@ class ExtractReview(pype.api.Extractor): # Hide all other layers. extract_ids = set([ll.id for ll in photoshop_client. get_layers_in_layers(layers)]) - + self.log.info("extract_ids {}".format(extract_ids)) for layer in photoshop_client.get_layers(): # limit unnecessary calls to client if layer.visible and layer.id not in extract_ids: From 4c6e5fefb415eab87ee52fd3b0bceb416f0643bf Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 20 Aug 2020 16:05:03 +0200 Subject: [PATCH 110/813] implemented action for pushing frameStart and frameEnd values to task specific custom attributes --- .../action_push_frame_values_to_task.py | 245 ++++++++++++++++++ 1 file changed, 245 insertions(+) create mode 100644 pype/modules/ftrack/actions/action_push_frame_values_to_task.py diff --git a/pype/modules/ftrack/actions/action_push_frame_values_to_task.py b/pype/modules/ftrack/actions/action_push_frame_values_to_task.py new file mode 100644 index 0000000000..3037695452 --- /dev/null +++ b/pype/modules/ftrack/actions/action_push_frame_values_to_task.py @@ -0,0 +1,245 @@ +import json +import collections +import ftrack_api +from pype.modules.ftrack.lib import BaseAction, statics_icon + + +class PushFrameValuesToTaskAction(BaseAction): + """Action for testing purpose or as base for new actions.""" + + identifier = "admin.push_frame_values_to_task" + label = "Pype Admin" + variant = "- Push Frame values to Task" + role_list = ["Pypeclub", "Administrator", "Project Manager"] + icon = statics_icon("ftrack", "action_icons", "PypeAdmin.svg") + + entities_query = ( + "select id, name, parent_id, link" + " from TypedContext where project_id is \"{}\"" + ) + cust_attrs_query = ( + "select id, key, object_type_id, is_hierarchical, default" + " from CustomAttributeConfiguration" + " where key in ({})" + ) + cust_attr_value_query = ( + "select value, entity_id from CustomAttributeValue" + " where entity_id in ({}) and configuration_id in ({})" + ) + custom_attribute_keys = ["frameStart", "frameEnd"] + + def discover(self, session, entities, event): + return True + + def launch(self, session, entities, event): + task_attrs_by_key, hier_attrs = self.frame_attributes(session) + missing_keys = [ + key + for key in self.custom_attribute_keys + if key not in task_attrs_by_key + ] + if missing_keys: + if len(missing_keys) == 1: + sub_msg = " \"{}\"".format(missing_keys[0]) + else: + sub_msg = "s {}".format(", ".join([ + "\"{}\"".format(key) + for key in missing_keys + ])) + + msg = "Missing Task's custom attribute{}.".format(sub_msg) + self.log.warning(msg) + return { + "success": False, + "message": msg + } + + self.log.debug("{}: Creating job".format(self.label)) + + user_entity = session.query( + "User where id is {}".format(event["source"]["user"]["id"]) + ).one() + job = session.create("Job", { + "user": user_entity, + "status": "running", + "data": json.dumps({ + "description": "Propagation of Frame attribute values to task." + }) + }) + session.commit() + + try: + project_entity = self.get_project_from_entity(entities[0]) + result = self.propagate_values( + session, + tuple(task_attrs_by_key.values()), + hier_attrs, + project_entity + ) + job["status"] = "done" + session.commit() + + return result + + except Exception: + session.rollback() + job["status"] = "failed" + session.commit() + + msg = "Pushing Custom attribute values to task Failed" + self.log.warning(msg, exc_info=True) + return { + "success": False, + "message": msg + } + + finally: + if job["status"] == "running": + job["status"] = "failed" + session.commit() + + def frame_attributes(self, session): + task_object_type = session.query( + "ObjectType where name is \"Task\"" + ).one() + + attr_names = self.custom_attribute_keys + if isinstance(attr_names, str): + attr_names = [attr_names] + + joined_keys = ",".join([ + "\"{}\"".format(attr_name) + for attr_name in attr_names + ]) + + attribute_entities = session.query( + self.cust_attrs_query.format(joined_keys) + ).all() + + hier_attrs = [] + task_attrs = {} + for attr in attribute_entities: + attr_key = attr["key"] + if attr["is_hierarchical"]: + hier_attrs.append(attr) + elif attr["object_type_id"] == task_object_type["id"]: + task_attrs[attr_key] = attr + return task_attrs, hier_attrs + + def join_keys(self, items): + return ",".join(["\"{}\"".format(item) for item in items]) + + def propagate_values( + self, session, task_attrs, hier_attrs, project_entity + ): + self.log.debug("Querying project's entities \"{}\".".format( + project_entity["full_name"] + )) + entities = session.query( + self.entities_query.format(project_entity["id"]) + ).all() + + self.log.debug("Filtering Task entities.") + task_entities_by_parent_id = collections.defaultdict(list) + for entity in entities: + if entity.entity_type.lower() == "task": + task_entities_by_parent_id[entity["parent_id"]].append(entity) + + self.log.debug("Getting Custom attribute values from tasks' parents.") + hier_values_by_entity_id = self.get_hier_values( + session, + hier_attrs, + list(task_entities_by_parent_id.keys()) + ) + + self.log.debug("Setting parents' values to task.") + self.set_task_attr_values( + session, + task_entities_by_parent_id, + hier_values_by_entity_id, + task_attrs + ) + + return True + + def get_hier_values(self, session, hier_attrs, focus_entity_ids): + joined_entity_ids = self.join_keys(focus_entity_ids) + hier_attr_ids = self.join_keys( + tuple(hier_attr["id"] for hier_attr in hier_attrs) + ) + hier_attrs_key_by_id = { + hier_attr["id"]: hier_attr["key"] + for hier_attr in hier_attrs + } + call_expr = [{ + "action": "query", + "expression": self.cust_attr_value_query.format( + joined_entity_ids, hier_attr_ids + ) + }] + if hasattr(session, "call"): + [values] = session.call(call_expr) + else: + [values] = session._call(call_expr) + + values_per_entity_id = {} + for item in values["data"]: + entity_id = item["entity_id"] + key = hier_attrs_key_by_id[item["configuration_id"]] + + if entity_id not in values_per_entity_id: + values_per_entity_id[entity_id] = {} + value = item["value"] + if value is not None: + values_per_entity_id[entity_id][key] = value + + output = {} + for entity_id in focus_entity_ids: + value = values_per_entity_id.get(entity_id) + if value: + output[entity_id] = value + + return output + + def set_task_attr_values( + self, + session, + task_entities_by_parent_id, + hier_values_by_entity_id, + task_attrs + ): + task_attr_ids_by_key = { + attr["key"]: attr["id"] + for attr in task_attrs + } + + total_parents = len(hier_values_by_entity_id) + idx = 1 + for parent_id, values in hier_values_by_entity_id.items(): + self.log.info(( + "[{}/{}] {} Processing values to children. Values: {}" + ).format(idx, total_parents, parent_id, values)) + + task_entities = task_entities_by_parent_id[parent_id] + for key, value in values.items(): + for task_entity in task_entities: + _entity_key = collections.OrderedDict({ + "configuration_id": task_attr_ids_by_key[key], + "entity_id": task_entity["id"] + }) + + session.recorded_operations.push( + ftrack_api.operation.UpdateEntityOperation( + "ContextCustomAttributeValue", + _entity_key, + "value", + ftrack_api.symbol.NOT_SET, + value + ) + ) + session.commit() + idx += 1 + + +def register(session, plugins_presets={}): + PushFrameValuesToTask(session, plugins_presets).register() From 1431930e6af89df656cced3572598907d13525ad Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 20 Aug 2020 16:05:13 +0200 Subject: [PATCH 111/813] implemented event handler for pushing frameStart and frameEnd values to task specific custom attributes --- .../events/event_push_frame_values_to_task.py | 148 ++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 pype/modules/ftrack/events/event_push_frame_values_to_task.py diff --git a/pype/modules/ftrack/events/event_push_frame_values_to_task.py b/pype/modules/ftrack/events/event_push_frame_values_to_task.py new file mode 100644 index 0000000000..dd5c5911ec --- /dev/null +++ b/pype/modules/ftrack/events/event_push_frame_values_to_task.py @@ -0,0 +1,148 @@ +import collections +import ftrack_api +from pype.modules.ftrack import BaseEvent + + +class PushFrameValuesToTaskEvent(BaseEvent): + """Action for testing purpose or as base for new actions.""" + cust_attrs_query = ( + "select id, key, object_type_id, is_hierarchical, default" + " from CustomAttributeConfiguration" + " where key in ({}) and object_type_id = {}" + ) + + # Ignore event handler by default + ignore_me = True + + interest_attributes = ["frameStart", "frameEnd"] + _cached_task_object_id = None + + @classmethod + def task_object_id(cls, session): + if cls._cached_task_object_id is None: + task_object_type = session.query( + "ObjectType where name is \"Task\"" + ).one() + cls._cached_task_object_id = task_object_type["id"] + return cls._cached_task_object_id + + def extract_interesting_data(self, session, event): + # Filter if event contain relevant data + entities_info = event["data"].get("entities") + if not entities_info: + return + + interesting_data = {} + for entity_info in entities_info: + # Care only about tasks + if entity_info.get("entityType") != "task": + continue + + # Care only about changes of status + changes = entity_info.get("changes") or {} + if not changes: + continue + + # Care only about changes if specific keys + entity_changes = {} + for key in self.interest_attributes: + if key in changes: + entity_changes[key] = changes[key]["new"] + + if not entity_changes: + continue + + # Do not care about "Task" entity_type + task_object_id = self.task_object_id(session) + if entity_info.get("objectTypeId") == task_object_id: + continue + + interesting_data[entity_info["entityId"]] = entity_changes + return interesting_data + + def join_keys(self, keys): + return ",".join(["\"{}\"".format(key) for key in keys]) + + def get_task_entities(self, session, entities_info): + return session.query( + "Task where parent_id in ({})".format( + self.join_keys(entities_info.keys()) + ) + ).all() + + def task_attrs(self, session): + return session.query(self.cust_attrs_query.format( + self.join_keys(self.interest_attributes), + self.task_object_id(session) + )).all() + + def launch(self, session, event): + interesting_data = self.extract_interesting_data(session, event) + if not interesting_data: + return + + task_entities = self.get_task_entities(session, interesting_data) + if not task_entities: + return + + task_attrs = self.task_attrs(session) + if not task_attrs: + self.log.warning(( + "There is not created Custom Attributes {}" + " for \"Task\" entity type." + ).format(self.join_keys(self.interest_attributes))) + return + + task_attr_id_by_key = { + attr["key"]: attr["id"] + for attr in task_attrs + } + task_entities_by_parent_id = collections.defaultdict(list) + for task_entity in task_entities: + task_entities_by_parent_id[task_entity["parent_id"]].append( + task_entity + ) + + for parent_id, values in interesting_data.items(): + task_entities = task_entities_by_parent_id[parent_id] + for key, value in values.items(): + changed_ids = [] + for task_entity in task_entities: + task_id = task_entity["id"] + changed_ids.append(task_id) + + entity_key = collections.OrderedDict({ + "configuration_id": task_attr_id_by_key[key], + "entity_id": task_id + }) + if value is None: + op = ftrack_api.operation.DeleteEntityOperation( + "CustomAttributeValue", + entity_key + ) + else: + op = ftrack_api.operation.UpdateEntityOperation( + "ContextCustomAttributeValue", + entity_key, + "value", + ftrack_api.symbol.NOT_SET, + value + ) + + session.recorded_operations.push(op) + self.log.info(( + "Changing Custom Attribute \"{}\" to value" + " \"{}\" on entities: {}" + ).format(key, value, self.join_keys(changed_ids))) + try: + session.commit() + except Exception: + session.rollback() + self.log.warning( + "Changing of values failed.", + exc_info=True + ) + + +def register(session, plugins_presets): + PushFrameValuesToTaskEvent(session, plugins_presets).register() From 516fafbfec822e8b5f2957bdd108f157412a5963 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 20 Aug 2020 16:39:16 +0200 Subject: [PATCH 112/813] moved action to server --- .../{actions => events}/action_push_frame_values_to_task.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename pype/modules/ftrack/{actions => events}/action_push_frame_values_to_task.py (100%) diff --git a/pype/modules/ftrack/actions/action_push_frame_values_to_task.py b/pype/modules/ftrack/events/action_push_frame_values_to_task.py similarity index 100% rename from pype/modules/ftrack/actions/action_push_frame_values_to_task.py rename to pype/modules/ftrack/events/action_push_frame_values_to_task.py From 8ae527a154c24d0b0e5634546d759f421f0c0426 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 20 Aug 2020 16:39:47 +0200 Subject: [PATCH 113/813] action converted to server action --- .../action_push_frame_values_to_task.py | 64 ++++++++++++++----- 1 file changed, 49 insertions(+), 15 deletions(-) diff --git a/pype/modules/ftrack/events/action_push_frame_values_to_task.py b/pype/modules/ftrack/events/action_push_frame_values_to_task.py index 3037695452..bd036411ac 100644 --- a/pype/modules/ftrack/events/action_push_frame_values_to_task.py +++ b/pype/modules/ftrack/events/action_push_frame_values_to_task.py @@ -1,7 +1,7 @@ import json import collections import ftrack_api -from pype.modules.ftrack.lib import BaseAction, statics_icon +from pype.modules.ftrack.lib import BaseAction class PushFrameValuesToTaskAction(BaseAction): @@ -10,8 +10,6 @@ class PushFrameValuesToTaskAction(BaseAction): identifier = "admin.push_frame_values_to_task" label = "Pype Admin" variant = "- Push Frame values to Task" - role_list = ["Pypeclub", "Administrator", "Project Manager"] - icon = statics_icon("ftrack", "action_icons", "PypeAdmin.svg") entities_query = ( "select id, name, parent_id, link" @@ -26,12 +24,56 @@ class PushFrameValuesToTaskAction(BaseAction): "select value, entity_id from CustomAttributeValue" " where entity_id in ({}) and configuration_id in ({})" ) - custom_attribute_keys = ["frameStart", "frameEnd"] + custom_attribute_keys = {"frameStart", "frameEnd"} + discover_role_list = {"Pypeclub", "Administrator", "Project Manager"} + + def register(self): + modified_role_names = set() + for role_name in self.discover_role_list: + modified_role_names.add(role_name.lower()) + self.discover_role_list = modified_role_names + + self.session.event_hub.subscribe( + "topic=ftrack.action.discover", + self._discover, + priority=self.priority + ) + + launch_subscription = ( + "topic=ftrack.action.launch and data.actionIdentifier={0}" + ).format(self.identifier) + self.session.event_hub.subscribe(launch_subscription, self._launch) def discover(self, session, entities, event): - return True + """ Validation """ + # Check if selection is valid + valid_selection = False + for ent in event["data"]["selection"]: + # Ignore entities that are not tasks or projects + if ent["entityType"].lower() in ["show", "task"]: + valid_selection = True + break + + if not valid_selection: + return False + + # Get user and check his roles + user_id = event.get("source", {}).get("user", {}).get("id") + if not user_id: + return False + + user = session.query("User where id is \"{}\"".format(user_id)).first() + if not user: + return False + + for role in user["user_security_roles"]: + lowered_role = role["security_role"]["name"].lower() + if lowered_role in self.discover_role_list: + return True + return False def launch(self, session, entities, event): + # TODO this can be threaded task_attrs_by_key, hier_attrs = self.frame_attributes(session) missing_keys = [ key @@ -103,15 +145,7 @@ class PushFrameValuesToTaskAction(BaseAction): "ObjectType where name is \"Task\"" ).one() - attr_names = self.custom_attribute_keys - if isinstance(attr_names, str): - attr_names = [attr_names] - - joined_keys = ",".join([ - "\"{}\"".format(attr_name) - for attr_name in attr_names - ]) - + joined_keys = self.join_keys(self.custom_attribute_keys) attribute_entities = session.query( self.cust_attrs_query.format(joined_keys) ).all() @@ -242,4 +276,4 @@ class PushFrameValuesToTaskAction(BaseAction): def register(session, plugins_presets={}): - PushFrameValuesToTask(session, plugins_presets).register() + PushFrameValuesToTaskAction(session, plugins_presets).register() From 63c2cf6a41ca8d66cfffca914d6933c28d32b920 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Thu, 20 Aug 2020 16:39:57 +0200 Subject: [PATCH 114/813] support for different tile order in vray --- pype/plugins/maya/publish/submit_maya_deadline.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/pype/plugins/maya/publish/submit_maya_deadline.py b/pype/plugins/maya/publish/submit_maya_deadline.py index d9ee7f9646..747d2727b7 100644 --- a/pype/plugins/maya/publish/submit_maya_deadline.py +++ b/pype/plugins/maya/publish/submit_maya_deadline.py @@ -65,7 +65,9 @@ payload_skeleton = { } -def _format_tiles(filename, index, tiles_x, tiles_y, width, height, prefix): +def _format_tiles( + filename, index, tiles_x, tiles_y, + width, height, prefix, origin="blc"): """Generate tile entries for Deadline tile job. Returns two dictionaries - one that can be directly used in Deadline @@ -142,7 +144,11 @@ def _format_tiles(filename, index, tiles_x, tiles_y, width, height, prefix): cfg["Tile{}".format(tile)] = new_filename cfg["Tile{}Tile".format(tile)] = new_filename cfg["Tile{}X".format(tile)] = (tile_x - 1) * w_space - cfg["Tile{}Y".format(tile)] = (tile_y - 1) * h_space + if origin == "blc": + cfg["Tile{}Y".format(tile)] = (tile_y - 1) * h_space + else: + cfg["Tile{}Y".format(tile)] = int(height) - ((tile_y - 1) * h_space) # noqa: E501 + cfg["Tile{}Width".format(tile)] = tile_x * w_space cfg["Tile{}Height".format(tile)] = tile_y * h_space @@ -549,6 +555,7 @@ class MayaSubmitDeadline(pyblish.api.InstancePlugin): assembly_payload["JobInfo"].update(output_filenames) assembly_payload["JobInfo"]["Priority"] = self._instance.data.get( "priority", 50) + assembly_payload["JobInfo"]["UserName"] = deadline_user frame_payloads = [] assembly_payloads = [] @@ -620,6 +627,7 @@ class MayaSubmitDeadline(pyblish.api.InstancePlugin): REPL_FRAME_NUMBER, "\\1{}\\3".format("#" * len(frame)), file) + new_assembly_payload["PluginInfo"]["Renderer"] = self._instance.data["renderer"] # noqa: E501 new_assembly_payload["JobInfo"]["ExtraInfo0"] = frame_jobs[frame] # noqa: E501 new_assembly_payload["JobInfo"]["ExtraInfo1"] = file assembly_payloads.append(new_assembly_payload) From a37da37bd15c6e5aca9c37ea56a57eb163359f11 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 20 Aug 2020 16:42:56 +0200 Subject: [PATCH 115/813] commit all changes at once --- .../modules/ftrack/events/action_push_frame_values_to_task.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/modules/ftrack/events/action_push_frame_values_to_task.py b/pype/modules/ftrack/events/action_push_frame_values_to_task.py index bd036411ac..4f0c7ffeb7 100644 --- a/pype/modules/ftrack/events/action_push_frame_values_to_task.py +++ b/pype/modules/ftrack/events/action_push_frame_values_to_task.py @@ -253,6 +253,7 @@ class PushFrameValuesToTaskAction(BaseAction): self.log.info(( "[{}/{}] {} Processing values to children. Values: {}" ).format(idx, total_parents, parent_id, values)) + idx += 1 task_entities = task_entities_by_parent_id[parent_id] for key, value in values.items(): @@ -271,8 +272,7 @@ class PushFrameValuesToTaskAction(BaseAction): value ) ) - session.commit() - idx += 1 + session.commit() def register(session, plugins_presets={}): From 97b42d8703150038feb6833832c9da0d39df40ce Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 20 Aug 2020 16:46:35 +0200 Subject: [PATCH 116/813] ignore action by default --- pype/modules/ftrack/events/action_push_frame_values_to_task.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pype/modules/ftrack/events/action_push_frame_values_to_task.py b/pype/modules/ftrack/events/action_push_frame_values_to_task.py index 4f0c7ffeb7..5b7da8bebb 100644 --- a/pype/modules/ftrack/events/action_push_frame_values_to_task.py +++ b/pype/modules/ftrack/events/action_push_frame_values_to_task.py @@ -7,6 +7,9 @@ from pype.modules.ftrack.lib import BaseAction class PushFrameValuesToTaskAction(BaseAction): """Action for testing purpose or as base for new actions.""" + # Ignore event handler by default + ignore_me = True + identifier = "admin.push_frame_values_to_task" label = "Pype Admin" variant = "- Push Frame values to Task" From 41e2149d1295d5f89363648bd64da9cd11ec9f5c Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 20 Aug 2020 17:15:36 +0200 Subject: [PATCH 117/813] Fix select correct layers for multiple images --- .../clients/photoshop_client.py | 29 ++++++++----------- .../photoshop/publish/collect_instances.py | 2 +- .../photoshop/publish/extract_image.py | 13 +++------ 3 files changed, 17 insertions(+), 27 deletions(-) diff --git a/pype/modules/websocket_server/clients/photoshop_client.py b/pype/modules/websocket_server/clients/photoshop_client.py index 00e5355786..330c2ceff0 100644 --- a/pype/modules/websocket_server/clients/photoshop_client.py +++ b/pype/modules/websocket_server/clients/photoshop_client.py @@ -26,7 +26,7 @@ class PhotoshopClientStub(): :return: """ if layers_meta is None: - layers_meta = self._get_layers_metadata() + layers_meta = self.get_layers_metadata() return layers_meta.get(str(layer.id)) @@ -38,10 +38,13 @@ class PhotoshopClientStub(): :param all_layers: - for performance, could be injected for usage in loop, if not, single call will be triggered + :param layers_meta: json representation from Headline + (for performance - provide only if imprint is in + loop - value should be same) :return: None """ if not layers_meta: - layers_meta = self._get_layers_metadata() + layers_meta = self.get_layers_metadata() # json.dumps writes integer values in a dictionary to string, so # anticipating it here. if str(layer.id) in layers_meta and layers_meta[str(layer.id)]: @@ -83,25 +86,20 @@ class PhotoshopClientStub(): def get_layers_in_layers(self, layers): """ Return all layers that belong to layers (might be groups). - :param layers: - :return: + :param layers: + :return: """ all_layers = self.get_layers() - print("get_layers_in_layers {}".format(layers)) - print("get_layers_in_layers len {}".format(len(layers))) - print("get_layers_in_layers type {}".format(type(layers))) ret = [] parent_ids = set([lay.id for lay in layers]) - print("parent_ids ".format(parent_ids)) + for layer in all_layers: - print("layer {}".format(layer)) parents = set(layer.parents) - print("parents {}".format(layer)) if len(parent_ids & parents) > 0: ret.append(layer) if layer.id in parent_ids: ret.append(layer) - print("ret {}".format(ret)) + return ret def group_selected_layers(self): @@ -140,7 +138,8 @@ class PhotoshopClientStub(): :return: full path with name """ res = self.websocketserver.call( - self.client.call('Photoshop.get_active_document_full_name')) + self.client.call + ('Photoshop.get_active_document_full_name')) return res @@ -188,7 +187,7 @@ class PhotoshopClientStub(): layer_id=layer_id, visibility=visibility)) - def _get_layers_metadata(self): + def get_layers_metadata(self): """ Reads layers metadata from Headline from active document in PS. (Headline accessible by File > File Info) @@ -242,7 +241,3 @@ class PhotoshopClientStub(): for d in layers_data: ret.append(namedtuple('Layer', d.keys())(*d.values())) return ret - - - - diff --git a/pype/plugins/photoshop/publish/collect_instances.py b/pype/plugins/photoshop/publish/collect_instances.py index 47584272d2..7e433bc92f 100644 --- a/pype/plugins/photoshop/publish/collect_instances.py +++ b/pype/plugins/photoshop/publish/collect_instances.py @@ -31,7 +31,7 @@ class CollectInstances(pyblish.api.ContextPlugin): photoshop_client = PhotoshopClientStub() layers = photoshop_client.get_layers() - layers_meta = photoshop_client._get_layers_metadata() + layers_meta = photoshop_client.get_layers_metadata() for layer in layers: layer_data = photoshop_client.read(layer, layers_meta) diff --git a/pype/plugins/photoshop/publish/extract_image.py b/pype/plugins/photoshop/publish/extract_image.py index 4cbac38ce7..e32444c641 100644 --- a/pype/plugins/photoshop/publish/extract_image.py +++ b/pype/plugins/photoshop/publish/extract_image.py @@ -3,8 +3,9 @@ import os import pype.api from avalon import photoshop -from pype.modules.websocket_server.clients.photoshop_client import \ - PhotoshopClientStub +from pype.modules.websocket_server.clients.photoshop_client import ( + PhotoshopClientStub +) class ExtractImage(pype.api.Extractor): @@ -23,12 +24,6 @@ class ExtractImage(pype.api.Extractor): staging_dir = self.staging_dir(instance) self.log.info("Outputting image to {}".format(staging_dir)) - layers = [] - for image_instance in instance.context: - if image_instance.data["family"] != "image": - continue - layers.append(image_instance[0]) - # Perform extraction photoshop_client = PhotoshopClientStub() files = {} @@ -37,7 +32,7 @@ class ExtractImage(pype.api.Extractor): with photoshop.maintained_visibility(): # Hide all other layers. extract_ids = set([ll.id for ll in photoshop_client. - get_layers_in_layers(layers)]) + get_layers_in_layers([instance[0]])]) for layer in photoshop_client.get_layers(): # limit unnecessary calls to client From dce5de49380c618b89cf899424e356e7bfe82c90 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 20 Aug 2020 17:31:20 +0200 Subject: [PATCH 118/813] Finish loader --- pype/modules/websocket_server/clients/photoshop_client.py | 3 +++ pype/plugins/photoshop/load/load_image.py | 7 ++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/pype/modules/websocket_server/clients/photoshop_client.py b/pype/modules/websocket_server/clients/photoshop_client.py index 330c2ceff0..a81870a4ee 100644 --- a/pype/modules/websocket_server/clients/photoshop_client.py +++ b/pype/modules/websocket_server/clients/photoshop_client.py @@ -208,6 +208,9 @@ class PhotoshopClientStub(): Args: path (str): File path to import. """ + self.websocketserver.call(self.client.call + ('Photoshop.import_smart_object', + path=path)) def replace_smart_object(self, layer, path): """ diff --git a/pype/plugins/photoshop/load/load_image.py b/pype/plugins/photoshop/load/load_image.py index a24280553c..1856155b2a 100644 --- a/pype/plugins/photoshop/load/load_image.py +++ b/pype/plugins/photoshop/load/load_image.py @@ -1,7 +1,8 @@ from avalon import api, photoshop -from pype.modules.websocket_server.clients.photoshop_client \ - import PhotoshopClientStub +from pype.modules.websocket_server.clients.photoshop_client import ( + PhotoshopClientStub +) photoshopClient = PhotoshopClientStub() @@ -17,7 +18,7 @@ class ImageLoader(api.Loader): def load(self, context, name=None, namespace=None, data=None): with photoshop.maintained_selection(): - layer = photoshop.import_smart_object(self.fname) + layer = photoshopClient.import_smart_object(self.fname) self[:] = [layer] From 07b34dec926f0135a03327d4c52aa2d2837e5199 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 20 Aug 2020 17:45:59 +0200 Subject: [PATCH 119/813] show only on project --- pype/modules/ftrack/events/action_push_frame_values_to_task.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/modules/ftrack/events/action_push_frame_values_to_task.py b/pype/modules/ftrack/events/action_push_frame_values_to_task.py index 5b7da8bebb..e6276d84ac 100644 --- a/pype/modules/ftrack/events/action_push_frame_values_to_task.py +++ b/pype/modules/ftrack/events/action_push_frame_values_to_task.py @@ -53,7 +53,7 @@ class PushFrameValuesToTaskAction(BaseAction): valid_selection = False for ent in event["data"]["selection"]: # Ignore entities that are not tasks or projects - if ent["entityType"].lower() in ["show", "task"]: + if ent["entityType"].lower() == "show": valid_selection = True break From fb6de46cd6c186934cd878c6e055dadf86c89d83 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 20 Aug 2020 19:05:56 +0200 Subject: [PATCH 120/813] pushing is also pushing to item itself --- .../action_push_frame_values_to_task.py | 279 ++++++++++++++---- 1 file changed, 221 insertions(+), 58 deletions(-) diff --git a/pype/modules/ftrack/events/action_push_frame_values_to_task.py b/pype/modules/ftrack/events/action_push_frame_values_to_task.py index e6276d84ac..d88f4a1016 100644 --- a/pype/modules/ftrack/events/action_push_frame_values_to_task.py +++ b/pype/modules/ftrack/events/action_push_frame_values_to_task.py @@ -15,8 +15,8 @@ class PushFrameValuesToTaskAction(BaseAction): variant = "- Push Frame values to Task" entities_query = ( - "select id, name, parent_id, link" - " from TypedContext where project_id is \"{}\"" + "select id, name, parent_id, link from TypedContext" + " where project_id is \"{}\" and object_type_id in ({})" ) cust_attrs_query = ( "select id, key, object_type_id, is_hierarchical, default" @@ -27,7 +27,13 @@ class PushFrameValuesToTaskAction(BaseAction): "select value, entity_id from CustomAttributeValue" " where entity_id in ({}) and configuration_id in ({})" ) - custom_attribute_keys = {"frameStart", "frameEnd"} + + pushing_entity_types = {"Shot"} + hierarchical_custom_attribute_keys = {"frameStart", "frameEnd"} + custom_attribute_mapping = { + "frameStart": "fstart", + "frameEnd": "fend" + } discover_role_list = {"Pypeclub", "Administrator", "Project Manager"} def register(self): @@ -76,29 +82,6 @@ class PushFrameValuesToTaskAction(BaseAction): return False def launch(self, session, entities, event): - # TODO this can be threaded - task_attrs_by_key, hier_attrs = self.frame_attributes(session) - missing_keys = [ - key - for key in self.custom_attribute_keys - if key not in task_attrs_by_key - ] - if missing_keys: - if len(missing_keys) == 1: - sub_msg = " \"{}\"".format(missing_keys[0]) - else: - sub_msg = "s {}".format(", ".join([ - "\"{}\"".format(key) - for key in missing_keys - ])) - - msg = "Missing Task's custom attribute{}.".format(sub_msg) - self.log.warning(msg) - return { - "success": False, - "message": msg - } - self.log.debug("{}: Creating job".format(self.label)) user_entity = session.query( @@ -115,12 +98,7 @@ class PushFrameValuesToTaskAction(BaseAction): try: project_entity = self.get_project_from_entity(entities[0]) - result = self.propagate_values( - session, - tuple(task_attrs_by_key.values()), - hier_attrs, - project_entity - ) + result = self.propagate_values(session, project_entity, event) job["status"] = "done" session.commit() @@ -143,12 +121,20 @@ class PushFrameValuesToTaskAction(BaseAction): job["status"] = "failed" session.commit() - def frame_attributes(self, session): + def task_attributes(self, session): task_object_type = session.query( "ObjectType where name is \"Task\"" ).one() - joined_keys = self.join_keys(self.custom_attribute_keys) + hier_attr_names = list( + self.custom_attribute_mapping.keys() + ) + entity_type_specific_names = list( + self.custom_attribute_mapping.values() + ) + joined_keys = self.join_keys( + hier_attr_names + entity_type_specific_names + ) attribute_entities = session.query( self.cust_attrs_query.format(joined_keys) ).all() @@ -158,47 +144,139 @@ class PushFrameValuesToTaskAction(BaseAction): for attr in attribute_entities: attr_key = attr["key"] if attr["is_hierarchical"]: - hier_attrs.append(attr) + if attr_key in hier_attr_names: + hier_attrs.append(attr) elif attr["object_type_id"] == task_object_type["id"]: - task_attrs[attr_key] = attr + if attr_key in entity_type_specific_names: + task_attrs[attr_key] = attr["id"] return task_attrs, hier_attrs def join_keys(self, items): return ",".join(["\"{}\"".format(item) for item in items]) - def propagate_values( - self, session, task_attrs, hier_attrs, project_entity - ): + def propagate_values(self, session, project_entity, event): self.log.debug("Querying project's entities \"{}\".".format( project_entity["full_name"] )) - entities = session.query( - self.entities_query.format(project_entity["id"]) - ).all() + pushing_entity_types = tuple( + ent_type.lower() + for ent_type in self.pushing_entity_types + ) + destination_object_types = [] + all_object_types = session.query("ObjectType").all() + for object_type in all_object_types: + lowered_name = object_type["name"].lower() + if ( + lowered_name == "task" + or lowered_name in pushing_entity_types + ): + destination_object_types.append(object_type) + + destination_object_type_ids = tuple( + obj_type["id"] + for obj_type in destination_object_types + ) + entities = session.query(self.entities_query.format( + project_entity["id"], + self.join_keys(destination_object_type_ids) + )).all() + + entities_by_id = { + entity["id"]: entity + for entity in entities + } self.log.debug("Filtering Task entities.") task_entities_by_parent_id = collections.defaultdict(list) + non_task_entities = [] + non_task_entity_ids = [] for entity in entities: - if entity.entity_type.lower() == "task": - task_entities_by_parent_id[entity["parent_id"]].append(entity) + if entity.entity_type.lower() != "task": + non_task_entities.append(entity) + non_task_entity_ids.append(entity["id"]) + continue + + parent_id = entity["parent_id"] + if parent_id in entities_by_id: + task_entities_by_parent_id[parent_id].append(entity) + + task_attr_id_by_keys, hier_attrs = self.task_attributes(session) self.log.debug("Getting Custom attribute values from tasks' parents.") hier_values_by_entity_id = self.get_hier_values( session, hier_attrs, - list(task_entities_by_parent_id.keys()) + non_task_entity_ids ) self.log.debug("Setting parents' values to task.") - self.set_task_attr_values( + task_missing_keys = self.set_task_attr_values( session, task_entities_by_parent_id, hier_values_by_entity_id, - task_attrs + task_attr_id_by_keys ) + self.log.debug("Setting values to entities themselves.") + missing_keys_by_object_name = self.push_values_to_entities( + session, + non_task_entities, + hier_values_by_entity_id + ) + if task_missing_keys: + missing_keys_by_object_name["Task"] = task_missing_keys + if missing_keys_by_object_name: + self.report(missing_keys_by_object_name, event) return True + def report(self, missing_keys_by_object_name, event): + splitter = {"type": "label", "value": "---"} + + title = "Push Custom Attribute values report:" + + items = [] + items.append({ + "type": "label", + "value": "# Pushing values was not complete" + }) + items.append({ + "type": "label", + "value": ( + "

It was due to missing custom" + " attribute configurations for specific entity type/s." + " These configurations are not created automatically.

" + ) + }) + + log_message_items = [] + log_message_item_template = ( + "Entity type \"{}\" does not have created Custom Attribute/s: {}" + ) + for object_name, missing_attr_names in ( + missing_keys_by_object_name.items() + ): + log_message_items.append(log_message_item_template.format( + object_name, self.join_keys(missing_attr_names) + )) + + items.append(splitter) + items.append({ + "type": "label", + "value": "## Entity type: {}".format(object_name) + }) + + items.append({ + "type": "label", + "value": "

{}

".format("
".join(missing_attr_names)) + }) + + self.log.warning(( + "Couldn't finish pushing attribute values because" + " few entity types miss Custom attribute configurations:\n{}" + ).format("\n".join(log_message_items))) + + self.show_interface(items, title, event) + def get_hier_values(self, session, hier_attrs, focus_entity_ids): joined_entity_ids = self.join_keys(focus_entity_ids) hier_attr_ids = self.join_keys( @@ -243,26 +321,28 @@ class PushFrameValuesToTaskAction(BaseAction): session, task_entities_by_parent_id, hier_values_by_entity_id, - task_attrs + task_attr_id_by_keys ): - task_attr_ids_by_key = { - attr["key"]: attr["id"] - for attr in task_attrs - } + missing_keys = set() total_parents = len(hier_values_by_entity_id) - idx = 1 + idx = 0 for parent_id, values in hier_values_by_entity_id.items(): - self.log.info(( - "[{}/{}] {} Processing values to children. Values: {}" - ).format(idx, total_parents, parent_id, values)) idx += 1 + self.log.info(( + "[{}/{}] {} Processing values to task. Values: {}" + ).format(idx, total_parents, parent_id, values)) task_entities = task_entities_by_parent_id[parent_id] - for key, value in values.items(): + for hier_key, value in values.items(): + key = self.custom_attribute_mapping[hier_key] + if key not in task_attr_id_by_keys: + missing_keys.add(key) + continue + for task_entity in task_entities: _entity_key = collections.OrderedDict({ - "configuration_id": task_attr_ids_by_key[key], + "configuration_id": task_attr_id_by_keys[key], "entity_id": task_entity["id"] }) @@ -277,6 +357,89 @@ class PushFrameValuesToTaskAction(BaseAction): ) session.commit() + return missing_keys + + def push_values_to_entities( + self, + session, + non_task_entities, + hier_values_by_entity_id + ): + object_types = session.query( + "ObjectType where name in ({})".format( + self.join_keys(self.pushing_entity_types) + ) + ).all() + object_type_names_by_id = { + object_type["id"]: object_type["name"] + for object_type in object_types + } + joined_keys = self.join_keys( + self.custom_attribute_mapping.values() + ) + attribute_entities = session.query( + self.cust_attrs_query.format(joined_keys) + ).all() + + attrs_by_obj_id = {} + for attr in attribute_entities: + if attr["is_hierarchical"]: + continue + + obj_id = attr["object_type_id"] + if obj_id not in object_type_names_by_id: + continue + + if obj_id not in attrs_by_obj_id: + attrs_by_obj_id[obj_id] = {} + + attr_key = attr["key"] + attrs_by_obj_id[obj_id][attr_key] = attr["id"] + + entities_by_obj_id = collections.defaultdict(list) + for entity in non_task_entities: + entities_by_obj_id[entity["object_type_id"]].append(entity) + + missing_keys_by_object_id = collections.defaultdict(set) + for obj_type_id, attr_keys in attrs_by_obj_id.items(): + entities = entities_by_obj_id.get(obj_type_id) + if not entities: + continue + + for entity in entities: + values = hier_values_by_entity_id.get(entity["id"]) + if not values: + continue + + for hier_key, value in values.items(): + key = self.custom_attribute_mapping[hier_key] + if key not in attr_keys: + missing_keys_by_object_id[obj_type_id].add(key) + continue + + _entity_key = collections.OrderedDict({ + "configuration_id": attr_keys[key], + "entity_id": entity["id"] + }) + + session.recorded_operations.push( + ftrack_api.operation.UpdateEntityOperation( + "ContextCustomAttributeValue", + _entity_key, + "value", + ftrack_api.symbol.NOT_SET, + value + ) + ) + session.commit() + + missing_keys_by_object_name = {} + for obj_id, missing_keys in missing_keys_by_object_id.items(): + obj_name = object_type_names_by_id[obj_id] + missing_keys_by_object_name[obj_name] = missing_keys + + return missing_keys_by_object_name + def register(session, plugins_presets={}): PushFrameValuesToTaskAction(session, plugins_presets).register() From 044434b35205f10e0d7d415d7d32ecefdbd65257 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 20 Aug 2020 19:32:36 +0200 Subject: [PATCH 121/813] event handle the same way as action --- .../events/event_push_frame_values_to_task.py | 145 ++++++++++++++---- 1 file changed, 114 insertions(+), 31 deletions(-) diff --git a/pype/modules/ftrack/events/event_push_frame_values_to_task.py b/pype/modules/ftrack/events/event_push_frame_values_to_task.py index dd5c5911ec..dd24110c1b 100644 --- a/pype/modules/ftrack/events/event_push_frame_values_to_task.py +++ b/pype/modules/ftrack/events/event_push_frame_values_to_task.py @@ -4,18 +4,27 @@ from pype.modules.ftrack import BaseEvent class PushFrameValuesToTaskEvent(BaseEvent): - """Action for testing purpose or as base for new actions.""" - cust_attrs_query = ( - "select id, key, object_type_id, is_hierarchical, default" - " from CustomAttributeConfiguration" - " where key in ({}) and object_type_id = {}" - ) - # Ignore event handler by default ignore_me = True - interest_attributes = ["frameStart", "frameEnd"] + cust_attrs_query = ( + "select id, key, object_type_id, is_hierarchical, default" + " from CustomAttributeConfiguration" + " where key in ({}) and object_type_id in ({})" + ) + + interest_entity_types = {"Shot"} + interest_attributes = {"frameStart", "frameEnd"} + interest_attr_mapping = { + "frameStart": "fstart", + "frameEnd": "fend" + } _cached_task_object_id = None + _cached_interest_object_ids = None + + @staticmethod + def join_keys(keys): + return ",".join(["\"{}\"".format(key) for key in keys]) @classmethod def task_object_id(cls, session): @@ -26,6 +35,20 @@ class PushFrameValuesToTaskEvent(BaseEvent): cls._cached_task_object_id = task_object_type["id"] return cls._cached_task_object_id + @classmethod + def interest_object_ids(cls, session): + if cls._cached_interest_object_ids is None: + object_types = session.query( + "ObjectType where name in ({})".format( + cls.join_keys(cls.interest_entity_types) + ) + ).all() + cls._cached_interest_object_ids = tuple( + object_type["id"] + for object_type in object_types + ) + return cls._cached_interest_object_ids + def extract_interesting_data(self, session, event): # Filter if event contain relevant data entities_info = event["data"].get("entities") @@ -60,60 +83,107 @@ class PushFrameValuesToTaskEvent(BaseEvent): interesting_data[entity_info["entityId"]] = entity_changes return interesting_data - def join_keys(self, keys): - return ",".join(["\"{}\"".format(key) for key in keys]) - - def get_task_entities(self, session, entities_info): - return session.query( - "Task where parent_id in ({})".format( - self.join_keys(entities_info.keys()) + def get_entities(self, session, interesting_data): + entities = session.query( + "TypedContext where id in ({})".format( + self.join_keys(interesting_data.keys()) ) ).all() - def task_attrs(self, session): - return session.query(self.cust_attrs_query.format( - self.join_keys(self.interest_attributes), - self.task_object_id(session) + output = [] + interest_object_ids = self.interest_object_ids(session) + for entity in entities: + if entity["object_type_id"] in interest_object_ids: + output.append(entity) + return output + + def get_task_entities(self, session, interesting_data): + return session.query( + "Task where parent_id in ({})".format( + self.join_keys(interesting_data.keys()) + ) + ).all() + + def attrs_configurations(self, session): + object_ids = list(self.interest_object_ids(session)) + object_ids.append(self.task_object_id(session)) + + attrs = session.query(self.cust_attrs_query.format( + self.join_keys(self.interest_attr_mapping.values()), + self.join_keys(object_ids) )).all() + output = {} + for attr in attrs: + obj_id = attr["object_type_id"] + if obj_id not in output: + output[obj_id] = {} + output[obj_id][attr["key"]] = attr["id"] + return output + def launch(self, session, event): interesting_data = self.extract_interesting_data(session, event) if not interesting_data: return + entities = self.get_entities(session, interesting_data) + if not entities: + return + + entities_by_id = { + entity["id"]: entity + for entity in entities + } + for entity_id in tuple(interesting_data.keys()): + if entity_id not in entities_by_id: + interesting_data.pop(entity_id) + task_entities = self.get_task_entities(session, interesting_data) if not task_entities: return - task_attrs = self.task_attrs(session) - if not task_attrs: + attrs_by_obj_id = self.attrs_configurations(session) + if not attrs_by_obj_id: self.log.warning(( "There is not created Custom Attributes {}" " for \"Task\" entity type." ).format(self.join_keys(self.interest_attributes))) return - task_attr_id_by_key = { - attr["key"]: attr["id"] - for attr in task_attrs - } task_entities_by_parent_id = collections.defaultdict(list) for task_entity in task_entities: task_entities_by_parent_id[task_entity["parent_id"]].append( task_entity ) + missing_keys_by_object_name = collections.defaultdict(set) for parent_id, values in interesting_data.items(): - task_entities = task_entities_by_parent_id[parent_id] + entities = task_entities_by_parent_id.get(parent_id) or [] + entities.append(entities_by_id[parent_id]) + for key, value in values.items(): changed_ids = [] - for task_entity in task_entities: - task_id = task_entity["id"] - changed_ids.append(task_id) + for entity in entities: + entity_attrs_mapping = ( + attrs_by_obj_id.get(entity["object_type_id"]) + ) + if not entity_attrs_mapping: + missing_keys_by_object_name[entity.entity_key].add( + key + ) + continue + configuration_id = entity_attrs_mapping.get(key) + if not configuration_id: + missing_keys_by_object_name[entity.entity_key].add( + key + ) + continue + + changed_ids.append(entity["id"]) entity_key = collections.OrderedDict({ - "configuration_id": task_attr_id_by_key[key], - "entity_id": task_id + "configuration_id": configuration_id, + "entity_id": entity["id"] }) if value is None: op = ftrack_api.operation.DeleteEntityOperation( @@ -142,6 +212,19 @@ class PushFrameValuesToTaskEvent(BaseEvent): "Changing of values failed.", exc_info=True ) + if not missing_keys_by_object_name: + return + + msg_items = [] + for object_name, missing_keys in missing_keys_by_object_name.items(): + msg_items.append( + "{}: ({})".format(object_name, self.join_keys(missing_keys)) + ) + + self.log.warning(( + "Missing Custom Attribute configuration" + " per specific object types: {}" + ).format(", ".join(msg_items))) def register(session, plugins_presets): From a628260c82d6c787960df501560fea8999acbc16 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 20 Aug 2020 19:33:42 +0200 Subject: [PATCH 122/813] moved code in better order --- .../events/event_push_frame_values_to_task.py | 144 +++++++++--------- 1 file changed, 72 insertions(+), 72 deletions(-) diff --git a/pype/modules/ftrack/events/event_push_frame_values_to_task.py b/pype/modules/ftrack/events/event_push_frame_values_to_task.py index dd24110c1b..d4056c2ae5 100644 --- a/pype/modules/ftrack/events/event_push_frame_values_to_task.py +++ b/pype/modules/ftrack/events/event_push_frame_values_to_task.py @@ -49,78 +49,6 @@ class PushFrameValuesToTaskEvent(BaseEvent): ) return cls._cached_interest_object_ids - def extract_interesting_data(self, session, event): - # Filter if event contain relevant data - entities_info = event["data"].get("entities") - if not entities_info: - return - - interesting_data = {} - for entity_info in entities_info: - # Care only about tasks - if entity_info.get("entityType") != "task": - continue - - # Care only about changes of status - changes = entity_info.get("changes") or {} - if not changes: - continue - - # Care only about changes if specific keys - entity_changes = {} - for key in self.interest_attributes: - if key in changes: - entity_changes[key] = changes[key]["new"] - - if not entity_changes: - continue - - # Do not care about "Task" entity_type - task_object_id = self.task_object_id(session) - if entity_info.get("objectTypeId") == task_object_id: - continue - - interesting_data[entity_info["entityId"]] = entity_changes - return interesting_data - - def get_entities(self, session, interesting_data): - entities = session.query( - "TypedContext where id in ({})".format( - self.join_keys(interesting_data.keys()) - ) - ).all() - - output = [] - interest_object_ids = self.interest_object_ids(session) - for entity in entities: - if entity["object_type_id"] in interest_object_ids: - output.append(entity) - return output - - def get_task_entities(self, session, interesting_data): - return session.query( - "Task where parent_id in ({})".format( - self.join_keys(interesting_data.keys()) - ) - ).all() - - def attrs_configurations(self, session): - object_ids = list(self.interest_object_ids(session)) - object_ids.append(self.task_object_id(session)) - - attrs = session.query(self.cust_attrs_query.format( - self.join_keys(self.interest_attr_mapping.values()), - self.join_keys(object_ids) - )).all() - - output = {} - for attr in attrs: - obj_id = attr["object_type_id"] - if obj_id not in output: - output[obj_id] = {} - output[obj_id][attr["key"]] = attr["id"] - return output - def launch(self, session, event): interesting_data = self.extract_interesting_data(session, event) if not interesting_data: @@ -226,6 +154,78 @@ class PushFrameValuesToTaskEvent(BaseEvent): " per specific object types: {}" ).format(", ".join(msg_items))) + def extract_interesting_data(self, session, event): + # Filter if event contain relevant data + entities_info = event["data"].get("entities") + if not entities_info: + return + + interesting_data = {} + for entity_info in entities_info: + # Care only about tasks + if entity_info.get("entityType") != "task": + continue + + # Care only about changes of status + changes = entity_info.get("changes") or {} + if not changes: + continue + + # Care only about changes if specific keys + entity_changes = {} + for key in self.interest_attributes: + if key in changes: + entity_changes[key] = changes[key]["new"] + + if not entity_changes: + continue + + # Do not care about "Task" entity_type + task_object_id = self.task_object_id(session) + if entity_info.get("objectTypeId") == task_object_id: + continue + + interesting_data[entity_info["entityId"]] = entity_changes + return interesting_data + + def get_entities(self, session, interesting_data): + entities = session.query( + "TypedContext where id in ({})".format( + self.join_keys(interesting_data.keys()) + ) + ).all() + + output = [] + interest_object_ids = self.interest_object_ids(session) + for entity in entities: + if entity["object_type_id"] in interest_object_ids: + output.append(entity) + return output + + def get_task_entities(self, session, interesting_data): + return session.query( + "Task where parent_id in ({})".format( + self.join_keys(interesting_data.keys()) + ) + ).all() + + def attrs_configurations(self, session): + object_ids = list(self.interest_object_ids(session)) + object_ids.append(self.task_object_id(session)) + + attrs = session.query(self.cust_attrs_query.format( + self.join_keys(self.interest_attr_mapping.values()), + self.join_keys(object_ids) + )).all() + + output = {} + for attr in attrs: + obj_id = attr["object_type_id"] + if obj_id not in output: + output[obj_id] = {} + output[obj_id][attr["key"]] = attr["id"] + return output + def register(session, plugins_presets): PushFrameValuesToTaskEvent(session, plugins_presets).register() From 293ceb8e0bffda6dd4f1975633c915f5457ccb6f Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 20 Aug 2020 19:33:53 +0200 Subject: [PATCH 123/813] fixed few minor bugs --- .../ftrack/events/event_push_frame_values_to_task.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/pype/modules/ftrack/events/event_push_frame_values_to_task.py b/pype/modules/ftrack/events/event_push_frame_values_to_task.py index d4056c2ae5..32993ef938 100644 --- a/pype/modules/ftrack/events/event_push_frame_values_to_task.py +++ b/pype/modules/ftrack/events/event_push_frame_values_to_task.py @@ -67,8 +67,6 @@ class PushFrameValuesToTaskEvent(BaseEvent): interesting_data.pop(entity_id) task_entities = self.get_task_entities(session, interesting_data) - if not task_entities: - return attrs_by_obj_id = self.attrs_configurations(session) if not attrs_by_obj_id: @@ -89,21 +87,22 @@ class PushFrameValuesToTaskEvent(BaseEvent): entities = task_entities_by_parent_id.get(parent_id) or [] entities.append(entities_by_id[parent_id]) - for key, value in values.items(): + for hier_key, value in values.items(): changed_ids = [] for entity in entities: + key = self.interest_attr_mapping[hier_key] entity_attrs_mapping = ( attrs_by_obj_id.get(entity["object_type_id"]) ) if not entity_attrs_mapping: - missing_keys_by_object_name[entity.entity_key].add( + missing_keys_by_object_name[entity.entity_type].add( key ) continue configuration_id = entity_attrs_mapping.get(key) if not configuration_id: - missing_keys_by_object_name[entity.entity_key].add( + missing_keys_by_object_name[entity.entity_type].add( key ) continue From 009a02f104019c64e94a7cacc4b7c8748c56d018 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 20 Aug 2020 19:39:49 +0200 Subject: [PATCH 124/813] removed unnecessary logs --- .../ftrack/events/action_push_frame_values_to_task.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/pype/modules/ftrack/events/action_push_frame_values_to_task.py b/pype/modules/ftrack/events/action_push_frame_values_to_task.py index d88f4a1016..dec34a58cb 100644 --- a/pype/modules/ftrack/events/action_push_frame_values_to_task.py +++ b/pype/modules/ftrack/events/action_push_frame_values_to_task.py @@ -326,13 +326,7 @@ class PushFrameValuesToTaskAction(BaseAction): missing_keys = set() total_parents = len(hier_values_by_entity_id) - idx = 0 for parent_id, values in hier_values_by_entity_id.items(): - idx += 1 - self.log.info(( - "[{}/{}] {} Processing values to task. Values: {}" - ).format(idx, total_parents, parent_id, values)) - task_entities = task_entities_by_parent_id[parent_id] for hier_key, value in values.items(): key = self.custom_attribute_mapping[hier_key] From fc2018e22b5a3e69a34577b123c07b7d522254c4 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 20 Aug 2020 20:12:03 +0200 Subject: [PATCH 125/813] Finished Creator --- .../clients/photoshop_client.py | 27 +++++++++++++--- pype/plugins/photoshop/create/create_image.py | 32 ++++++++++--------- 2 files changed, 39 insertions(+), 20 deletions(-) diff --git a/pype/modules/websocket_server/clients/photoshop_client.py b/pype/modules/websocket_server/clients/photoshop_client.py index a81870a4ee..4f0bca99cc 100644 --- a/pype/modules/websocket_server/clients/photoshop_client.py +++ b/pype/modules/websocket_server/clients/photoshop_client.py @@ -102,13 +102,28 @@ class PhotoshopClientStub(): return ret - def group_selected_layers(self): + def create_group(self, name): """ - Group selected layers into new layer - :return: + Create new group (eg. LayerSet) + :return: """ - self.websocketserver.call(self.client.call - ('Photoshop.group_selected_layers')) + ret = self.websocketserver.call(self.client.call + ('Photoshop.create_group', + name=name)) + # create group on PS is asynchronous, returns only id + layer = {"id": ret, "name": name, "group": True} + return namedtuple('Layer', layer.keys())(*layer.values()) + + def group_selected_layers(self, name): + """ + Group selected layers into new LayerSet (eg. group) + :return: + """ + res = self.websocketserver.call(self.client.call + ('Photoshop.group_selected_layers', + name=name) + ) + return self._to_records(res) def get_selected_layers(self): """ @@ -241,6 +256,8 @@ class PhotoshopClientStub(): raise ValueError("Received broken JSON {}".format(res)) ret = [] # convert to namedtuple to use dot donation + if isinstance(layers_data, dict): # TODO refactore + layers_data = [layers_data] for d in layers_data: ret.append(namedtuple('Layer', d.keys())(*d.values())) return ret diff --git a/pype/plugins/photoshop/create/create_image.py b/pype/plugins/photoshop/create/create_image.py index 5b2f9f7981..6b54b2a036 100644 --- a/pype/plugins/photoshop/create/create_image.py +++ b/pype/plugins/photoshop/create/create_image.py @@ -1,5 +1,8 @@ -from avalon import api, photoshop +from avalon import api from avalon.vendor import Qt +from pype.modules.websocket_server.clients.photoshop_client import ( + PhotoshopClientStub +) class CreateImage(api.Creator): @@ -13,11 +16,12 @@ class CreateImage(api.Creator): groups = [] layers = [] create_group = False - group_constant = photoshop.get_com_objects().constants().psLayerSet + + photoshopClient = PhotoshopClientStub() if (self.options or {}).get("useSelection"): multiple_instances = False - selection = photoshop.get_selected_layers() - + selection = photoshopClient.get_selected_layers() + self.log.info("selection {}".format(selection)) if len(selection) > 1: # Ask user whether to create one image or image per selected # item. @@ -40,19 +44,19 @@ class CreateImage(api.Creator): if multiple_instances: for item in selection: - if item.LayerType == group_constant: + if item.group: groups.append(item) else: layers.append(item) else: - group = photoshop.group_selected_layers() - group.Name = self.name + group = photoshopClient.group_selected_layers() + group.name = self.name groups.append(group) elif len(selection) == 1: # One selected item. Use group if its a LayerSet (group), else # create a new group. - if selection[0].LayerType == group_constant: + if selection[0].group: groups.append(selection[0]) else: layers.append(selection[0]) @@ -63,16 +67,14 @@ class CreateImage(api.Creator): create_group = True if create_group: - group = photoshop.app().ActiveDocument.LayerSets.Add() - group.Name = self.name + group = photoshopClient.create_group(self.name) groups.append(group) for layer in layers: - photoshop.select_layers([layer]) - group = photoshop.group_selected_layers() - group.Name = layer.Name + photoshopClient.select_layers([layer]) + group = photoshopClient.group_selected_layers(layer.name) groups.append(group) for group in groups: - self.data.update({"subset": "image" + group.Name}) - photoshop.imprint(group, self.data) + self.data.update({"subset": "image" + group.name}) + photoshopClient.imprint(group, self.data) From 4fb557c8376ed44280e1a73f50ba10a5c2e0d1e1 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 20 Aug 2020 20:15:09 +0200 Subject: [PATCH 126/813] Hound --- pype/plugins/photoshop/publish/collect_current_file.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pype/plugins/photoshop/publish/collect_current_file.py b/pype/plugins/photoshop/publish/collect_current_file.py index 604ce97f89..7877caa137 100644 --- a/pype/plugins/photoshop/publish/collect_current_file.py +++ b/pype/plugins/photoshop/publish/collect_current_file.py @@ -2,8 +2,9 @@ import os import pyblish.api -from pype.modules.websocket_server.clients.photoshop_client import \ - PhotoshopClientStub +from pype.modules.websocket_server.clients.photoshop_client import ( + PhotoshopClientStub +) class CollectCurrentFile(pyblish.api.ContextPlugin): From 7f5fc953ce98167b5f70127d6eb0506ec2d05cd3 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 20 Aug 2020 20:15:09 +0200 Subject: [PATCH 127/813] Hound --- pype/plugins/photoshop/publish/collect_current_file.py | 5 +++-- pype/plugins/photoshop/publish/extract_save_scene.py | 5 +++-- pype/plugins/photoshop/publish/increment_workfile.py | 5 +++-- pype/plugins/photoshop/publish/validate_instance_asset.py | 6 ++++-- pype/plugins/photoshop/publish/validate_naming.py | 5 +++-- 5 files changed, 16 insertions(+), 10 deletions(-) diff --git a/pype/plugins/photoshop/publish/collect_current_file.py b/pype/plugins/photoshop/publish/collect_current_file.py index 604ce97f89..7877caa137 100644 --- a/pype/plugins/photoshop/publish/collect_current_file.py +++ b/pype/plugins/photoshop/publish/collect_current_file.py @@ -2,8 +2,9 @@ import os import pyblish.api -from pype.modules.websocket_server.clients.photoshop_client import \ - PhotoshopClientStub +from pype.modules.websocket_server.clients.photoshop_client import ( + PhotoshopClientStub +) class CollectCurrentFile(pyblish.api.ContextPlugin): diff --git a/pype/plugins/photoshop/publish/extract_save_scene.py b/pype/plugins/photoshop/publish/extract_save_scene.py index 3357a05f24..c56e5418c9 100644 --- a/pype/plugins/photoshop/publish/extract_save_scene.py +++ b/pype/plugins/photoshop/publish/extract_save_scene.py @@ -1,8 +1,9 @@ import pype.api from avalon import photoshop -from pype.modules.websocket_server.clients.photoshop_client import \ - PhotoshopClientStub +from pype.modules.websocket_server.clients.photoshop_client import ( + PhotoshopClientStub +) class ExtractSaveScene(pype.api.Extractor): diff --git a/pype/plugins/photoshop/publish/increment_workfile.py b/pype/plugins/photoshop/publish/increment_workfile.py index 4298eb8e77..af8ba0b6ae 100644 --- a/pype/plugins/photoshop/publish/increment_workfile.py +++ b/pype/plugins/photoshop/publish/increment_workfile.py @@ -2,8 +2,9 @@ import pyblish.api from pype.action import get_errored_plugins_from_data from pype.lib import version_up -from pype.modules.websocket_server.clients.photoshop_client import \ - PhotoshopClientStub +from pype.modules.websocket_server.clients.photoshop_client import ( + PhotoshopClientStub +) class IncrementWorkfile(pyblish.api.InstancePlugin): diff --git a/pype/plugins/photoshop/publish/validate_instance_asset.py b/pype/plugins/photoshop/publish/validate_instance_asset.py index 4bbea69eb4..aa8d2661ff 100644 --- a/pype/plugins/photoshop/publish/validate_instance_asset.py +++ b/pype/plugins/photoshop/publish/validate_instance_asset.py @@ -4,8 +4,10 @@ import pyblish.api import pype.api from avalon import photoshop -from pype.modules.websocket_server.clients.photoshop_client import \ - PhotoshopClientStub +from pype.modules.websocket_server.clients.photoshop_client import ( + PhotoshopClientStub +) + class ValidateInstanceAssetRepair(pyblish.api.Action): """Repair the instance asset.""" diff --git a/pype/plugins/photoshop/publish/validate_naming.py b/pype/plugins/photoshop/publish/validate_naming.py index ba8a3e997e..c612270802 100644 --- a/pype/plugins/photoshop/publish/validate_naming.py +++ b/pype/plugins/photoshop/publish/validate_naming.py @@ -2,8 +2,9 @@ import pyblish.api import pype.api from avalon import photoshop -from pype.modules.websocket_server.clients.photoshop_client import \ - PhotoshopClientStub +from pype.modules.websocket_server.clients.photoshop_client import ( + PhotoshopClientStub +) class ValidateNamingRepair(pyblish.api.Action): From 576beb744687294bd12f48ce64d7a9ac8d1361bf Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 20 Aug 2020 20:34:50 +0200 Subject: [PATCH 128/813] removed unused variable --- pype/modules/ftrack/events/action_push_frame_values_to_task.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pype/modules/ftrack/events/action_push_frame_values_to_task.py b/pype/modules/ftrack/events/action_push_frame_values_to_task.py index dec34a58cb..a55c1e46a6 100644 --- a/pype/modules/ftrack/events/action_push_frame_values_to_task.py +++ b/pype/modules/ftrack/events/action_push_frame_values_to_task.py @@ -324,8 +324,6 @@ class PushFrameValuesToTaskAction(BaseAction): task_attr_id_by_keys ): missing_keys = set() - - total_parents = len(hier_values_by_entity_id) for parent_id, values in hier_values_by_entity_id.items(): task_entities = task_entities_by_parent_id[parent_id] for hier_key, value in values.items(): From 690bb9f8b16b85c36616ae0031a39874322827c5 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 21 Aug 2020 10:54:38 +0200 Subject: [PATCH 129/813] Fix missed providing name to group_selected_layers --- pype/plugins/photoshop/create/create_image.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pype/plugins/photoshop/create/create_image.py b/pype/plugins/photoshop/create/create_image.py index 6b54b2a036..0a019fe2f8 100644 --- a/pype/plugins/photoshop/create/create_image.py +++ b/pype/plugins/photoshop/create/create_image.py @@ -49,8 +49,7 @@ class CreateImage(api.Creator): else: layers.append(item) else: - group = photoshopClient.group_selected_layers() - group.name = self.name + group = photoshopClient.group_selected_layers(self.name) groups.append(group) elif len(selection) == 1: From b855038889b8b7ccb7e0e3cc849f7f896297c5a5 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 21 Aug 2020 12:56:14 +0200 Subject: [PATCH 130/813] change project overides that are only one file --- .../kuba_each_case/global/creator.json | 8 -------- .../kuba_each_case/global/intents.json | 3 --- .../kuba_each_case/plugins/maya/publish.json | 8 -------- .../kuba_each_case/project_presets.json | 18 ++++++++++++++++++ 4 files changed, 18 insertions(+), 19 deletions(-) delete mode 100644 pype/tools/config_setting/config/project_overrides/kuba_each_case/global/creator.json delete mode 100644 pype/tools/config_setting/config/project_overrides/kuba_each_case/global/intents.json delete mode 100644 pype/tools/config_setting/config/project_overrides/kuba_each_case/plugins/maya/publish.json create mode 100644 pype/tools/config_setting/config/project_overrides/kuba_each_case/project_presets.json diff --git a/pype/tools/config_setting/config/project_overrides/kuba_each_case/global/creator.json b/pype/tools/config_setting/config/project_overrides/kuba_each_case/global/creator.json deleted file mode 100644 index d14e779f01..0000000000 --- a/pype/tools/config_setting/config/project_overrides/kuba_each_case/global/creator.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "Model": ["model"], - "Render Globals": ["light", "render"], - "Layout": ["layout"], - "Set Dress": ["setdress"], - "Look": ["look"], - "Rig": ["rigging"] -} diff --git a/pype/tools/config_setting/config/project_overrides/kuba_each_case/global/intents.json b/pype/tools/config_setting/config/project_overrides/kuba_each_case/global/intents.json deleted file mode 100644 index bf147c7a19..0000000000 --- a/pype/tools/config_setting/config/project_overrides/kuba_each_case/global/intents.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "default": "test" -} diff --git a/pype/tools/config_setting/config/project_overrides/kuba_each_case/plugins/maya/publish.json b/pype/tools/config_setting/config/project_overrides/kuba_each_case/plugins/maya/publish.json deleted file mode 100644 index 46fc343b6c..0000000000 --- a/pype/tools/config_setting/config/project_overrides/kuba_each_case/plugins/maya/publish.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "ValidateModelName": { - "enabled": true - }, - "ValidateAssemblyName": { - "enabled": false - } -} diff --git a/pype/tools/config_setting/config/project_overrides/kuba_each_case/project_presets.json b/pype/tools/config_setting/config/project_overrides/kuba_each_case/project_presets.json new file mode 100644 index 0000000000..599a5dcbea --- /dev/null +++ b/pype/tools/config_setting/config/project_overrides/kuba_each_case/project_presets.json @@ -0,0 +1,18 @@ +{ + "__override_version__": 1, + "plugins": { + "maya": { + "__overriden_keys__": [ + "publish" + ], + "publish": { + "ValidateModelName": { + "enabled": true + }, + "ValidateAssemblyName": { + "enabled": false + } + } + } + } +} From d01d9d55da1d5e1715cdf3a25a360114c71308ce Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 21 Aug 2020 12:56:35 +0200 Subject: [PATCH 131/813] config can work with single file overrides --- pype/tools/config_setting/widgets/config.py | 34 +++++++++++++-------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/pype/tools/config_setting/widgets/config.py b/pype/tools/config_setting/widgets/config.py index 8d9d4fa1d2..321a97a4b8 100644 --- a/pype/tools/config_setting/widgets/config.py +++ b/pype/tools/config_setting/widgets/config.py @@ -18,8 +18,9 @@ config_path = os.path.dirname(os.path.dirname(__file__)) studio_presets_path = os.path.normpath( os.path.join(config_path, "config", "studio_presets") ) +project_configurations_dir = "project_presets" project_presets_path = os.path.normpath( - os.path.join(config_path, "config", "project_presets") + os.path.join(config_path, "config", project_configurations_dir) ) first_run = False @@ -118,15 +119,18 @@ def load_jsons_from_dir(path, *args, **kwargs): base_len = len(path) + 1 for base, _directories, filenames in os.walk(path): + base_items_str = base[base_len:] + if not base_items_str: + base_items = [] + else: + base_items = base_items_str.split(os.path.sep) + for filename in filenames: basename, ext = os.path.splitext(filename) if ext == ".json": full_path = os.path.join(base, filename) value = load_json(full_path) - - # dict_path = os.path.join(base[base_len:], basename) - # dict_keys = dict_path.split(os.path.sep) - dict_keys = base[base_len:].split(os.path.sep) + [basename] + dict_keys = base_items + [basename] output = subkey_merge(output, value, dict_keys) for sub_key in sub_keys: @@ -145,27 +149,31 @@ def global_project_presets(**kwargs): def project_preset_overrides(project_name, **kwargs): project_configs_path = os.environ.get("PYPE_PROJECT_CONFIGS") if project_name and project_configs_path: - return load_jsons_from_dir( + result = load_jsons_from_dir( os.path.join(project_configs_path, project_name), **kwargs ) + print(json.dumps(result, indent=4)) + if result: + result = result.get(project_configurations_dir) or {} + return result return {} def merge_overrides(global_dict, override_dict): if OVERRIDEN_KEY in override_dict: - _override = override_dict.pop(OVERRIDEN_KEY) - if _override: - return override_dict + overriden_keys = set(override_dict.pop(OVERRIDEN_KEY)) + else: + overriden_keys = set() for key, value in override_dict.items(): if value == POP_KEY: global_dict.pop(key) - elif key == OVERRIDEN_KEY: - continue - - elif key not in global_dict: + elif ( + key in overriden_keys + or key not in global_dict + ): global_dict[key] = value elif isinstance(value, dict) and isinstance(global_dict[key], dict): From 742c2b069b28203ec1703f36109d5497f00e1690 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 21 Aug 2020 13:12:04 +0200 Subject: [PATCH 132/813] removed debug print --- pype/tools/config_setting/widgets/lib.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pype/tools/config_setting/widgets/lib.py b/pype/tools/config_setting/widgets/lib.py index fd3f45b590..d733396d59 100644 --- a/pype/tools/config_setting/widgets/lib.py +++ b/pype/tools/config_setting/widgets/lib.py @@ -28,7 +28,6 @@ def convert_gui_data_to_overrides(data, first=True): metadata = data.pop(METADATA_KEY) for key, value in metadata.items(): if key == "groups": - print("**", value) output[OVERRIDEN_KEY] = value else: KeyError("Unknown metadata key \"{}\"".format(key)) From d830ad8b00811bea3fd2937d7c82a8f9b03db5fe Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Fri, 21 Aug 2020 15:13:36 +0200 Subject: [PATCH 133/813] deal with tasks in edit --- pype/plugins/global/publish/extract_hierarchy_avalon.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pype/plugins/global/publish/extract_hierarchy_avalon.py b/pype/plugins/global/publish/extract_hierarchy_avalon.py index ab8226f6ef..1d8191f2e3 100644 --- a/pype/plugins/global/publish/extract_hierarchy_avalon.py +++ b/pype/plugins/global/publish/extract_hierarchy_avalon.py @@ -78,6 +78,11 @@ class ExtractHierarchyToAvalon(pyblish.api.ContextPlugin): if entity: # Do not override data, only update cur_entity_data = entity.get("data") or {} + new_tasks = data.pop("tasks", []) + if "tasks" in cur_entity_data and new_tasks: + for task_name in new_tasks: + if task_name not in cur_entity_data["tasks"]: + cur_entity_data["tasks"].append(task_name) cur_entity_data.update(data) data = cur_entity_data else: From f8743e3b80dedfb37da392fd4c7108e5680ae2f6 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 21 Aug 2020 15:23:35 +0200 Subject: [PATCH 134/813] Removed usage of http_server --- .../websocket_server/hosts/photoshop.py | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/pype/modules/websocket_server/hosts/photoshop.py b/pype/modules/websocket_server/hosts/photoshop.py index c63f66865e..ae72963b1b 100644 --- a/pype/modules/websocket_server/hosts/photoshop.py +++ b/pype/modules/websocket_server/hosts/photoshop.py @@ -1,5 +1,8 @@ from pype.api import Logger from wsrpc_aiohttp import WebSocketRoute +import functools + +import avalon.photoshop as photoshop log = Logger().get_logger("WebsocketServer") @@ -31,3 +34,31 @@ class Photoshop(WebSocketRoute): log.debug("photoshop.read client calls server server calls " "Photo client") return await self.socket.call('Photoshop.read') + + # panel routes for tools + async def creator_route(self): + self._tool_route("creator") + + async def workfiles_route(self): + self._tool_route("workfiles") + + async def loader_route(self): + self._tool_route("loader") + + async def publish_route(self): + self._tool_route("publish") + + async def sceneinventory_route(self): + self._tool_route("sceneinventory") + + async def projectmanager_route(self): + self._tool_route("projectmanager") + + def _tool_route(self, tool_name): + """The address accessed when clicking on the buttons.""" + partial_method = functools.partial(photoshop.show, tool_name) + + photoshop.execute_in_main_thread(partial_method) + + # Required return statement. + return "nothing" \ No newline at end of file From 7584d1ef72e0a03364ff112ec1475be6825cfdbd Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Fri, 21 Aug 2020 15:29:49 +0200 Subject: [PATCH 135/813] load audio and refence in harmony --- pype/plugins/harmony/load/load_audio.py | 2 +- pype/plugins/harmony/load/load_imagesequence.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/plugins/harmony/load/load_audio.py b/pype/plugins/harmony/load/load_audio.py index 694fda3247..600791e61a 100644 --- a/pype/plugins/harmony/load/load_audio.py +++ b/pype/plugins/harmony/load/load_audio.py @@ -31,7 +31,7 @@ func class ImportAudioLoader(api.Loader): """Import audio.""" - families = ["shot"] + families = ["shot", "audio"] representations = ["wav"] label = "Import Audio" diff --git a/pype/plugins/harmony/load/load_imagesequence.py b/pype/plugins/harmony/load/load_imagesequence.py index 774782b092..c5f50a7d23 100644 --- a/pype/plugins/harmony/load/load_imagesequence.py +++ b/pype/plugins/harmony/load/load_imagesequence.py @@ -230,7 +230,7 @@ class ImageSequenceLoader(api.Loader): """Load images Stores the imported asset in a container named after the asset. """ - families = ["shot", "render", "image", "plate"] + families = ["shot", "render", "image", "plate", "reference"] representations = ["jpeg", "png", "jpg"] def load(self, context, name=None, namespace=None, data=None): From f8492befdfe8b4a0c5f73139f61a046ec1fac645 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 21 Aug 2020 15:38:09 +0200 Subject: [PATCH 136/813] fixed getting overrides --- pype/tools/config_setting/widgets/config.py | 34 +++++++++++---------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/pype/tools/config_setting/widgets/config.py b/pype/tools/config_setting/widgets/config.py index 321a97a4b8..7bce18b7f4 100644 --- a/pype/tools/config_setting/widgets/config.py +++ b/pype/tools/config_setting/widgets/config.py @@ -18,9 +18,9 @@ config_path = os.path.dirname(os.path.dirname(__file__)) studio_presets_path = os.path.normpath( os.path.join(config_path, "config", "studio_presets") ) -project_configurations_dir = "project_presets" +PROJECT_CONFIGURATION_DIR = "project_presets" project_presets_path = os.path.normpath( - os.path.join(config_path, "config", project_configurations_dir) + os.path.join(config_path, "config", PROJECT_CONFIGURATION_DIR) ) first_run = False @@ -146,18 +146,20 @@ def global_project_presets(**kwargs): return load_jsons_from_dir(project_presets_path, **kwargs) +def path_to_project_overrides(project_name): + project_configs_path = os.environ["PYPE_PROJECT_CONFIGS"] + dirpath = os.path.join(project_configs_path, project_name) + return os.path.join(dirpath, PROJECT_CONFIGURATION_DIR + ".json") + + def project_preset_overrides(project_name, **kwargs): - project_configs_path = os.environ.get("PYPE_PROJECT_CONFIGS") - if project_name and project_configs_path: - result = load_jsons_from_dir( - os.path.join(project_configs_path, project_name), - **kwargs - ) - print(json.dumps(result, indent=4)) - if result: - result = result.get(project_configurations_dir) or {} - return result - return {} + if not project_name: + return {} + + path_to_json = path_to_project_overrides(project_name) + if not os.path.exists(path_to_json): + return {} + return load_json(path_to_json) def merge_overrides(global_dict, override_dict): @@ -227,7 +229,7 @@ def replace_inner_schemas(schema_data, schema_collection): return schema_data -class ShemaMissingFileInfo(Exception): +class SchemaMissingFileInfo(Exception): def __init__(self, invalid): full_path_keys = [] for item in invalid: @@ -237,7 +239,7 @@ class ShemaMissingFileInfo(Exception): "Schema has missing definition of output file (\"is_file\" key)" " for keys. [{}]" ).format(", ".join(full_path_keys)) - super(ShemaMissingFileInfo, self).__init__(msg) + super(SchemaMissingFileInfo, self).__init__(msg) def file_keys_from_schema(schema_data): @@ -290,7 +292,7 @@ def validate_all_has_ending_file(schema_data, is_top=True): if not is_top: return invalid - raise ShemaMissingFileInfo(invalid) + raise SchemaMissingFileInfo(invalid) def validate_schema(schema_data): From 33fc39bf6265eac2713f704efc540c4f04d3a3ac Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 21 Aug 2020 14:38:10 +0100 Subject: [PATCH 137/813] Update subset families on integration --- pype/plugins/global/publish/integrate_new.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pype/plugins/global/publish/integrate_new.py b/pype/plugins/global/publish/integrate_new.py index a3c2ffe52b..cc106ad8a2 100644 --- a/pype/plugins/global/publish/integrate_new.py +++ b/pype/plugins/global/publish/integrate_new.py @@ -680,6 +680,12 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin): instance.data.get('subsetGroup')}} ) + # Update families on subset. + io.update_many( + {"type": "subset", "_id": io.ObjectId(subset["_id"])}, + {"$set": {"data.families": instance.data.get("families", [])}} + ) + return subset def create_version(self, subset, version_number, data=None): From 1043b70550ac42409329dc6e6b43fce8d2517028 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 21 Aug 2020 15:38:26 +0200 Subject: [PATCH 138/813] added override saving --- .../projects_schema/project_gui_schema.json | 3 +-- pype/tools/config_setting/widgets/base.py | 17 ++++++++++++----- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/pype/tools/config_setting/config_gui_schema/projects_schema/project_gui_schema.json b/pype/tools/config_setting/config_gui_schema/projects_schema/project_gui_schema.json index 366400e5ff..0405524b40 100644 --- a/pype/tools/config_setting/config_gui_schema/projects_schema/project_gui_schema.json +++ b/pype/tools/config_setting/config_gui_schema/projects_schema/project_gui_schema.json @@ -1,7 +1,6 @@ { - "key": "studio", + "key": "project", "type": "dict-invisible", - "label": "Studio", "children": [ { "type": "schema", diff --git a/pype/tools/config_setting/widgets/base.py b/pype/tools/config_setting/widgets/base.py index bdacb60559..259ec9e02c 100644 --- a/pype/tools/config_setting/widgets/base.py +++ b/pype/tools/config_setting/widgets/base.py @@ -74,8 +74,6 @@ class StudioWidget(QtWidgets.QWidget, PypeConfigurationWidget): content_layout.setAlignment(QtCore.Qt.AlignTop) content_widget.setLayout(content_layout) - # scroll_widget.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) - # scroll_widget.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) scroll_widget.setWidgetResizable(True) scroll_widget.setWidget(content_widget) @@ -404,8 +402,17 @@ class ProjectWidget(QtWidgets.QWidget, PypeConfigurationWidget): if groups: data[METADATA_KEY] = {"groups": groups} - output = convert_gui_data_to_overrides(data) - print(json.dumps(output, indent=4)) + output_data = convert_gui_data_to_overrides(data) + + overrides_json_path = config.path_to_project_overrides( + self.project_name + ) + dirpath = os.path.dirname(overrides_json_path) + if not os.path.exists(dirpath): + os.makedirs(dirpath) + + with open(overrides_json_path, "w") as file_stream: + json.dump(output_data, file_stream, indent=4) def _save_defaults(self): output = {} @@ -429,7 +436,7 @@ class ProjectWidget(QtWidgets.QWidget, PypeConfigurationWidget): all_values = _all_values # Skip first key - all_values = all_values["studio"] + all_values = all_values["project"] # Load studio data with metadata current_presets = config.studio_presets() From 4ce1f70f88909ff0d60dbb6f45d42580c0428d88 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 21 Aug 2020 15:57:50 +0200 Subject: [PATCH 139/813] saving works properly --- .../kuba_each_case/project_presets.json | 2 +- pype/tools/config_setting/widgets/base.py | 13 ++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/pype/tools/config_setting/config/project_overrides/kuba_each_case/project_presets.json b/pype/tools/config_setting/config/project_overrides/kuba_each_case/project_presets.json index 599a5dcbea..b9da242453 100644 --- a/pype/tools/config_setting/config/project_overrides/kuba_each_case/project_presets.json +++ b/pype/tools/config_setting/config/project_overrides/kuba_each_case/project_presets.json @@ -15,4 +15,4 @@ } } } -} +} \ No newline at end of file diff --git a/pype/tools/config_setting/widgets/base.py b/pype/tools/config_setting/widgets/base.py index 259ec9e02c..2421c02a25 100644 --- a/pype/tools/config_setting/widgets/base.py +++ b/pype/tools/config_setting/widgets/base.py @@ -390,18 +390,17 @@ class ProjectWidget(QtWidgets.QWidget, PypeConfigurationWidget): self._save_overrides() def _save_overrides(self): - data = {} - groups = [] + _data = {} for item in self.input_fields: value, is_group = item.overrides() if value is not NOT_SET: - data.update(value) - + _data.update(value) if is_group: - groups.extend(value.keys()) + raise Exception( + "Top item can't be overriden in Project widget." + ) - if groups: - data[METADATA_KEY] = {"groups": groups} + data = _data.get("project") or {} output_data = convert_gui_data_to_overrides(data) overrides_json_path = config.path_to_project_overrides( From 993b0ab21fdc60461176894f461f936721231152 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 21 Aug 2020 16:03:27 +0200 Subject: [PATCH 140/813] default project configurations works --- pype/tools/config_setting/widgets/base.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/pype/tools/config_setting/widgets/base.py b/pype/tools/config_setting/widgets/base.py index 2421c02a25..0563f942be 100644 --- a/pype/tools/config_setting/widgets/base.py +++ b/pype/tools/config_setting/widgets/base.py @@ -357,6 +357,7 @@ class ProjectWidget(QtWidgets.QWidget, PypeConfigurationWidget): schema = config.gui_schema("projects_schema", "project_gui_schema") self.keys = schema.get("keys", []) self.add_children_gui(schema, values) + self.schema = schema def add_children_gui(self, child_configuration, values): item_type = child_configuration["type"] @@ -422,10 +423,6 @@ class ProjectWidget(QtWidgets.QWidget, PypeConfigurationWidget): _output = {key: output} output = _output - print(json.dumps(output, indent=4)) - return - - # TODO check implementation copied from studio all_values = {} for item in self.input_fields: all_values.update(item.config_value()) @@ -438,7 +435,7 @@ class ProjectWidget(QtWidgets.QWidget, PypeConfigurationWidget): all_values = all_values["project"] # Load studio data with metadata - current_presets = config.studio_presets() + current_presets = config.global_project_presets() keys_to_file = config.file_keys_from_schema(self.schema) for key_sequence in keys_to_file: @@ -458,7 +455,7 @@ class ProjectWidget(QtWidgets.QWidget, PypeConfigurationWidget): origin_values.update(new_values) output_path = os.path.join( - config.studio_presets_path, subpath + config.project_presets_path, subpath ) dirpath = os.path.dirname(output_path) if not os.path.exists(dirpath): From 1c47579334406a1b650c05d49b3dcf99ae8e0023 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 21 Aug 2020 16:08:55 +0200 Subject: [PATCH 141/813] modified getting paths --- pype/tools/config_setting/widgets/config.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/pype/tools/config_setting/widgets/config.py b/pype/tools/config_setting/widgets/config.py index 7bce18b7f4..b071d81afe 100644 --- a/pype/tools/config_setting/widgets/config.py +++ b/pype/tools/config_setting/widgets/config.py @@ -4,29 +4,25 @@ import logging import copy # DEBUG SETUP -os.environ["AVALON_PROJECT"] = "kuba_each_case" +os.environ["PYPE_CONFIG"] = os.path.dirname(os.path.dirname(__file__)) os.environ["PYPE_PROJECT_CONFIGS"] = os.path.join( - os.path.dirname(os.path.dirname(__file__)), - "config", - "project_overrides" + os.environ["PYPE_CONFIG"], "config", "project_overrides" ) -# log = logging.getLogger(__name__) -config_path = os.path.dirname(os.path.dirname(__file__)) studio_presets_path = os.path.normpath( - os.path.join(config_path, "config", "studio_presets") + os.path.join(os.environ["PYPE_CONFIG"], "config", "studio_presets") ) PROJECT_CONFIGURATION_DIR = "project_presets" project_presets_path = os.path.normpath( - os.path.join(config_path, "config", PROJECT_CONFIGURATION_DIR) + os.path.join(os.environ["PYPE_CONFIG"], "config", PROJECT_CONFIGURATION_DIR) ) first_run = False -OVERRIDEN_KEY = "__overriden_keys__" # TODO key popping not implemented yet POP_KEY = "__pop_key__" +OVERRIDEN_KEY = "__overriden_keys__" def load_json(fpath): From 57cac7135ddb644ba6e3ec9a08efb0d34452febb Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 21 Aug 2020 16:10:24 +0200 Subject: [PATCH 142/813] changed global variables --- pype/tools/config_setting/widgets/base.py | 4 ++-- pype/tools/config_setting/widgets/config.py | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pype/tools/config_setting/widgets/base.py b/pype/tools/config_setting/widgets/base.py index 0563f942be..0fc9c5cabf 100644 --- a/pype/tools/config_setting/widgets/base.py +++ b/pype/tools/config_setting/widgets/base.py @@ -147,7 +147,7 @@ class StudioWidget(QtWidgets.QWidget, PypeConfigurationWidget): origin_values.update(new_values) output_path = os.path.join( - config.studio_presets_path, subpath + config.STUDIO_PRESETS_PATH, subpath ) dirpath = os.path.dirname(output_path) if not os.path.exists(dirpath): @@ -455,7 +455,7 @@ class ProjectWidget(QtWidgets.QWidget, PypeConfigurationWidget): origin_values.update(new_values) output_path = os.path.join( - config.project_presets_path, subpath + config.PROJECT_PRESETS_PATH, subpath ) dirpath = os.path.dirname(output_path) if not os.path.exists(dirpath): diff --git a/pype/tools/config_setting/widgets/config.py b/pype/tools/config_setting/widgets/config.py index b071d81afe..34eec69fc7 100644 --- a/pype/tools/config_setting/widgets/config.py +++ b/pype/tools/config_setting/widgets/config.py @@ -11,13 +11,13 @@ os.environ["PYPE_PROJECT_CONFIGS"] = os.path.join( log = logging.getLogger(__name__) -studio_presets_path = os.path.normpath( +STUDIO_PRESETS_PATH = os.path.normpath( os.path.join(os.environ["PYPE_CONFIG"], "config", "studio_presets") ) PROJECT_CONFIGURATION_DIR = "project_presets" -project_presets_path = os.path.normpath( - os.path.join(os.environ["PYPE_CONFIG"], "config", PROJECT_CONFIGURATION_DIR) -) +PROJECT_PRESETS_PATH = os.path.normpath(os.path.join( + os.environ["PYPE_CONFIG"], "config", PROJECT_CONFIGURATION_DIR +)) first_run = False # TODO key popping not implemented yet @@ -135,11 +135,11 @@ def load_jsons_from_dir(path, *args, **kwargs): def studio_presets(*args, **kwargs): - return load_jsons_from_dir(studio_presets_path, *args, **kwargs) + return load_jsons_from_dir(STUDIO_PRESETS_PATH, *args, **kwargs) def global_project_presets(**kwargs): - return load_jsons_from_dir(project_presets_path, **kwargs) + return load_jsons_from_dir(PROJECT_PRESETS_PATH, **kwargs) def path_to_project_overrides(project_name): From ad7b7c6e220ccb0993e46f3e9036ee97ac531010 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 21 Aug 2020 16:19:59 +0200 Subject: [PATCH 143/813] fix(global): not default True value --- pype/plugins/global/publish/extract_jpeg.py | 2 +- pype/plugins/global/publish/extract_review.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/plugins/global/publish/extract_jpeg.py b/pype/plugins/global/publish/extract_jpeg.py index dd5f79b6ac..f6f41d1397 100644 --- a/pype/plugins/global/publish/extract_jpeg.py +++ b/pype/plugins/global/publish/extract_jpeg.py @@ -27,7 +27,7 @@ class ExtractJpegEXR(pyblish.api.InstancePlugin): return # Skip review when requested. - if not instance.data.get("review"): + if not instance.data.get("review", True): return # get representation and loop them diff --git a/pype/plugins/global/publish/extract_review.py b/pype/plugins/global/publish/extract_review.py index 3a5bd3464a..0bae1b2ddc 100644 --- a/pype/plugins/global/publish/extract_review.py +++ b/pype/plugins/global/publish/extract_review.py @@ -51,7 +51,7 @@ class ExtractReview(pyblish.api.InstancePlugin): def process(self, instance): # Skip review when requested. - if not instance.data.get("review"): + if not instance.data.get("review", True): return # ffmpeg doesn't support multipart exrs From b37037007e6b7b300e98cb3cdf96809f452ce2eb Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 21 Aug 2020 15:43:52 +0100 Subject: [PATCH 144/813] Fix optional skip reviews on renders. --- pype/plugins/global/publish/submit_publish_job.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pype/plugins/global/publish/submit_publish_job.py b/pype/plugins/global/publish/submit_publish_job.py index 2fe6735e90..bb8473dcf6 100644 --- a/pype/plugins/global/publish/submit_publish_job.py +++ b/pype/plugins/global/publish/submit_publish_job.py @@ -729,7 +729,8 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): "pixelAspect": data.get("pixelAspect", 1), "resolutionWidth": data.get("resolutionWidth", 1920), "resolutionHeight": data.get("resolutionHeight", 1080), - "multipartExr": data.get("multipartExr", False) + "multipartExr": data.get("multipartExr", False), + "review": data.get("review", True) } if "prerender" in instance.data["families"]: From 104027c17c494969fb0ae1c78e57b1eb910f80a1 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 21 Aug 2020 16:22:41 +0100 Subject: [PATCH 145/813] Integrate family as well --- pype/plugins/global/publish/integrate_new.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pype/plugins/global/publish/integrate_new.py b/pype/plugins/global/publish/integrate_new.py index cc106ad8a2..142e72e3ac 100644 --- a/pype/plugins/global/publish/integrate_new.py +++ b/pype/plugins/global/publish/integrate_new.py @@ -681,9 +681,11 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin): ) # Update families on subset. + families = [instance.data["family"]] + families.extend(instance.data.get("families", [])) io.update_many( {"type": "subset", "_id": io.ObjectId(subset["_id"])}, - {"$set": {"data.families": instance.data.get("families", [])}} + {"$set": {"data.families": families}} ) return subset From 0d711c3b7f8c28cd92a3674eeff880766c7f8054 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 21 Aug 2020 16:25:48 +0100 Subject: [PATCH 146/813] Get linked assets from "inputs". --- pype/lib.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pype/lib.py b/pype/lib.py index 7cf4e2f1a5..601c85f521 100644 --- a/pype/lib.py +++ b/pype/lib.py @@ -746,8 +746,9 @@ class PypeHook: def get_linked_assets(asset_entity): """Return linked assets for `asset_entity`.""" - # TODO implement - return [] + inputs = asset_entity["data"].get("inputs", []) + inputs = [io.find_one({"_id": x}) for x in inputs] + return inputs def map_subsets_by_family(subsets): From 1ac12eadb2d3e2a4c0530e830e195c30e4cf9970 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 21 Aug 2020 17:38:03 +0200 Subject: [PATCH 147/813] reorganized config setting tool to usable as tool --- pype/tools/config_setting/__init__.py | 7 + pype/tools/config_setting/__main__.py | 18 + .../config_setting/config_setting/__init__.py | 10 + .../ftrack_projects_gui_schema.json | 0 .../projects_schema/plugins_gui_schema.json | 0 .../projects_schema/project_gui_schema.json | 0 .../projects_schema/test_project_schema.json | 0 .../applications_gui_schema.json | 0 .../studio_schema/studio_gui_schema.json | 0 .../studio_schema/tools_gui_schema.json | 0 .../{ => config_setting}/style/__init__.py | 0 .../{ => config_setting}/style/pype_icon.png | Bin .../{ => config_setting}/style/style.css | 0 .../config_setting/widgets/__init__.py | 19 + .../{ => config_setting}/widgets/base.py | 28 +- .../{ => config_setting}/widgets/inputs.py | 6 +- .../config_setting/widgets/lib.py | 182 ++++++++++ .../{ => config_setting}/widgets/main.py | 0 .../{ => config_setting}/widgets/tests.py | 0 .../{ => config_setting}/widgets/widgets.py | 0 pype/tools/config_setting/interface.py | 56 --- pype/tools/config_setting/widgets/__init__.py | 6 - pype/tools/config_setting/widgets/config.py | 325 ------------------ pype/tools/config_setting/widgets/lib.py | 54 --- 24 files changed, 251 insertions(+), 460 deletions(-) create mode 100644 pype/tools/config_setting/__init__.py create mode 100644 pype/tools/config_setting/__main__.py create mode 100644 pype/tools/config_setting/config_setting/__init__.py rename pype/tools/config_setting/{ => config_setting}/config_gui_schema/projects_schema/ftrack_projects_gui_schema.json (100%) rename pype/tools/config_setting/{ => config_setting}/config_gui_schema/projects_schema/plugins_gui_schema.json (100%) rename pype/tools/config_setting/{ => config_setting}/config_gui_schema/projects_schema/project_gui_schema.json (100%) rename pype/tools/config_setting/{ => config_setting}/config_gui_schema/projects_schema/test_project_schema.json (100%) rename pype/tools/config_setting/{ => config_setting}/config_gui_schema/studio_schema/applications_gui_schema.json (100%) rename pype/tools/config_setting/{ => config_setting}/config_gui_schema/studio_schema/studio_gui_schema.json (100%) rename pype/tools/config_setting/{ => config_setting}/config_gui_schema/studio_schema/tools_gui_schema.json (100%) rename pype/tools/config_setting/{ => config_setting}/style/__init__.py (100%) rename pype/tools/config_setting/{ => config_setting}/style/pype_icon.png (100%) rename pype/tools/config_setting/{ => config_setting}/style/style.css (100%) create mode 100644 pype/tools/config_setting/config_setting/widgets/__init__.py rename pype/tools/config_setting/{ => config_setting}/widgets/base.py (95%) rename pype/tools/config_setting/{ => config_setting}/widgets/inputs.py (99%) create mode 100644 pype/tools/config_setting/config_setting/widgets/lib.py rename pype/tools/config_setting/{ => config_setting}/widgets/main.py (100%) rename pype/tools/config_setting/{ => config_setting}/widgets/tests.py (100%) rename pype/tools/config_setting/{ => config_setting}/widgets/widgets.py (100%) delete mode 100644 pype/tools/config_setting/interface.py delete mode 100644 pype/tools/config_setting/widgets/__init__.py delete mode 100644 pype/tools/config_setting/widgets/config.py delete mode 100644 pype/tools/config_setting/widgets/lib.py diff --git a/pype/tools/config_setting/__init__.py b/pype/tools/config_setting/__init__.py new file mode 100644 index 0000000000..c3bd49449d --- /dev/null +++ b/pype/tools/config_setting/__init__.py @@ -0,0 +1,7 @@ +from config_setting import style, MainWidget + + +__all__ = ( + "style", + "MainWidget" +) diff --git a/pype/tools/config_setting/__main__.py b/pype/tools/config_setting/__main__.py new file mode 100644 index 0000000000..171b85a775 --- /dev/null +++ b/pype/tools/config_setting/__main__.py @@ -0,0 +1,18 @@ +import os +import sys + +import config_setting +from Qt import QtWidgets, QtGui + + +if __name__ == "__main__": + app = QtWidgets.QApplication(sys.argv) + + stylesheet = config_setting.style.load_stylesheet() + app.setStyleSheet(stylesheet) + app.setWindowIcon(QtGui.QIcon(config_setting.style.app_icon_path())) + + widget = config_setting.MainWidget() + widget.show() + + sys.exit(app.exec_()) diff --git a/pype/tools/config_setting/config_setting/__init__.py b/pype/tools/config_setting/config_setting/__init__.py new file mode 100644 index 0000000000..835754e6a1 --- /dev/null +++ b/pype/tools/config_setting/config_setting/__init__.py @@ -0,0 +1,10 @@ +from . import style +# from . import widgets +from .widgets import MainWidget + + +__all__ = ( + "style", + # "widgets", + "MainWidget" +) diff --git a/pype/tools/config_setting/config_gui_schema/projects_schema/ftrack_projects_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/ftrack_projects_gui_schema.json similarity index 100% rename from pype/tools/config_setting/config_gui_schema/projects_schema/ftrack_projects_gui_schema.json rename to pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/ftrack_projects_gui_schema.json diff --git a/pype/tools/config_setting/config_gui_schema/projects_schema/plugins_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/plugins_gui_schema.json similarity index 100% rename from pype/tools/config_setting/config_gui_schema/projects_schema/plugins_gui_schema.json rename to pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/plugins_gui_schema.json diff --git a/pype/tools/config_setting/config_gui_schema/projects_schema/project_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/project_gui_schema.json similarity index 100% rename from pype/tools/config_setting/config_gui_schema/projects_schema/project_gui_schema.json rename to pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/project_gui_schema.json diff --git a/pype/tools/config_setting/config_gui_schema/projects_schema/test_project_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/test_project_schema.json similarity index 100% rename from pype/tools/config_setting/config_gui_schema/projects_schema/test_project_schema.json rename to pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/test_project_schema.json diff --git a/pype/tools/config_setting/config_gui_schema/studio_schema/applications_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/applications_gui_schema.json similarity index 100% rename from pype/tools/config_setting/config_gui_schema/studio_schema/applications_gui_schema.json rename to pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/applications_gui_schema.json diff --git a/pype/tools/config_setting/config_gui_schema/studio_schema/studio_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/studio_gui_schema.json similarity index 100% rename from pype/tools/config_setting/config_gui_schema/studio_schema/studio_gui_schema.json rename to pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/studio_gui_schema.json diff --git a/pype/tools/config_setting/config_gui_schema/studio_schema/tools_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/tools_gui_schema.json similarity index 100% rename from pype/tools/config_setting/config_gui_schema/studio_schema/tools_gui_schema.json rename to pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/tools_gui_schema.json diff --git a/pype/tools/config_setting/style/__init__.py b/pype/tools/config_setting/config_setting/style/__init__.py similarity index 100% rename from pype/tools/config_setting/style/__init__.py rename to pype/tools/config_setting/config_setting/style/__init__.py diff --git a/pype/tools/config_setting/style/pype_icon.png b/pype/tools/config_setting/config_setting/style/pype_icon.png similarity index 100% rename from pype/tools/config_setting/style/pype_icon.png rename to pype/tools/config_setting/config_setting/style/pype_icon.png diff --git a/pype/tools/config_setting/style/style.css b/pype/tools/config_setting/config_setting/style/style.css similarity index 100% rename from pype/tools/config_setting/style/style.css rename to pype/tools/config_setting/config_setting/style/style.css diff --git a/pype/tools/config_setting/config_setting/widgets/__init__.py b/pype/tools/config_setting/config_setting/widgets/__init__.py new file mode 100644 index 0000000000..0197917596 --- /dev/null +++ b/pype/tools/config_setting/config_setting/widgets/__init__.py @@ -0,0 +1,19 @@ +from .lib import ( + NOT_SET, + AS_WIDGET, + METADATA_KEY, + OVERRIDE_VERSION, + convert_gui_data_to_overrides, + convert_overrides_to_gui_data, + TypeToKlass +) + + +from .base import ( + PypeConfigurationWidget, + StudioWidget, + ProjectWidget +) +from .main import MainWidget + +from .inputs import * diff --git a/pype/tools/config_setting/widgets/base.py b/pype/tools/config_setting/config_setting/widgets/base.py similarity index 95% rename from pype/tools/config_setting/widgets/base.py rename to pype/tools/config_setting/config_setting/widgets/base.py index 0fc9c5cabf..dcbaf743a9 100644 --- a/pype/tools/config_setting/widgets/base.py +++ b/pype/tools/config_setting/config_setting/widgets/base.py @@ -1,16 +1,12 @@ import os import json from Qt import QtWidgets, QtCore, QtGui -from . import config +from pype.api import config from .widgets import UnsavedChangesDialog -from .lib import NOT_SET, METADATA_KEY, convert_gui_data_to_overrides +from . import lib from avalon import io -class TypeToKlass: - types = {} - - class PypeConfigurationWidget: default_state = "" @@ -23,7 +19,7 @@ class PypeConfigurationWidget: def value_from_values(self, values, keys=None): if not values: - return NOT_SET + return lib.NOT_SET if keys is None: keys = self.keys @@ -36,7 +32,7 @@ class PypeConfigurationWidget: ) if key not in value: - return NOT_SET + return lib.NOT_SET value = value[key] return value @@ -109,7 +105,7 @@ class StudioWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.input_fields.clear() values = {"studio": config.studio_presets()} - schema = config.gui_schema("studio_schema", "studio_gui_schema") + schema = lib.gui_schema("studio_schema", "studio_gui_schema") self.keys = schema.get("keys", []) self.add_children_gui(schema, values) self.schema = schema @@ -129,7 +125,7 @@ class StudioWidget(QtWidgets.QWidget, PypeConfigurationWidget): # Load studio data with metadata current_presets = config.studio_presets() - keys_to_file = config.file_keys_from_schema(self.schema) + keys_to_file = lib.file_keys_from_schema(self.schema) for key_sequence in keys_to_file: # Skip first key key_sequence = key_sequence[1:] @@ -158,7 +154,7 @@ class StudioWidget(QtWidgets.QWidget, PypeConfigurationWidget): def add_children_gui(self, child_configuration, values): item_type = child_configuration["type"] - klass = TypeToKlass.types.get(item_type) + klass = lib.TypeToKlass.types.get(item_type) item = klass( child_configuration, values, self.keys, self ) @@ -354,14 +350,14 @@ class ProjectWidget(QtWidgets.QWidget, PypeConfigurationWidget): def reset(self): values = config.global_project_presets() - schema = config.gui_schema("projects_schema", "project_gui_schema") + schema = lib.gui_schema("projects_schema", "project_gui_schema") self.keys = schema.get("keys", []) self.add_children_gui(schema, values) self.schema = schema def add_children_gui(self, child_configuration, values): item_type = child_configuration["type"] - klass = TypeToKlass.types.get(item_type) + klass = lib.TypeToKlass.types.get(item_type) item = klass( child_configuration, values, self.keys, self @@ -394,7 +390,7 @@ class ProjectWidget(QtWidgets.QWidget, PypeConfigurationWidget): _data = {} for item in self.input_fields: value, is_group = item.overrides() - if value is not NOT_SET: + if value is not lib.NOT_SET: _data.update(value) if is_group: raise Exception( @@ -402,7 +398,7 @@ class ProjectWidget(QtWidgets.QWidget, PypeConfigurationWidget): ) data = _data.get("project") or {} - output_data = convert_gui_data_to_overrides(data) + output_data = lib.convert_gui_data_to_overrides(data) overrides_json_path = config.path_to_project_overrides( self.project_name @@ -437,7 +433,7 @@ class ProjectWidget(QtWidgets.QWidget, PypeConfigurationWidget): # Load studio data with metadata current_presets = config.global_project_presets() - keys_to_file = config.file_keys_from_schema(self.schema) + keys_to_file = lib.file_keys_from_schema(self.schema) for key_sequence in keys_to_file: # Skip first key key_sequence = key_sequence[1:] diff --git a/pype/tools/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py similarity index 99% rename from pype/tools/config_setting/widgets/inputs.py rename to pype/tools/config_setting/config_setting/widgets/inputs.py index 1840572cfb..63415a16d3 100644 --- a/pype/tools/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1,14 +1,14 @@ import json from Qt import QtWidgets, QtCore, QtGui -from . import config -from .base import PypeConfigurationWidget, TypeToKlass +from pype.api import config +from .base import PypeConfigurationWidget from .widgets import ( ClickableWidget, ExpandingWidget, ModifiedIntSpinBox, ModifiedFloatSpinBox ) -from .lib import NOT_SET, AS_WIDGET, METADATA_KEY +from .lib import NOT_SET, AS_WIDGET, METADATA_KEY, TypeToKlass class SchemeGroupHierarchyBug(Exception): diff --git a/pype/tools/config_setting/config_setting/widgets/lib.py b/pype/tools/config_setting/config_setting/widgets/lib.py new file mode 100644 index 0000000000..454c0b07ed --- /dev/null +++ b/pype/tools/config_setting/config_setting/widgets/lib.py @@ -0,0 +1,182 @@ +import os +import json +import copy +from pype.api import config + +OVERRIDEN_KEY = config.OVERRIDEN_KEY + + +# Singleton database of available inputs +class TypeToKlass: + types = {} + + +NOT_SET = type("NOT_SET", (), {}) +AS_WIDGET = type("AS_WIDGET", (), {}) +METADATA_KEY = type("METADATA_KEY", (), {}) +OVERRIDE_VERSION = 1 + + +def convert_gui_data_to_overrides(data, first=True): + if not data or not isinstance(data, dict): + return data + + output = {} + if first: + output["__override_version__"] = OVERRIDE_VERSION + + if METADATA_KEY in data: + metadata = data.pop(METADATA_KEY) + for key, value in metadata.items(): + if key == "groups": + output[OVERRIDEN_KEY] = value + else: + KeyError("Unknown metadata key \"{}\"".format(key)) + + for key, value in data.items(): + output[key] = convert_gui_data_to_overrides(value, False) + return output + + +def convert_overrides_to_gui_data(data, first=True): + if not data or not isinstance(data, dict): + return data + + output = {} + if OVERRIDEN_KEY in data: + groups = data.pop(OVERRIDEN_KEY) + if METADATA_KEY not in output: + output[METADATA_KEY] = {} + output[METADATA_KEY]["groups"] = groups + + for key, value in data.items(): + output[key] = convert_overrides_to_gui_data(value, False) + + return output + + + +def replace_inner_schemas(schema_data, schema_collection): + if schema_data["type"] == "schema": + raise ValueError("First item in schema data can't be schema.") + + children = schema_data.get("children") + if not children: + return schema_data + + new_children = [] + for child in children: + if child["type"] != "schema": + new_child = replace_inner_schemas(child, schema_collection) + new_children.append(new_child) + continue + + for schema_name in child["children"]: + new_child = replace_inner_schemas( + schema_collection[schema_name], + schema_collection + ) + new_children.append(new_child) + + schema_data["children"] = new_children + return schema_data + + +class SchemaMissingFileInfo(Exception): + def __init__(self, invalid): + full_path_keys = [] + for item in invalid: + full_path_keys.append("\"{}\"".format("/".join(item))) + + msg = ( + "Schema has missing definition of output file (\"is_file\" key)" + " for keys. [{}]" + ).format(", ".join(full_path_keys)) + super(SchemaMissingFileInfo, self).__init__(msg) + + +def file_keys_from_schema(schema_data): + output = [] + keys = [] + key = schema_data.get("key") + if key: + keys.append(key) + + for child in schema_data["children"]: + if child.get("is_file"): + _keys = copy.deepcopy(keys) + _keys.append(child["key"]) + output.append(_keys) + continue + + for result in file_keys_from_schema(child): + _keys = copy.deepcopy(keys) + _keys.extend(result) + output.append(_keys) + return output + + +def validate_all_has_ending_file(schema_data, is_top=True): + if schema_data.get("is_file"): + return None + + children = schema_data.get("children") + if not children: + return [[schema_data["key"]]] + + invalid = [] + keyless = "key" not in schema_data + for child in children: + result = validate_all_has_ending_file(child, False) + if result is None: + continue + + if keyless: + invalid.extend(result) + else: + for item in result: + new_invalid = [schema_data["key"]] + new_invalid.extend(item) + invalid.append(new_invalid) + + if not invalid: + return None + + if not is_top: + return invalid + + raise SchemaMissingFileInfo(invalid) + + +def validate_schema(schema_data): + # TODO validator for key uniquenes + # TODO validator that is_group key is not before is_file child + # TODO validator that is_group or is_file is not on child without key + validate_all_has_ending_file(schema_data) + + +def gui_schema(subfolder, main_schema_name): + subfolder, main_schema_name + dirpath = os.path.join( + os.path.dirname(os.path.dirname(__file__)), + "config_gui_schema", + subfolder + ) + + loaded_schemas = {} + for filename in os.listdir(dirpath): + basename, ext = os.path.splitext(filename) + if ext != ".json": + continue + + filepath = os.path.join(dirpath, filename) + with open(filepath, "r") as json_stream: + schema_data = json.load(json_stream) + loaded_schemas[basename] = schema_data + + main_schema = replace_inner_schemas( + loaded_schemas[main_schema_name], + loaded_schemas + ) + validate_schema(main_schema) + return main_schema diff --git a/pype/tools/config_setting/widgets/main.py b/pype/tools/config_setting/config_setting/widgets/main.py similarity index 100% rename from pype/tools/config_setting/widgets/main.py rename to pype/tools/config_setting/config_setting/widgets/main.py diff --git a/pype/tools/config_setting/widgets/tests.py b/pype/tools/config_setting/config_setting/widgets/tests.py similarity index 100% rename from pype/tools/config_setting/widgets/tests.py rename to pype/tools/config_setting/config_setting/widgets/tests.py diff --git a/pype/tools/config_setting/widgets/widgets.py b/pype/tools/config_setting/config_setting/widgets/widgets.py similarity index 100% rename from pype/tools/config_setting/widgets/widgets.py rename to pype/tools/config_setting/config_setting/widgets/widgets.py diff --git a/pype/tools/config_setting/interface.py b/pype/tools/config_setting/interface.py deleted file mode 100644 index a8c05f5af3..0000000000 --- a/pype/tools/config_setting/interface.py +++ /dev/null @@ -1,56 +0,0 @@ -import os -import sys - - -def folder_up(path, times=1): - if times <= 0: - return path - return folder_up(os.path.dirname(path), times - 1) - - -PYPE_SETUP_PATH = folder_up(os.path.realpath(__file__), 6) -os.environ["PYPE_CONFIG"] = os.path.join( - PYPE_SETUP_PATH, "repos", "pype-config" -) -os.environ["AVALON_MONGO"] = "mongodb://localhost:2707" -sys_paths = ( - "C:/Users/Public/pype_env2/Lib/site-packages", - PYPE_SETUP_PATH, - os.path.join(PYPE_SETUP_PATH, "repos", "pype"), - os.path.join(PYPE_SETUP_PATH, "repos", "avalon-core"), - os.path.join(PYPE_SETUP_PATH, "repos", "pyblish-base") -) -for path in sys_paths: - sys.path.append(path) - -from widgets import main -import style -from Qt import QtWidgets, QtGui - - -class MyApp(QtWidgets.QApplication): - def __init__(self, *args, **kwargs): - super(MyApp, self).__init__(*args, **kwargs) - stylesheet = style.load_stylesheet() - self.setStyleSheet(stylesheet) - self.setWindowIcon(QtGui.QIcon(style.app_icon_path())) - - -if __name__ == "__main__": - app = MyApp(sys.argv) - - # main_widget = QtWidgets.QWidget() - # main_widget.setWindowIcon(QtGui.QIcon(style.app_icon_path())) - # - # layout = QtWidgets.QVBoxLayout(main_widget) - # - # widget = main.MainWidget(main_widget) - - # layout.addWidget(widget) - # main_widget.setLayout(layout) - # main_widget.show() - - widget = main.MainWidget() - widget.show() - - sys.exit(app.exec_()) diff --git a/pype/tools/config_setting/widgets/__init__.py b/pype/tools/config_setting/widgets/__init__.py deleted file mode 100644 index 9fbce6e1cf..0000000000 --- a/pype/tools/config_setting/widgets/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -from .lib import NOT_SET, AS_WIDGET, METADATA_KEY - - -from .base import * -from .main import * -from .inputs import * diff --git a/pype/tools/config_setting/widgets/config.py b/pype/tools/config_setting/widgets/config.py deleted file mode 100644 index 34eec69fc7..0000000000 --- a/pype/tools/config_setting/widgets/config.py +++ /dev/null @@ -1,325 +0,0 @@ -import os -import json -import logging -import copy - -# DEBUG SETUP -os.environ["PYPE_CONFIG"] = os.path.dirname(os.path.dirname(__file__)) -os.environ["PYPE_PROJECT_CONFIGS"] = os.path.join( - os.environ["PYPE_CONFIG"], "config", "project_overrides" -) - -log = logging.getLogger(__name__) - -STUDIO_PRESETS_PATH = os.path.normpath( - os.path.join(os.environ["PYPE_CONFIG"], "config", "studio_presets") -) -PROJECT_CONFIGURATION_DIR = "project_presets" -PROJECT_PRESETS_PATH = os.path.normpath(os.path.join( - os.environ["PYPE_CONFIG"], "config", PROJECT_CONFIGURATION_DIR -)) -first_run = False - -# TODO key popping not implemented yet -POP_KEY = "__pop_key__" -OVERRIDEN_KEY = "__overriden_keys__" - - -def load_json(fpath): - # Load json data - with open(fpath, "r") as opened_file: - lines = opened_file.read().splitlines() - - # prepare json string - standard_json = "" - for line in lines: - # Remove all whitespace on both sides - line = line.strip() - - # Skip blank lines - if len(line) == 0: - continue - - standard_json += line - - # Check if has extra commas - extra_comma = False - if ",]" in standard_json or ",}" in standard_json: - extra_comma = True - standard_json = standard_json.replace(",]", "]") - standard_json = standard_json.replace(",}", "}") - - global first_run - if extra_comma and first_run: - log.error("Extra comma in json file: \"{}\"".format(fpath)) - - # return empty dict if file is empty - if standard_json == "": - if first_run: - log.error("Empty json file: \"{}\"".format(fpath)) - return {} - - # Try to parse string - try: - return json.loads(standard_json) - - except json.decoder.JSONDecodeError: - # Return empty dict if it is first time that decode error happened - if not first_run: - return {} - - # Repreduce the exact same exception but traceback contains better - # information about position of error in the loaded json - try: - with open(fpath, "r") as opened_file: - json.load(opened_file) - - except json.decoder.JSONDecodeError: - log.warning( - "File has invalid json format \"{}\"".format(fpath), - exc_info=True - ) - - return {} - - -def subkey_merge(_dict, value, keys): - key = keys.pop(0) - if not keys: - _dict[key] = value - return _dict - - if key not in _dict: - _dict[key] = {} - _dict[key] = subkey_merge(_dict[key], value, keys) - - return _dict - - -def load_jsons_from_dir(path, *args, **kwargs): - output = {} - - path = os.path.normpath(path) - if not os.path.exists(path): - # TODO warning - return output - - sub_keys = list(kwargs.pop("subkeys", args)) - for sub_key in tuple(sub_keys): - _path = os.path.join(path, sub_key) - if not os.path.exists(_path): - break - - path = _path - sub_keys.pop(0) - - base_len = len(path) + 1 - for base, _directories, filenames in os.walk(path): - base_items_str = base[base_len:] - if not base_items_str: - base_items = [] - else: - base_items = base_items_str.split(os.path.sep) - - for filename in filenames: - basename, ext = os.path.splitext(filename) - if ext == ".json": - full_path = os.path.join(base, filename) - value = load_json(full_path) - dict_keys = base_items + [basename] - output = subkey_merge(output, value, dict_keys) - - for sub_key in sub_keys: - output = output[sub_key] - return output - - -def studio_presets(*args, **kwargs): - return load_jsons_from_dir(STUDIO_PRESETS_PATH, *args, **kwargs) - - -def global_project_presets(**kwargs): - return load_jsons_from_dir(PROJECT_PRESETS_PATH, **kwargs) - - -def path_to_project_overrides(project_name): - project_configs_path = os.environ["PYPE_PROJECT_CONFIGS"] - dirpath = os.path.join(project_configs_path, project_name) - return os.path.join(dirpath, PROJECT_CONFIGURATION_DIR + ".json") - - -def project_preset_overrides(project_name, **kwargs): - if not project_name: - return {} - - path_to_json = path_to_project_overrides(project_name) - if not os.path.exists(path_to_json): - return {} - return load_json(path_to_json) - - -def merge_overrides(global_dict, override_dict): - if OVERRIDEN_KEY in override_dict: - overriden_keys = set(override_dict.pop(OVERRIDEN_KEY)) - else: - overriden_keys = set() - - for key, value in override_dict.items(): - if value == POP_KEY: - global_dict.pop(key) - - elif ( - key in overriden_keys - or key not in global_dict - ): - global_dict[key] = value - - elif isinstance(value, dict) and isinstance(global_dict[key], dict): - global_dict[key] = merge_overrides(global_dict[key], value) - - else: - global_dict[key] = value - return global_dict - - -def apply_overrides(global_presets, project_overrides): - global_presets = copy.deepcopy(global_presets) - if not project_overrides: - return global_presets - return merge_overrides(global_presets, project_overrides) - - -def project_presets(project_name=None, **kwargs): - global_presets = global_project_presets(**kwargs) - - if not project_name: - project_name = os.environ.get("AVALON_PROJECT") - project_overrides = project_preset_overrides(project_name, **kwargs) - - return apply_overrides(global_presets, project_overrides) - - -def replace_inner_schemas(schema_data, schema_collection): - if schema_data["type"] == "schema": - raise ValueError("First item in schema data can't be schema.") - - children = schema_data.get("children") - if not children: - return schema_data - - new_children = [] - for child in children: - if child["type"] != "schema": - new_child = replace_inner_schemas(child, schema_collection) - new_children.append(new_child) - continue - - for schema_name in child["children"]: - new_child = replace_inner_schemas( - schema_collection[schema_name], - schema_collection - ) - new_children.append(new_child) - - schema_data["children"] = new_children - return schema_data - - -class SchemaMissingFileInfo(Exception): - def __init__(self, invalid): - full_path_keys = [] - for item in invalid: - full_path_keys.append("\"{}\"".format("/".join(item))) - - msg = ( - "Schema has missing definition of output file (\"is_file\" key)" - " for keys. [{}]" - ).format(", ".join(full_path_keys)) - super(SchemaMissingFileInfo, self).__init__(msg) - - -def file_keys_from_schema(schema_data): - output = [] - keys = [] - key = schema_data.get("key") - if key: - keys.append(key) - - for child in schema_data["children"]: - if child.get("is_file"): - _keys = copy.deepcopy(keys) - _keys.append(child["key"]) - output.append(_keys) - continue - - for result in file_keys_from_schema(child): - _keys = copy.deepcopy(keys) - _keys.extend(result) - output.append(_keys) - return output - - -def validate_all_has_ending_file(schema_data, is_top=True): - if schema_data.get("is_file"): - return None - - children = schema_data.get("children") - if not children: - return [[schema_data["key"]]] - - invalid = [] - keyless = "key" not in schema_data - for child in children: - result = validate_all_has_ending_file(child, False) - if result is None: - continue - - if keyless: - invalid.extend(result) - else: - for item in result: - new_invalid = [schema_data["key"]] - new_invalid.extend(item) - invalid.append(new_invalid) - - if not invalid: - return None - - if not is_top: - return invalid - - raise SchemaMissingFileInfo(invalid) - - -def validate_schema(schema_data): - # TODO validator for key uniquenes - # TODO validator that is_group key is not before is_file child - # TODO validator that is_group or is_file is not on child without key - validate_all_has_ending_file(schema_data) - - -def gui_schema(subfolder, main_schema_name): - subfolder, main_schema_name - dirpath = os.path.join( - os.path.dirname(os.path.dirname(__file__)), - "config_gui_schema", - subfolder - ) - - loaded_schemas = {} - for filename in os.listdir(dirpath): - basename, ext = os.path.splitext(filename) - if ext != ".json": - continue - - filepath = os.path.join(dirpath, filename) - with open(filepath, "r") as json_stream: - schema_data = json.load(json_stream) - loaded_schemas[basename] = schema_data - - main_schema = replace_inner_schemas( - loaded_schemas[main_schema_name], - loaded_schemas - ) - validate_schema(main_schema) - return main_schema diff --git a/pype/tools/config_setting/widgets/lib.py b/pype/tools/config_setting/widgets/lib.py deleted file mode 100644 index d733396d59..0000000000 --- a/pype/tools/config_setting/widgets/lib.py +++ /dev/null @@ -1,54 +0,0 @@ -from .config import OVERRIDEN_KEY - - -class CustomNone: - """Created object can be used as custom None (not equal to None).""" - def __bool__(self): - """Return False (like default None).""" - return False - - -NOT_SET = CustomNone() -AS_WIDGET = type("AS_WIDGET", (), {}) - -METADATA_KEY = type("METADATA_KEY", (), {}) - -OVERRIDE_VERSION = 1 - - -def convert_gui_data_to_overrides(data, first=True): - if not data or not isinstance(data, dict): - return data - - output = {} - if first: - output["__override_version__"] = OVERRIDE_VERSION - - if METADATA_KEY in data: - metadata = data.pop(METADATA_KEY) - for key, value in metadata.items(): - if key == "groups": - output[OVERRIDEN_KEY] = value - else: - KeyError("Unknown metadata key \"{}\"".format(key)) - - for key, value in data.items(): - output[key] = convert_gui_data_to_overrides(value, False) - return output - - -def convert_overrides_to_gui_data(data, first=True): - if not data or not isinstance(data, dict): - return data - - output = {} - if OVERRIDEN_KEY in data: - groups = data.pop(OVERRIDEN_KEY) - if METADATA_KEY not in output: - output[METADATA_KEY] = {} - output[METADATA_KEY]["groups"] = groups - - for key, value in data.items(): - output[key] = convert_overrides_to_gui_data(value, False) - - return output From 428382e070a66758104431527e1c24e0c3fd4ae3 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 21 Aug 2020 17:38:22 +0200 Subject: [PATCH 148/813] config temporarily moved to ~/pype/pype directory --- pype/api.py | 2 +- pype/config.py | 193 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 194 insertions(+), 1 deletion(-) create mode 100644 pype/config.py diff --git a/pype/api.py b/pype/api.py index 44a31f2626..e2705f81ea 100644 --- a/pype/api.py +++ b/pype/api.py @@ -1,8 +1,8 @@ +from . import config from pypeapp import ( Logger, Anatomy, project_overrides_dir_path, - config, execute ) diff --git a/pype/config.py b/pype/config.py new file mode 100644 index 0000000000..b3e860d72f --- /dev/null +++ b/pype/config.py @@ -0,0 +1,193 @@ +import os +import json +import logging +import copy + +log = logging.getLogger(__name__) + +STUDIO_PRESETS_PATH = os.path.normpath( + os.path.join(os.environ["PYPE_CONFIG"], "config", "studio_presets") +) +PROJECT_CONFIGURATION_DIR = "project_presets" +PROJECT_PRESETS_PATH = os.path.normpath(os.path.join( + os.environ["PYPE_CONFIG"], "config", PROJECT_CONFIGURATION_DIR +)) +first_run = False + +# TODO key popping not implemented yet +POP_KEY = "__pop_key__" +OVERRIDEN_KEY = "__overriden_keys__" + + +def load_json(fpath): + # Load json data + with open(fpath, "r") as opened_file: + lines = opened_file.read().splitlines() + + # prepare json string + standard_json = "" + for line in lines: + # Remove all whitespace on both sides + line = line.strip() + + # Skip blank lines + if len(line) == 0: + continue + + standard_json += line + + # Check if has extra commas + extra_comma = False + if ",]" in standard_json or ",}" in standard_json: + extra_comma = True + standard_json = standard_json.replace(",]", "]") + standard_json = standard_json.replace(",}", "}") + + global first_run + if extra_comma and first_run: + log.error("Extra comma in json file: \"{}\"".format(fpath)) + + # return empty dict if file is empty + if standard_json == "": + if first_run: + log.error("Empty json file: \"{}\"".format(fpath)) + return {} + + # Try to parse string + try: + return json.loads(standard_json) + + except json.decoder.JSONDecodeError: + # Return empty dict if it is first time that decode error happened + if not first_run: + return {} + + # Repreduce the exact same exception but traceback contains better + # information about position of error in the loaded json + try: + with open(fpath, "r") as opened_file: + json.load(opened_file) + + except json.decoder.JSONDecodeError: + log.warning( + "File has invalid json format \"{}\"".format(fpath), + exc_info=True + ) + + return {} + + +def subkey_merge(_dict, value, keys): + key = keys.pop(0) + if not keys: + _dict[key] = value + return _dict + + if key not in _dict: + _dict[key] = {} + _dict[key] = subkey_merge(_dict[key], value, keys) + + return _dict + + +def load_jsons_from_dir(path, *args, **kwargs): + output = {} + + path = os.path.normpath(path) + if not os.path.exists(path): + # TODO warning + return output + + sub_keys = list(kwargs.pop("subkeys", args)) + for sub_key in tuple(sub_keys): + _path = os.path.join(path, sub_key) + if not os.path.exists(_path): + break + + path = _path + sub_keys.pop(0) + + base_len = len(path) + 1 + for base, _directories, filenames in os.walk(path): + base_items_str = base[base_len:] + if not base_items_str: + base_items = [] + else: + base_items = base_items_str.split(os.path.sep) + + for filename in filenames: + basename, ext = os.path.splitext(filename) + if ext == ".json": + full_path = os.path.join(base, filename) + value = load_json(full_path) + dict_keys = base_items + [basename] + output = subkey_merge(output, value, dict_keys) + + for sub_key in sub_keys: + output = output[sub_key] + return output + + +def studio_presets(*args, **kwargs): + return load_jsons_from_dir(STUDIO_PRESETS_PATH, *args, **kwargs) + + +def global_project_presets(**kwargs): + return load_jsons_from_dir(PROJECT_PRESETS_PATH, **kwargs) + + +def path_to_project_overrides(project_name): + project_configs_path = os.environ["PYPE_PROJECT_CONFIGS"] + dirpath = os.path.join(project_configs_path, project_name) + return os.path.join(dirpath, PROJECT_CONFIGURATION_DIR + ".json") + + +def project_preset_overrides(project_name, **kwargs): + if not project_name: + return {} + + path_to_json = path_to_project_overrides(project_name) + if not os.path.exists(path_to_json): + return {} + return load_json(path_to_json) + + +def merge_overrides(global_dict, override_dict): + if OVERRIDEN_KEY in override_dict: + overriden_keys = set(override_dict.pop(OVERRIDEN_KEY)) + else: + overriden_keys = set() + + for key, value in override_dict.items(): + if value == POP_KEY: + global_dict.pop(key) + + elif ( + key in overriden_keys + or key not in global_dict + ): + global_dict[key] = value + + elif isinstance(value, dict) and isinstance(global_dict[key], dict): + global_dict[key] = merge_overrides(global_dict[key], value) + + else: + global_dict[key] = value + return global_dict + + +def apply_overrides(global_presets, project_overrides): + global_presets = copy.deepcopy(global_presets) + if not project_overrides: + return global_presets + return merge_overrides(global_presets, project_overrides) + + +def project_presets(project_name=None, **kwargs): + global_presets = global_project_presets(**kwargs) + + if not project_name: + project_name = os.environ.get("AVALON_PROJECT") + project_overrides = project_preset_overrides(project_name, **kwargs) + + return apply_overrides(global_presets, project_overrides) From 8f84f78d5d57d5e80014b888219c5761edd144de Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 21 Aug 2020 17:47:26 +0200 Subject: [PATCH 149/813] modified imports --- pype/tools/config_setting/__main__.py | 1 - .../config_setting/config_setting/__init__.py | 2 -- .../config_setting/widgets/__init__.py | 25 ++++++------------- .../widgets/{main.py => window.py} | 0 4 files changed, 7 insertions(+), 21 deletions(-) rename pype/tools/config_setting/config_setting/widgets/{main.py => window.py} (100%) diff --git a/pype/tools/config_setting/__main__.py b/pype/tools/config_setting/__main__.py index 171b85a775..aa6f707443 100644 --- a/pype/tools/config_setting/__main__.py +++ b/pype/tools/config_setting/__main__.py @@ -1,4 +1,3 @@ -import os import sys import config_setting diff --git a/pype/tools/config_setting/config_setting/__init__.py b/pype/tools/config_setting/config_setting/__init__.py index 835754e6a1..0c2fd6d4bb 100644 --- a/pype/tools/config_setting/config_setting/__init__.py +++ b/pype/tools/config_setting/config_setting/__init__.py @@ -1,10 +1,8 @@ from . import style -# from . import widgets from .widgets import MainWidget __all__ = ( "style", - # "widgets", "MainWidget" ) diff --git a/pype/tools/config_setting/config_setting/widgets/__init__.py b/pype/tools/config_setting/config_setting/widgets/__init__.py index 0197917596..0682f00324 100644 --- a/pype/tools/config_setting/config_setting/widgets/__init__.py +++ b/pype/tools/config_setting/config_setting/widgets/__init__.py @@ -1,19 +1,8 @@ -from .lib import ( - NOT_SET, - AS_WIDGET, - METADATA_KEY, - OVERRIDE_VERSION, - convert_gui_data_to_overrides, - convert_overrides_to_gui_data, - TypeToKlass -) +from .window import MainWidget +# TODO properly register inputs to TypeToKlass class +from . import inputs - -from .base import ( - PypeConfigurationWidget, - StudioWidget, - ProjectWidget -) -from .main import MainWidget - -from .inputs import * +__all__ = [ + "MainWidget", + "inputs" +] diff --git a/pype/tools/config_setting/config_setting/widgets/main.py b/pype/tools/config_setting/config_setting/widgets/window.py similarity index 100% rename from pype/tools/config_setting/config_setting/widgets/main.py rename to pype/tools/config_setting/config_setting/widgets/window.py From 1a5389dc5e16ea5968d8d6d9991e1bee5e0f2099 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 21 Aug 2020 18:44:28 +0200 Subject: [PATCH 150/813] cleaned attribute definitions --- .../config_setting/widgets/base.py | 50 +--- .../config_setting/widgets/inputs.py | 264 +++++------------- .../config_setting/widgets/lib.py | 1 - 3 files changed, 80 insertions(+), 235 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/base.py b/pype/tools/config_setting/config_setting/widgets/base.py index dcbaf743a9..af979c0f9f 100644 --- a/pype/tools/config_setting/config_setting/widgets/base.py +++ b/pype/tools/config_setting/config_setting/widgets/base.py @@ -7,50 +7,7 @@ from . import lib from avalon import io -class PypeConfigurationWidget: - default_state = "" - - def config_value(self): - raise NotImplementedError( - "Method `config_value` is not implemented for `{}`.".format( - self.__class__.__name__ - ) - ) - - def value_from_values(self, values, keys=None): - if not values: - return lib.NOT_SET - - if keys is None: - keys = self.keys - - value = values - for key in keys: - if not isinstance(value, dict): - raise TypeError( - "Expected dictionary got {}.".format(str(type(value))) - ) - - if key not in value: - return lib.NOT_SET - value = value[key] - return value - - def style_state(self, is_overriden, is_modified): - items = [] - if is_overriden: - items.append("overriden") - if is_modified: - items.append("modified") - return "-".join(items) or self.default_state - - def add_children_gui(self, child_configuration, values): - raise NotImplementedError(( - "Method `add_children_gui` is not implemented for `{}`." - ).format(self.__class__.__name__)) - - -class StudioWidget(QtWidgets.QWidget, PypeConfigurationWidget): +class StudioWidget(QtWidgets.QWidget): is_overidable = False is_overriden = False is_group = False @@ -149,6 +106,7 @@ class StudioWidget(QtWidgets.QWidget, PypeConfigurationWidget): if not os.path.exists(dirpath): os.makedirs(dirpath) + print("Saving data to: ", output_path) with open(output_path, "w") as file_stream: json.dump(origin_values, file_stream, indent=4) @@ -286,7 +244,7 @@ class ProjectListWidget(QtWidgets.QWidget): ) -class ProjectWidget(QtWidgets.QWidget, PypeConfigurationWidget): +class ProjectWidget(QtWidgets.QWidget): is_overriden = False is_group = False any_parent_is_group = False @@ -407,6 +365,7 @@ class ProjectWidget(QtWidgets.QWidget, PypeConfigurationWidget): if not os.path.exists(dirpath): os.makedirs(dirpath) + print("Saving data to: ", overrides_json_path) with open(overrides_json_path, "w") as file_stream: json.dump(output_data, file_stream, indent=4) @@ -457,5 +416,6 @@ class ProjectWidget(QtWidgets.QWidget, PypeConfigurationWidget): if not os.path.exists(dirpath): os.makedirs(dirpath) + print("Saving data to: ", output_path) with open(output_path, "w") as file_stream: json.dump(origin_values, file_stream, indent=4) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 63415a16d3..4e49406e26 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1,7 +1,6 @@ import json from Qt import QtWidgets, QtCore, QtGui from pype.api import config -from .base import PypeConfigurationWidget from .widgets import ( ClickableWidget, ExpandingWidget, @@ -19,14 +18,73 @@ class SchemeGroupHierarchyBug(Exception): super(SchemeGroupHierarchyBug, self).__init__(msg) -class InputWidget: +class ConfigWidget: + default_state = "" + + @property + def is_overidable(self): + return self._parent.is_overidable + + @property + def ignore_value_changes(self): + return self._parent.ignore_value_changes + + def config_value(self): + raise NotImplementedError( + "Method `config_value` is not implemented for `{}`.".format( + self.__class__.__name__ + ) + ) + + def value_from_values(self, values, keys=None): + if not values: + return NOT_SET + + if keys is None: + keys = self.keys + + value = values + for key in keys: + if not isinstance(value, dict): + raise TypeError( + "Expected dictionary got {}.".format(str(type(value))) + ) + + if key not in value: + return NOT_SET + value = value[key] + return value + + def style_state(self, is_overriden, is_modified): + items = [] + if is_overriden: + items.append("overriden") + if is_modified: + items.append("modified") + return "-".join(items) or self.default_state + + def add_children_gui(self, child_configuration, values): + raise NotImplementedError(( + "Method `add_children_gui` is not implemented for `{}`." + ).format(self.__class__.__name__)) + + +class InputWidget(ConfigWidget): def overrides(self): if not self.is_overriden: return NOT_SET, False return self.config_value(), self.is_group + @property + def child_modified(self): + return self.is_modified -class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget, InputWidget): + @property + def child_overriden(self): + return self._is_overriden + + +class BooleanWidget(QtWidgets.QWidget, InputWidget): value_changed = QtCore.Signal(object) def __init__( @@ -120,30 +178,14 @@ class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget, InputWidget): self.set_value(value) self.update_style() - @property - def child_modified(self): - return self.is_modified - - @property - def child_overriden(self): - return self._is_overriden - @property def is_modified(self): return self._is_modified or (self._was_overriden != self.is_overriden) - @property - def is_overidable(self): - return self._parent.is_overidable - @property def is_overriden(self): return self._is_overriden or self._parent.is_overriden - @property - def ignore_value_changes(self): - return self._parent.ignore_value_changes - def _on_value_change(self, item=None): if self.ignore_value_changes: return @@ -185,7 +227,7 @@ class BooleanWidget(QtWidgets.QWidget, PypeConfigurationWidget, InputWidget): return {self.key: self.item_value()} -class IntegerWidget(QtWidgets.QWidget, PypeConfigurationWidget, InputWidget): +class IntegerWidget(QtWidgets.QWidget, InputWidget): value_changed = QtCore.Signal(object) def __init__( @@ -243,30 +285,14 @@ class IntegerWidget(QtWidgets.QWidget, PypeConfigurationWidget, InputWidget): self.int_input.valueChanged.connect(self._on_value_change) - @property - def child_modified(self): - return self.is_modified - - @property - def child_overriden(self): - return self._is_overriden - @property def is_modified(self): return self._is_modified or (self._was_overriden != self.is_overriden) - @property - def is_overidable(self): - return self._parent.is_overidable - @property def is_overriden(self): return self._is_overriden or self._parent.is_overriden - @property - def ignore_value_changes(self): - return self._parent.ignore_value_changes - def set_value(self, value, *, default_value=False): self.int_input.setValue(value) if default_value: @@ -329,7 +355,7 @@ class IntegerWidget(QtWidgets.QWidget, PypeConfigurationWidget, InputWidget): return {self.key: self.item_value()} -class FloatWidget(QtWidgets.QWidget, PypeConfigurationWidget, InputWidget): +class FloatWidget(QtWidgets.QWidget, InputWidget): value_changed = QtCore.Signal(object) def __init__( @@ -397,30 +423,14 @@ class FloatWidget(QtWidgets.QWidget, PypeConfigurationWidget, InputWidget): self.float_input.valueChanged.connect(self._on_value_change) - @property - def child_modified(self): - return self.is_modified - - @property - def child_overriden(self): - return self._is_overriden - @property def is_modified(self): return self._is_modified or (self._was_overriden != self.is_overriden) - @property - def is_overidable(self): - return self._parent.is_overidable - @property def is_overriden(self): return self._is_overriden or self._parent.is_overriden - @property - def ignore_value_changes(self): - return self._parent.ignore_value_changes - def set_value(self, value, *, default_value=False): self.float_input.setValue(value) if default_value: @@ -481,7 +491,7 @@ class FloatWidget(QtWidgets.QWidget, PypeConfigurationWidget, InputWidget): return {self.key: self.item_value()} -class TextSingleLineWidget(QtWidgets.QWidget, PypeConfigurationWidget, InputWidget): +class TextSingleLineWidget(QtWidgets.QWidget, InputWidget): value_changed = QtCore.Signal(object) def __init__( @@ -539,30 +549,14 @@ class TextSingleLineWidget(QtWidgets.QWidget, PypeConfigurationWidget, InputWidg self.text_input.textChanged.connect(self._on_value_change) - @property - def child_modified(self): - return self.is_modified - - @property - def child_overriden(self): - return self._is_overriden - @property def is_modified(self): return self._is_modified or (self._was_overriden != self.is_overriden) - @property - def is_overidable(self): - return self._parent.is_overidable - @property def is_overriden(self): return self._is_overriden or self._parent.is_overriden - @property - def ignore_value_changes(self): - return self._parent.ignore_value_changes - def set_value(self, value, *, default_value=False): self.text_input.setText(value) if default_value: @@ -625,7 +619,7 @@ class TextSingleLineWidget(QtWidgets.QWidget, PypeConfigurationWidget, InputWidg return {self.key: self.item_value()} -class TextMultiLineWidget(QtWidgets.QWidget, PypeConfigurationWidget, InputWidget): +class TextMultiLineWidget(QtWidgets.QWidget, InputWidget): value_changed = QtCore.Signal(object) def __init__( @@ -681,30 +675,14 @@ class TextMultiLineWidget(QtWidgets.QWidget, PypeConfigurationWidget, InputWidge self.text_input.textChanged.connect(self._on_value_change) - @property - def child_modified(self): - return self.is_modified - - @property - def child_overriden(self): - return self._is_overriden - @property def is_modified(self): return self._is_modified or (self._was_overriden != self.is_overriden) - @property - def is_overidable(self): - return self._parent.is_overidable - @property def is_overriden(self): return self._is_overriden or self._parent.is_overriden - @property - def ignore_value_changes(self): - return self._parent.ignore_value_changes - def set_value(self, value, *, default_value=False): self.text_input.setPlainText(value) if default_value: @@ -822,7 +800,7 @@ class RawJsonInput(QtWidgets.QPlainTextEdit): self.update_style(is_valid) -class RawJsonWidget(QtWidgets.QWidget, PypeConfigurationWidget, InputWidget): +class RawJsonWidget(QtWidgets.QWidget, InputWidget): value_changed = QtCore.Signal(object) def __init__( @@ -879,30 +857,14 @@ class RawJsonWidget(QtWidgets.QWidget, PypeConfigurationWidget, InputWidget): self.text_input.textChanged.connect(self._on_value_change) - @property - def child_modified(self): - return self.is_modified - - @property - def child_overriden(self): - return self._is_overriden - @property def is_modified(self): return self._is_modified or (self._was_overriden != self.is_overriden) - @property - def is_overidable(self): - return self._parent.is_overidable - @property def is_overriden(self): return self._is_overriden or self._parent.is_overriden - @property - def ignore_value_changes(self): - return self._parent.ignore_value_changes - def set_value(self, value, *, default_value=False): self.text_input.setPlainText(value) if default_value: @@ -963,7 +925,7 @@ class RawJsonWidget(QtWidgets.QWidget, PypeConfigurationWidget, InputWidget): return {self.key: self.item_value()} -class TextListItem(QtWidgets.QWidget, PypeConfigurationWidget): +class TextListItem(QtWidgets.QWidget, ConfigWidget): _btn_size = 20 value_changed = QtCore.Signal(object) @@ -1013,7 +975,7 @@ class TextListItem(QtWidgets.QWidget, PypeConfigurationWidget): return self.text_input.text() -class TextListSubWidget(QtWidgets.QWidget, PypeConfigurationWidget): +class TextListSubWidget(QtWidgets.QWidget, ConfigWidget): value_changed = QtCore.Signal(object) def __init__(self, input_data, values, parent_keys, parent): @@ -1123,7 +1085,7 @@ class TextListSubWidget(QtWidgets.QWidget, PypeConfigurationWidget): return {self.key: self.item_value()} -class TextListWidget(QtWidgets.QWidget, PypeConfigurationWidget, InputWidget): +class TextListWidget(QtWidgets.QWidget, InputWidget): value_changed = QtCore.Signal(object) def __init__( @@ -1180,30 +1142,14 @@ class TextListWidget(QtWidgets.QWidget, PypeConfigurationWidget, InputWidget): self.default_value = self.item_value() self.override_value = None - @property - def child_modified(self): - return self.is_modified - - @property - def child_overriden(self): - return self._is_overriden - @property def is_modified(self): return self._is_modified or (self._was_overriden != self.is_overriden) - @property - def is_overidable(self): - return self._parent.is_overidable - @property def is_overriden(self): return self._is_overriden or self._parent.is_overriden - @property - def ignore_value_changes(self): - return self._parent.ignore_value_changes - def _on_value_change(self, item=None): if self.ignore_value_changes: return @@ -1256,7 +1202,7 @@ class TextListWidget(QtWidgets.QWidget, PypeConfigurationWidget, InputWidget): return {self.key: self.item_value()} -class ModifiableDictItem(QtWidgets.QWidget, PypeConfigurationWidget): +class ModifiableDictItem(QtWidgets.QWidget, ConfigWidget): _btn_size = 20 value_changed = QtCore.Signal(object) @@ -1323,18 +1269,10 @@ class ModifiableDictItem(QtWidgets.QWidget, PypeConfigurationWidget): def any_parent_is_group(self): return self._parent.any_parent_is_group - @property - def is_overidable(self): - return self._parent.is_overidable - @property def is_overriden(self): return self._parent.is_overriden - @property - def ignore_value_changes(self): - return self._parent.ignore_value_changes - def is_key_modified(self): return self._key() != self.default_key @@ -1375,7 +1313,7 @@ class ModifiableDictItem(QtWidgets.QWidget, PypeConfigurationWidget): return {key: value} -class ModifiableDictSubWidget(QtWidgets.QWidget, PypeConfigurationWidget): +class ModifiableDictSubWidget(QtWidgets.QWidget, ConfigWidget): value_changed = QtCore.Signal(object) def __init__(self, input_data, values, parent_keys, parent): @@ -1408,10 +1346,6 @@ class ModifiableDictSubWidget(QtWidgets.QWidget, PypeConfigurationWidget): self.default_value = self.config_value() self.override_value = None - @property - def is_overidable(self): - return self._parent.is_overidable - @property def is_overriden(self): return self._parent.is_overriden @@ -1420,10 +1354,6 @@ class ModifiableDictSubWidget(QtWidgets.QWidget, PypeConfigurationWidget): def is_group(self): return self._parent.is_group - @property - def ignore_value_changes(self): - return self._parent.ignore_value_changes - @property def any_parent_is_group(self): return self._parent.any_parent_is_group @@ -1492,7 +1422,7 @@ class ModifiableDictSubWidget(QtWidgets.QWidget, PypeConfigurationWidget): return output -class ModifiableDict(ExpandingWidget, PypeConfigurationWidget, InputWidget): +class ModifiableDict(ExpandingWidget, InputWidget): # Should be used only for dictionary with one datatype as value # TODO this is actually input field (do not care if is group or not) value_changed = QtCore.Signal(object) @@ -1554,34 +1484,14 @@ class ModifiableDict(ExpandingWidget, PypeConfigurationWidget, InputWidget): self.update_style() - @property - def child_modified(self): - return self.is_modified - @property def is_modified(self): return self._is_modified - @property - def child_overriden(self): - return self._is_overriden - - @property - def is_overidable(self): - return self._parent.is_overidable - @property def is_overriden(self): return self._is_overriden or self._parent.is_overriden - @property - def is_modified(self): - return self._is_modified - - @property - def ignore_value_changes(self): - return self._parent.ignore_value_changes - def apply_overrides(self, override_value): self._state = None self._is_modified = False @@ -1623,7 +1533,7 @@ class ModifiableDict(ExpandingWidget, PypeConfigurationWidget, InputWidget): return {self.key: self.item_value()} -class DictExpandWidget(QtWidgets.QWidget, PypeConfigurationWidget): +class DictExpandWidget(QtWidgets.QWidget, ConfigWidget): value_changed = QtCore.Signal(object) def __init__( @@ -1734,10 +1644,6 @@ class DictExpandWidget(QtWidgets.QWidget, PypeConfigurationWidget): def is_overriden(self): return self._is_overriden or self._parent.is_overriden - @property - def ignore_value_changes(self): - return self._parent.ignore_value_changes - def apply_overrides(self, override_value): # Make sure this is set to False self._is_overriden = False @@ -1830,10 +1736,6 @@ class DictExpandWidget(QtWidgets.QWidget, PypeConfigurationWidget): def config_value(self): return {self.key: self.item_value()} - @property - def is_overidable(self): - return self._parent.is_overidable - def add_children_gui(self, child_configuration, values): item_type = child_configuration["type"] klass = TypeToKlass.types.get(item_type) @@ -1864,7 +1766,7 @@ class DictExpandWidget(QtWidgets.QWidget, PypeConfigurationWidget): return {self.key: values}, self.is_group -class DictInvisible(QtWidgets.QWidget, PypeConfigurationWidget): +class DictInvisible(QtWidgets.QWidget, ConfigWidget): # TODO is not overridable by itself value_changed = QtCore.Signal(object) @@ -1915,10 +1817,6 @@ class DictInvisible(QtWidgets.QWidget, PypeConfigurationWidget): def is_overriden(self): return self._is_overriden or self._parent.is_overriden - @property - def is_overidable(self): - return self._parent.is_overidable - @property def child_modified(self): for input_field in self.input_fields: @@ -1933,10 +1831,6 @@ class DictInvisible(QtWidgets.QWidget, PypeConfigurationWidget): return True return False - @property - def ignore_value_changes(self): - return self._parent.ignore_value_changes - def item_value(self): output = {} for input_field in self.input_fields: @@ -2019,7 +1913,7 @@ class DictInvisible(QtWidgets.QWidget, PypeConfigurationWidget): return {self.key: values}, self.is_group -class DictFormWidget(QtWidgets.QWidget): +class DictFormWidget(QtWidgets.QWidget, ConfigWidget): value_changed = QtCore.Signal(object) def __init__( @@ -2070,14 +1964,6 @@ class DictFormWidget(QtWidgets.QWidget): return True return False - @property - def is_overidable(self): - return self._parent.is_overidable - - @property - def ignore_value_changes(self): - return self._parent.ignore_value_changes - def add_children_gui(self, child_configuration, values): item_type = child_configuration["type"] key = child_configuration["key"] diff --git a/pype/tools/config_setting/config_setting/widgets/lib.py b/pype/tools/config_setting/config_setting/widgets/lib.py index 454c0b07ed..c6379b4816 100644 --- a/pype/tools/config_setting/config_setting/widgets/lib.py +++ b/pype/tools/config_setting/config_setting/widgets/lib.py @@ -55,7 +55,6 @@ def convert_overrides_to_gui_data(data, first=True): return output - def replace_inner_schemas(schema_data, schema_collection): if schema_data["type"] == "schema": raise ValueError("First item in schema data can't be schema.") From 8f4cb392f8ba967491bcfb9febd19ea1510219db Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 21 Aug 2020 18:56:12 +0200 Subject: [PATCH 151/813] _is_overriden is global attribute --- .../config_setting/widgets/inputs.py | 70 ++----------------- 1 file changed, 7 insertions(+), 63 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 4e49406e26..8f6908eb3a 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -20,6 +20,11 @@ class SchemeGroupHierarchyBug(Exception): class ConfigWidget: default_state = "" + _is_overriden = False + + @property + def is_overriden(self): + return self._is_overriden or self._parent.is_overriden @property def is_overidable(self): @@ -107,7 +112,6 @@ class BooleanWidget(QtWidgets.QWidget, InputWidget): self.is_group = is_group self._is_modified = False self._was_overriden = False - self._is_overriden = False self._state = None @@ -182,10 +186,6 @@ class BooleanWidget(QtWidgets.QWidget, InputWidget): def is_modified(self): return self._is_modified or (self._was_overriden != self.is_overriden) - @property - def is_overriden(self): - return self._is_overriden or self._parent.is_overriden - def _on_value_change(self, item=None): if self.ignore_value_changes: return @@ -250,7 +250,6 @@ class IntegerWidget(QtWidgets.QWidget, InputWidget): self.is_group = is_group self._is_modified = False self._was_overriden = False - self._is_overriden = False self._state = None @@ -289,10 +288,6 @@ class IntegerWidget(QtWidgets.QWidget, InputWidget): def is_modified(self): return self._is_modified or (self._was_overriden != self.is_overriden) - @property - def is_overriden(self): - return self._is_overriden or self._parent.is_overriden - def set_value(self, value, *, default_value=False): self.int_input.setValue(value) if default_value: @@ -378,7 +373,6 @@ class FloatWidget(QtWidgets.QWidget, InputWidget): self.is_group = is_group self._is_modified = False self._was_overriden = False - self._is_overriden = False self._state = None @@ -427,10 +421,6 @@ class FloatWidget(QtWidgets.QWidget, InputWidget): def is_modified(self): return self._is_modified or (self._was_overriden != self.is_overriden) - @property - def is_overriden(self): - return self._is_overriden or self._parent.is_overriden - def set_value(self, value, *, default_value=False): self.float_input.setValue(value) if default_value: @@ -514,7 +504,6 @@ class TextSingleLineWidget(QtWidgets.QWidget, InputWidget): self.is_group = is_group self._is_modified = False self._was_overriden = False - self._is_overriden = False self._state = None @@ -553,10 +542,6 @@ class TextSingleLineWidget(QtWidgets.QWidget, InputWidget): def is_modified(self): return self._is_modified or (self._was_overriden != self.is_overriden) - @property - def is_overriden(self): - return self._is_overriden or self._parent.is_overriden - def set_value(self, value, *, default_value=False): self.text_input.setText(value) if default_value: @@ -642,7 +627,6 @@ class TextMultiLineWidget(QtWidgets.QWidget, InputWidget): self.is_group = is_group self._is_modified = False self._was_overriden = False - self._is_overriden = False self._state = None @@ -679,10 +663,6 @@ class TextMultiLineWidget(QtWidgets.QWidget, InputWidget): def is_modified(self): return self._is_modified or (self._was_overriden != self.is_overriden) - @property - def is_overriden(self): - return self._is_overriden or self._parent.is_overriden - def set_value(self, value, *, default_value=False): self.text_input.setPlainText(value) if default_value: @@ -823,7 +803,6 @@ class RawJsonWidget(QtWidgets.QWidget, InputWidget): self.is_group = is_group self._is_modified = False self._was_overriden = False - self._is_overriden = False self._state = None @@ -861,10 +840,6 @@ class RawJsonWidget(QtWidgets.QWidget, InputWidget): def is_modified(self): return self._is_modified or (self._was_overriden != self.is_overriden) - @property - def is_overriden(self): - return self._is_overriden or self._parent.is_overriden - def set_value(self, value, *, default_value=False): self.text_input.setPlainText(value) if default_value: @@ -1107,7 +1082,6 @@ class TextListWidget(QtWidgets.QWidget, InputWidget): self._is_modified = False self.is_group = is_group self._was_overriden = False - self._is_overriden = False self._state = None @@ -1146,10 +1120,6 @@ class TextListWidget(QtWidgets.QWidget, InputWidget): def is_modified(self): return self._is_modified or (self._was_overriden != self.is_overriden) - @property - def is_overriden(self): - return self._is_overriden or self._parent.is_overriden - def _on_value_change(self, item=None): if self.ignore_value_changes: return @@ -1269,10 +1239,6 @@ class ModifiableDictItem(QtWidgets.QWidget, ConfigWidget): def any_parent_is_group(self): return self._parent.any_parent_is_group - @property - def is_overriden(self): - return self._parent.is_overriden - def is_key_modified(self): return self._key() != self.default_key @@ -1346,10 +1312,6 @@ class ModifiableDictSubWidget(QtWidgets.QWidget, ConfigWidget): self.default_value = self.config_value() self.override_value = None - @property - def is_overriden(self): - return self._parent.is_overriden - @property def is_group(self): return self._parent.is_group @@ -1448,7 +1410,6 @@ class ModifiableDict(ExpandingWidget, InputWidget): self.is_group = is_group self._is_modified = False - self._is_overriden = False self._was_overriden = False self._state = None @@ -1488,10 +1449,6 @@ class ModifiableDict(ExpandingWidget, InputWidget): def is_modified(self): return self._is_modified - @property - def is_overriden(self): - return self._is_overriden or self._parent.is_overriden - def apply_overrides(self, override_value): self._state = None self._is_modified = False @@ -1533,6 +1490,7 @@ class ModifiableDict(ExpandingWidget, InputWidget): return {self.key: self.item_value()} +# Dictionaries class DictExpandWidget(QtWidgets.QWidget, ConfigWidget): value_changed = QtCore.Signal(object) @@ -1556,7 +1514,6 @@ class DictExpandWidget(QtWidgets.QWidget, ConfigWidget): self.any_parent_is_group = any_parent_is_group self._is_modified = False - self._is_overriden = False self.is_group = is_group self._state = None @@ -1640,10 +1597,6 @@ class DictExpandWidget(QtWidgets.QWidget, ConfigWidget): super(DictExpandWidget, self).resizeEvent(event) self.content_widget.updateGeometry() - @property - def is_overriden(self): - return self._is_overriden or self._parent.is_overriden - def apply_overrides(self, override_value): # Make sure this is set to False self._is_overriden = False @@ -1785,7 +1738,6 @@ class DictInvisible(QtWidgets.QWidget, ConfigWidget): self.any_parent_is_group = any_parent_is_group - self._is_overriden = False self.is_modified = False self.is_group = is_group @@ -1813,10 +1765,6 @@ class DictInvisible(QtWidgets.QWidget, ConfigWidget): def update_style(self, *args, **kwargs): return - @property - def is_overriden(self): - return self._is_overriden or self._parent.is_overriden - @property def child_modified(self): for input_field in self.input_fields: @@ -1913,6 +1861,7 @@ class DictInvisible(QtWidgets.QWidget, ConfigWidget): return {self.key: values}, self.is_group +# Proxy for form layout class DictFormWidget(QtWidgets.QWidget, ConfigWidget): value_changed = QtCore.Signal(object) @@ -1928,7 +1877,6 @@ class DictFormWidget(QtWidgets.QWidget, ConfigWidget): self.any_parent_is_group = any_parent_is_group self.is_modified = False - self._is_overriden = False self.is_group = False super(DictFormWidget, self).__init__(parent) @@ -1946,10 +1894,6 @@ class DictFormWidget(QtWidgets.QWidget, ConfigWidget): return self.value_changed.emit(self) - @property - def is_overriden(self): - return self._parent.is_overriden - @property def child_modified(self): for input_field in self.input_fields.values(): From 949c844cc1b85648a99e3f82336a55f161f50230 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 21 Aug 2020 19:01:32 +0200 Subject: [PATCH 152/813] was overriden as global attribute --- .../config_setting/widgets/inputs.py | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 8f6908eb3a..bbc5387c02 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -21,11 +21,16 @@ class SchemeGroupHierarchyBug(Exception): class ConfigWidget: default_state = "" _is_overriden = False + _was_overriden = False @property def is_overriden(self): return self._is_overriden or self._parent.is_overriden + @property + def was_overriden(self): + return self._was_overriden + @property def is_overidable(self): return self._parent.is_overidable @@ -111,7 +116,6 @@ class BooleanWidget(QtWidgets.QWidget, InputWidget): self.is_group = is_group self._is_modified = False - self._was_overriden = False self._state = None @@ -184,7 +188,7 @@ class BooleanWidget(QtWidgets.QWidget, InputWidget): @property def is_modified(self): - return self._is_modified or (self._was_overriden != self.is_overriden) + return self._is_modified or (self.was_overriden != self.is_overriden) def _on_value_change(self, item=None): if self.ignore_value_changes: @@ -249,7 +253,6 @@ class IntegerWidget(QtWidgets.QWidget, InputWidget): self.is_group = is_group self._is_modified = False - self._was_overriden = False self._state = None @@ -286,7 +289,7 @@ class IntegerWidget(QtWidgets.QWidget, InputWidget): @property def is_modified(self): - return self._is_modified or (self._was_overriden != self.is_overriden) + return self._is_modified or (self.was_overriden != self.is_overriden) def set_value(self, value, *, default_value=False): self.int_input.setValue(value) @@ -372,7 +375,6 @@ class FloatWidget(QtWidgets.QWidget, InputWidget): self.is_group = is_group self._is_modified = False - self._was_overriden = False self._state = None @@ -419,7 +421,7 @@ class FloatWidget(QtWidgets.QWidget, InputWidget): @property def is_modified(self): - return self._is_modified or (self._was_overriden != self.is_overriden) + return self._is_modified or (self.was_overriden != self.is_overriden) def set_value(self, value, *, default_value=False): self.float_input.setValue(value) @@ -503,7 +505,6 @@ class TextSingleLineWidget(QtWidgets.QWidget, InputWidget): self.is_group = is_group self._is_modified = False - self._was_overriden = False self._state = None @@ -540,7 +541,7 @@ class TextSingleLineWidget(QtWidgets.QWidget, InputWidget): @property def is_modified(self): - return self._is_modified or (self._was_overriden != self.is_overriden) + return self._is_modified or (self.was_overriden != self.is_overriden) def set_value(self, value, *, default_value=False): self.text_input.setText(value) @@ -626,7 +627,6 @@ class TextMultiLineWidget(QtWidgets.QWidget, InputWidget): self.is_group = is_group self._is_modified = False - self._was_overriden = False self._state = None @@ -661,7 +661,7 @@ class TextMultiLineWidget(QtWidgets.QWidget, InputWidget): @property def is_modified(self): - return self._is_modified or (self._was_overriden != self.is_overriden) + return self._is_modified or (self.was_overriden != self.is_overriden) def set_value(self, value, *, default_value=False): self.text_input.setPlainText(value) @@ -802,7 +802,6 @@ class RawJsonWidget(QtWidgets.QWidget, InputWidget): self.is_group = is_group self._is_modified = False - self._was_overriden = False self._state = None @@ -838,7 +837,7 @@ class RawJsonWidget(QtWidgets.QWidget, InputWidget): @property def is_modified(self): - return self._is_modified or (self._was_overriden != self.is_overriden) + return self._is_modified or (self.was_overriden != self.is_overriden) def set_value(self, value, *, default_value=False): self.text_input.setPlainText(value) @@ -1081,7 +1080,6 @@ class TextListWidget(QtWidgets.QWidget, InputWidget): self._is_modified = False self.is_group = is_group - self._was_overriden = False self._state = None @@ -1118,7 +1116,7 @@ class TextListWidget(QtWidgets.QWidget, InputWidget): @property def is_modified(self): - return self._is_modified or (self._was_overriden != self.is_overriden) + return self._is_modified or (self.was_overriden != self.is_overriden) def _on_value_change(self, item=None): if self.ignore_value_changes: @@ -1410,7 +1408,6 @@ class ModifiableDict(ExpandingWidget, InputWidget): self.is_group = is_group self._is_modified = False - self._was_overriden = False self._state = None super(ModifiableDict, self).__init__(input_data["label"], parent) From aaab8b4bf014fee3f6a728121b1c3d78788ebfe7 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 21 Aug 2020 19:08:40 +0200 Subject: [PATCH 153/813] working path to config --- pype/config.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/config.py b/pype/config.py index b3e860d72f..488492d722 100644 --- a/pype/config.py +++ b/pype/config.py @@ -6,11 +6,11 @@ import copy log = logging.getLogger(__name__) STUDIO_PRESETS_PATH = os.path.normpath( - os.path.join(os.environ["PYPE_CONFIG"], "config", "studio_presets") + os.path.join(os.environ["PYPE_CONFIG"], "studio_presets") ) PROJECT_CONFIGURATION_DIR = "project_presets" PROJECT_PRESETS_PATH = os.path.normpath(os.path.join( - os.environ["PYPE_CONFIG"], "config", PROJECT_CONFIGURATION_DIR + os.environ["PYPE_CONFIG"], PROJECT_CONFIGURATION_DIR )) first_run = False From 91158eaed98af947a2a487595d5f9c0cfe19c9ac Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 21 Aug 2020 19:10:00 +0200 Subject: [PATCH 154/813] is modified is global attribute --- .../config_setting/widgets/inputs.py | 49 ++----------------- 1 file changed, 5 insertions(+), 44 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index bbc5387c02..72dd0932e7 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -21,8 +21,13 @@ class SchemeGroupHierarchyBug(Exception): class ConfigWidget: default_state = "" _is_overriden = False + _is_modified = False _was_overriden = False + @property + def is_modified(self): + return self._is_modified or (self.was_overriden != self.is_overriden) + @property def is_overriden(self): return self._is_overriden or self._parent.is_overriden @@ -115,7 +120,6 @@ class BooleanWidget(QtWidgets.QWidget, InputWidget): is_group = True self.is_group = is_group - self._is_modified = False self._state = None @@ -186,10 +190,6 @@ class BooleanWidget(QtWidgets.QWidget, InputWidget): self.set_value(value) self.update_style() - @property - def is_modified(self): - return self._is_modified or (self.was_overriden != self.is_overriden) - def _on_value_change(self, item=None): if self.ignore_value_changes: return @@ -252,7 +252,6 @@ class IntegerWidget(QtWidgets.QWidget, InputWidget): is_group = True self.is_group = is_group - self._is_modified = False self._state = None @@ -287,10 +286,6 @@ class IntegerWidget(QtWidgets.QWidget, InputWidget): self.int_input.valueChanged.connect(self._on_value_change) - @property - def is_modified(self): - return self._is_modified or (self.was_overriden != self.is_overriden) - def set_value(self, value, *, default_value=False): self.int_input.setValue(value) if default_value: @@ -374,7 +369,6 @@ class FloatWidget(QtWidgets.QWidget, InputWidget): is_group = True self.is_group = is_group - self._is_modified = False self._state = None @@ -419,10 +413,6 @@ class FloatWidget(QtWidgets.QWidget, InputWidget): self.float_input.valueChanged.connect(self._on_value_change) - @property - def is_modified(self): - return self._is_modified or (self.was_overriden != self.is_overriden) - def set_value(self, value, *, default_value=False): self.float_input.setValue(value) if default_value: @@ -504,7 +494,6 @@ class TextSingleLineWidget(QtWidgets.QWidget, InputWidget): is_group = True self.is_group = is_group - self._is_modified = False self._state = None @@ -539,10 +528,6 @@ class TextSingleLineWidget(QtWidgets.QWidget, InputWidget): self.text_input.textChanged.connect(self._on_value_change) - @property - def is_modified(self): - return self._is_modified or (self.was_overriden != self.is_overriden) - def set_value(self, value, *, default_value=False): self.text_input.setText(value) if default_value: @@ -626,7 +611,6 @@ class TextMultiLineWidget(QtWidgets.QWidget, InputWidget): is_group = True self.is_group = is_group - self._is_modified = False self._state = None @@ -659,10 +643,6 @@ class TextMultiLineWidget(QtWidgets.QWidget, InputWidget): self.text_input.textChanged.connect(self._on_value_change) - @property - def is_modified(self): - return self._is_modified or (self.was_overriden != self.is_overriden) - def set_value(self, value, *, default_value=False): self.text_input.setPlainText(value) if default_value: @@ -801,7 +781,6 @@ class RawJsonWidget(QtWidgets.QWidget, InputWidget): is_group = True self.is_group = is_group - self._is_modified = False self._state = None @@ -835,10 +814,6 @@ class RawJsonWidget(QtWidgets.QWidget, InputWidget): self.text_input.textChanged.connect(self._on_value_change) - @property - def is_modified(self): - return self._is_modified or (self.was_overriden != self.is_overriden) - def set_value(self, value, *, default_value=False): self.text_input.setPlainText(value) if default_value: @@ -1078,7 +1053,6 @@ class TextListWidget(QtWidgets.QWidget, InputWidget): if not any_parent_is_group and not is_group: is_group = True - self._is_modified = False self.is_group = is_group self._state = None @@ -1114,10 +1088,6 @@ class TextListWidget(QtWidgets.QWidget, InputWidget): self.default_value = self.item_value() self.override_value = None - @property - def is_modified(self): - return self._is_modified or (self.was_overriden != self.is_overriden) - def _on_value_change(self, item=None): if self.ignore_value_changes: return @@ -1407,7 +1377,6 @@ class ModifiableDict(ExpandingWidget, InputWidget): self.any_parent_is_group = any_parent_is_group self.is_group = is_group - self._is_modified = False self._state = None super(ModifiableDict, self).__init__(input_data["label"], parent) @@ -1442,10 +1411,6 @@ class ModifiableDict(ExpandingWidget, InputWidget): self.update_style() - @property - def is_modified(self): - return self._is_modified - def apply_overrides(self, override_value): self._state = None self._is_modified = False @@ -1510,7 +1475,6 @@ class DictExpandWidget(QtWidgets.QWidget, ConfigWidget): self.any_parent_is_group = any_parent_is_group - self._is_modified = False self.is_group = is_group self._state = None @@ -1734,8 +1698,6 @@ class DictInvisible(QtWidgets.QWidget, ConfigWidget): raise SchemeGroupHierarchyBug() self.any_parent_is_group = any_parent_is_group - - self.is_modified = False self.is_group = is_group super(DictInvisible, self).__init__(parent) @@ -1873,7 +1835,6 @@ class DictFormWidget(QtWidgets.QWidget, ConfigWidget): self.any_parent_is_group = any_parent_is_group - self.is_modified = False self.is_group = False super(DictFormWidget, self).__init__(parent) From 0acbc5459eb65acc5b216913578e6df5692b1a23 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 21 Aug 2020 19:17:41 +0200 Subject: [PATCH 155/813] config_value is global method --- .../config_setting/widgets/inputs.py | 57 +++++++------------ 1 file changed, 20 insertions(+), 37 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 72dd0932e7..ee8c800b29 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -44,13 +44,26 @@ class ConfigWidget: def ignore_value_changes(self): return self._parent.ignore_value_changes - def config_value(self): + def reset_attributes(self): + self._is_overriden = False + self._is_modified = False + self._was_overriden = False + + self.reset_children_attributes() + + def reset_children_attributes(self): raise NotImplementedError( - "Method `config_value` is not implemented for `{}`.".format( - self.__class__.__name__ - ) + "Method `reset_children_attributes` not implemented!" ) + def item_value(self): + raise NotImplementedError( + "Method `item_value` not implemented!" + ) + + def config_value(self): + return {self.key: self.item_value()} + def value_from_values(self, values, keys=None): if not values: return NOT_SET @@ -98,6 +111,9 @@ class InputWidget(ConfigWidget): def child_overriden(self): return self._is_overriden + def reset_children_attributes(self): + return + class BooleanWidget(QtWidgets.QWidget, InputWidget): value_changed = QtCore.Signal(object) @@ -227,9 +243,6 @@ class BooleanWidget(QtWidgets.QWidget, InputWidget): def item_value(self): return self.checkbox.isChecked() - def config_value(self): - return {self.key: self.item_value()} - class IntegerWidget(QtWidgets.QWidget, InputWidget): value_changed = QtCore.Signal(object) @@ -344,9 +357,6 @@ class IntegerWidget(QtWidgets.QWidget, InputWidget): def item_value(self): return self.int_input.value() - def config_value(self): - return {self.key: self.item_value()} - class FloatWidget(QtWidgets.QWidget, InputWidget): value_changed = QtCore.Signal(object) @@ -469,9 +479,6 @@ class FloatWidget(QtWidgets.QWidget, InputWidget): def item_value(self): return self.float_input.value() - def config_value(self): - return {self.key: self.item_value()} - class TextSingleLineWidget(QtWidgets.QWidget, InputWidget): value_changed = QtCore.Signal(object) @@ -586,9 +593,6 @@ class TextSingleLineWidget(QtWidgets.QWidget, InputWidget): def item_value(self): return self.text_input.text() - def config_value(self): - return {self.key: self.item_value()} - class TextMultiLineWidget(QtWidgets.QWidget, InputWidget): value_changed = QtCore.Signal(object) @@ -699,9 +703,6 @@ class TextMultiLineWidget(QtWidgets.QWidget, InputWidget): def item_value(self): return self.text_input.toPlainText() - def config_value(self): - return {self.key: self.item_value()} - class RawJsonInput(QtWidgets.QPlainTextEdit): tab_length = 4 @@ -870,9 +871,6 @@ class RawJsonWidget(QtWidgets.QWidget, InputWidget): def item_value(self): return self.text_input.toPlainText() - def config_value(self): - return {self.key: self.item_value()} - class TextListItem(QtWidgets.QWidget, ConfigWidget): _btn_size = 20 @@ -1030,9 +1028,6 @@ class TextListSubWidget(QtWidgets.QWidget, ConfigWidget): return output - def config_value(self): - return {self.key: self.item_value()} - class TextListWidget(QtWidgets.QWidget, InputWidget): value_changed = QtCore.Signal(object) @@ -1136,9 +1131,6 @@ class TextListWidget(QtWidgets.QWidget, InputWidget): def item_value(self): return self.value_widget.config_value() - def config_value(self): - return {self.key: self.item_value()} - class ModifiableDictItem(QtWidgets.QWidget, ConfigWidget): _btn_size = 20 @@ -1448,9 +1440,6 @@ class ModifiableDict(ExpandingWidget, InputWidget): def item_value(self): return self.value_widget.config_value() - def config_value(self): - return {self.key: self.item_value()} - # Dictionaries class DictExpandWidget(QtWidgets.QWidget, ConfigWidget): @@ -1647,9 +1636,6 @@ class DictExpandWidget(QtWidgets.QWidget, ConfigWidget): output.update(input_field.config_value()) return output - def config_value(self): - return {self.key: self.item_value()} - def add_children_gui(self, child_configuration, values): item_type = child_configuration["type"] klass = TypeToKlass.types.get(item_type) @@ -1746,9 +1732,6 @@ class DictInvisible(QtWidgets.QWidget, ConfigWidget): output.update(input_field.config_value()) return output - def config_value(self): - return {self.key: self.item_value()} - def add_children_gui(self, child_configuration, values): item_type = child_configuration["type"] if item_type == "schema": From b402c8d0135057809ce2ce9a61999934d1ea4293 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 21 Aug 2020 19:35:44 +0200 Subject: [PATCH 156/813] removed overrides --- .../kuba_each_case/project_presets.json | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 pype/tools/config_setting/config/project_overrides/kuba_each_case/project_presets.json diff --git a/pype/tools/config_setting/config/project_overrides/kuba_each_case/project_presets.json b/pype/tools/config_setting/config/project_overrides/kuba_each_case/project_presets.json deleted file mode 100644 index b9da242453..0000000000 --- a/pype/tools/config_setting/config/project_overrides/kuba_each_case/project_presets.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "__override_version__": 1, - "plugins": { - "maya": { - "__overriden_keys__": [ - "publish" - ], - "publish": { - "ValidateModelName": { - "enabled": true - }, - "ValidateAssemblyName": { - "enabled": false - } - } - } - } -} \ No newline at end of file From 71c2bb3ec1a5da3b68b5709db3bc6c287c3b0d11 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 21 Aug 2020 22:32:44 +0200 Subject: [PATCH 157/813] moved type up --- .../applications_gui_schema.json | 68 +++++++++---------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/applications_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/applications_gui_schema.json index 12fbb3cc26..bbf74a8f3f 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/applications_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/applications_gui_schema.json @@ -9,144 +9,144 @@ "type": "dict-form", "children": [ { - "key": "blender_2.80", "type": "boolean", + "key": "blender_2.80", "label": "Blender 2.80" }, { - "key": "blender_2.81", "type": "boolean", + "key": "blender_2.81", "label": "Blender 2.81" }, { - "key": "blender_2.82", "type": "boolean", + "key": "blender_2.82", "label": "Blender 2.82" }, { - "key": "blender_2.83", "type": "boolean", + "key": "blender_2.83", "label": "Blender 2.83" }, { - "key": "celaction_local", "type": "boolean", + "key": "celaction_local", "label": "Celaction Local" }, { - "key": "celaction_remote", "type": "boolean", + "key": "celaction_remote", "label": "Celaction Remote" }, { - "key": "harmony_17", "type": "boolean", + "key": "harmony_17", "label": "Harmony 17" }, { - "key": "houdini_16", "type": "boolean", + "key": "houdini_16", "label": "Houdini 16" }, { - "key": "houdini_17", "type": "boolean", + "key": "houdini_17", "label": "Houdini 17" }, { - "key": "houdini_18", "type": "boolean", + "key": "houdini_18", "label": "Houdini 18" }, { - "key": "maya_2017", "type": "boolean", + "key": "maya_2017", "label": "Autodest Maya 2017" }, { - "key": "maya_2018", "type": "boolean", + "key": "maya_2018", "label": "Autodest Maya 2018" }, { - "key": "maya_2019", "type": "boolean", + "key": "maya_2019", "label": "Autodest Maya 2019" }, { - "key": "maya_2020", "type": "boolean", + "key": "maya_2020", "label": "Autodest Maya 2020" }, { "key": "nuke_10.0", "type": "boolean", "label": "Nuke 10.0" }, { - "key": "nuke_11.2", "type": "boolean", + "key": "nuke_11.2", "label": "Nuke 11.2" }, { - "key": "nuke_11.3", "type": "boolean", + "key": "nuke_11.3", "label": "Nuke 11.3" }, { - "key": "nuke_12.0", "type": "boolean", + "key": "nuke_12.0", "label": "Nuke 12.0" }, { - "key": "nukex_10.0", "type": "boolean", + "key": "nukex_10.0", "label": "NukeX 10.0" }, { - "key": "nukex_11.2", "type": "boolean", + "key": "nukex_11.2", "label": "NukeX 11.2" }, { - "key": "nukex_11.3", "type": "boolean", + "key": "nukex_11.3", "label": "NukeX 11.3" }, { - "key": "nukex_12.0", "type": "boolean", + "key": "nukex_12.0", "label": "NukeX 12.0" }, { - "key": "nukestudio_10.0", "type": "boolean", + "key": "nukestudio_10.0", "label": "NukeStudio 10.0" }, { - "key": "nukestudio_11.2", "type": "boolean", + "key": "nukestudio_11.2", "label": "NukeStudio 11.2" }, { - "key": "nukestudio_11.3", "type": "boolean", + "key": "nukestudio_11.3", "label": "NukeStudio 11.3" }, { - "key": "nukestudio_12.0", "type": "boolean", + "key": "nukestudio_12.0", "label": "NukeStudio 12.0" }, { - "key": "houdini_16.5", "type": "boolean", + "key": "houdini_16.5", "label": "Houdini 16.5" }, { - "key": "houdini_17", "type": "boolean", + "key": "houdini_17", "label": "Houdini 17" }, { - "key": "houdini_18", "type": "boolean", + "key": "houdini_18", "label": "Houdini 18" }, { - "key": "premiere_2019", "type": "boolean", + "key": "premiere_2019", "label": "Premiere 2019" }, { - "key": "premiere_2020", "type": "boolean", + "key": "premiere_2020", "label": "Premiere 2020" }, { - "key": "premiere_2020", "type": "boolean", + "key": "premiere_2020", "label": "Premiere 2020" }, { + "type": "boolean", "key": "resolve_16", - "type": "boolean", "label": "BM DaVinci Resolve 16" }, { - "key": "storyboardpro_7", "type": "boolean", + "key": "storyboardpro_7", "label": "Storyboard Pro 7" }, { - "key": "unreal_4.24", "type": "boolean", + "key": "unreal_4.24", "label": "Unreal Editor 4.24" } ] From 5f0ea1378c5d9c5fc34fdcc91f7fd03698f77584 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Sun, 23 Aug 2020 19:16:50 +0200 Subject: [PATCH 158/813] indexes should be removed properly now for artist view --- pype/tools/pyblish_pype/model.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/pype/tools/pyblish_pype/model.py b/pype/tools/pyblish_pype/model.py index fdcdffd33f..3c9d4806ac 100644 --- a/pype/tools/pyblish_pype/model.py +++ b/pype/tools/pyblish_pype/model.py @@ -870,13 +870,18 @@ class ArtistProxy(QtCore.QAbstractProxyModel): self.rowsInserted.emit(self.parent(), new_from, new_to + 1) def _remove_rows(self, parent_row, from_row, to_row): - removed_rows = [] increment_num = self.mapping_from[parent_row][from_row] + + to_end_index = len(self.mapping_from[parent_row]) - 1 + for _idx in range(0, parent_row): + to_end_index += len(self.mapping_from[_idx]) + + removed_rows = 0 _emit_last = None for row_num in reversed(range(from_row, to_row + 1)): row = self.mapping_from[parent_row].pop(row_num) _emit_last = row - removed_rows.append(row) + removed_rows += 1 _emit_first = int(increment_num) mapping_from_len = len(self.mapping_from) @@ -896,11 +901,8 @@ class ArtistProxy(QtCore.QAbstractProxyModel): self.mapping_from[idx_i][idx_j] = increment_num increment_num += 1 - first_to_row = None - for row in removed_rows: - if first_to_row is None: - first_to_row = row - self.mapping_to.pop(row) + for idx in range(removed_rows): + self.mapping_to.pop(to_end_index - idx) return (_emit_first, _emit_last) From a5e6979a86009d56a52a7ccf41651e4a51277dac Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 24 Aug 2020 10:52:52 +0200 Subject: [PATCH 159/813] renamed gui schemas files and added intent --- ...i_schema.json => 0_studio_gui_schema.json} | 5 +++-- ...ma.json => 1_applications_gui_schema.json} | 0 .../studio_schema/1_intents_gui_schema.json | 19 +++++++++++++++++++ ...ui_schema.json => 1_tools_gui_schema.json} | 0 .../config_setting/widgets/base.py | 2 +- 5 files changed, 23 insertions(+), 3 deletions(-) rename pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/{studio_gui_schema.json => 0_studio_gui_schema.json} (83%) rename pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/{applications_gui_schema.json => 1_applications_gui_schema.json} (100%) create mode 100644 pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_intents_gui_schema.json rename pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/{tools_gui_schema.json => 1_tools_gui_schema.json} (100%) diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/studio_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/0_studio_gui_schema.json similarity index 83% rename from pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/studio_gui_schema.json rename to pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/0_studio_gui_schema.json index 088c407804..868fbea82e 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/studio_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/0_studio_gui_schema.json @@ -10,8 +10,9 @@ "children": [{ "type": "schema", "children": [ - "applications_gui_schema", - "tools_gui_schema" + "1_applications_gui_schema", + "1_tools_gui_schema", + "1_intents_gui_schema" ] }] }, { diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/applications_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_applications_gui_schema.json similarity index 100% rename from pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/applications_gui_schema.json rename to pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_applications_gui_schema.json diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_intents_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_intents_gui_schema.json new file mode 100644 index 0000000000..1469ffc5fc --- /dev/null +++ b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_intents_gui_schema.json @@ -0,0 +1,19 @@ +{ + "key": "intent", + "type": "dict-expanding", + "label": "Intent Setting", + "is_group": true, + "is_file": true, + "children": [ + { + "type": "dict-modifiable", + "key": "items", + "label": "Intent Key/Label", + "object_type": "text-singleline" + }, { + "type": "text-singleline", + "key": "default", + "label": "Default intent" + } + ] +} diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/tools_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_tools_gui_schema.json similarity index 100% rename from pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/tools_gui_schema.json rename to pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_tools_gui_schema.json diff --git a/pype/tools/config_setting/config_setting/widgets/base.py b/pype/tools/config_setting/config_setting/widgets/base.py index af979c0f9f..a794ea64f3 100644 --- a/pype/tools/config_setting/config_setting/widgets/base.py +++ b/pype/tools/config_setting/config_setting/widgets/base.py @@ -62,7 +62,7 @@ class StudioWidget(QtWidgets.QWidget): self.input_fields.clear() values = {"studio": config.studio_presets()} - schema = lib.gui_schema("studio_schema", "studio_gui_schema") + schema = lib.gui_schema("studio_schema", "0_studio_gui_schema") self.keys = schema.get("keys", []) self.add_children_gui(schema, values) self.schema = schema From df0eae7719495b8cf8bc7740b20e68067cf39e81 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 24 Aug 2020 11:39:38 +0200 Subject: [PATCH 160/813] added tray items --- .../studio_schema/0_studio_gui_schema.json | 15 +-- .../studio_schema/1_muster.json | 12 ++ .../studio_schema/1_tray_items.json | 106 ++++++++++++++++++ 3 files changed, 121 insertions(+), 12 deletions(-) create mode 100644 pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_muster.json create mode 100644 pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_tray_items.json diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/0_studio_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/0_studio_gui_schema.json index 868fbea82e..9063dfcc8e 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/0_studio_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/0_studio_gui_schema.json @@ -10,22 +10,13 @@ "children": [{ "type": "schema", "children": [ + "1_tray_items", "1_applications_gui_schema", "1_tools_gui_schema", - "1_intents_gui_schema" + "1_intents_gui_schema", + "1_muster" ] }] - }, { - "key": "muster", - "type": "dict-invisible", - "is_group": true, - "children": [{ - "key": "templates_mapping", - "label": "Muster", - "is_file": true, - "type": "dict-modifiable", - "object_type": "int" - }] } ] } diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_muster.json b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_muster.json new file mode 100644 index 0000000000..224f2efc71 --- /dev/null +++ b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_muster.json @@ -0,0 +1,12 @@ +{ + "key": "muster", + "type": "dict-invisible", + "is_group": true, + "children": [{ + "key": "templates_mapping", + "label": "Muster", + "is_file": true, + "type": "dict-modifiable", + "object_type": "int" + }] +} diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_tray_items.json b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_tray_items.json new file mode 100644 index 0000000000..0456e52985 --- /dev/null +++ b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_tray_items.json @@ -0,0 +1,106 @@ +{ + "key": "tray_modules", + "type": "dict-expanding", + "label": "Modules", + "is_file": true, + "is_group": true, + "children": [ + { + "key": "item_usage", + "type": "dict-invisible", + "children": [ + { + "type": "dict-form", + "children": [ + { + "type": "boolean", + "key": "User settings", + "label": "User settings" + }, { + "type": "boolean", + "key": "Ftrack", + "label": "Ftrack" + }, { + "type": "boolean", + "key": "Muster", + "label": "Muster" + }, { + "type": "boolean", + "key": "Avalon", + "label": "Avalon" + }, { + "type": "boolean", + "key": "Clockify", + "label": "Clockify" + }, { + "type": "boolean", + "key": "Standalone Publish", + "label": "Standalone Publish" + }, { + "type": "boolean", + "key": "Logging", + "label": "Logging" + }, { + "type": "boolean", + "key": "Idle Manager", + "label": "Idle Manager" + }, { + "type": "boolean", + "key": "Rest Api", + "label": "Rest Api" + }, { + "type": "boolean", + "key": "Adobe Communicator", + "label": "Adobe Communicator" + } + ] + } + ] + }, { + "key": "attributes", + "type": "dict-expanding", + "label": "Module attributes", + "children": [ + { + "type": "dict-expanding", + "key": "Rest Api", + "label": "Rest Api", + "MISINGKEYCONF": {"exclude_ports": []}, + "children": [ + { + "type": "int", + "key": "default_port", + "label": "Default Port" + } + ] + }, { + "type": "dict-expanding", + "key": "Timers Manager", + "label": "Timers Manager", + "children": [ + { + "type": "float", + "key": "full_time", + "label": "Max idle time" + }, { + "type": "float", + "key": "message_time", + "label": "When dialog will show" + } + ] + }, { + "type": "dict-expanding", + "key": "Clockify", + "label": "Clockify", + "children": [ + { + "type": "text-singleline", + "key": "workspace_name", + "label": "Workspace name" + } + ] + } + ] + } + ] +} From f32aa4fd84abfa09e53bc4ceb5c07f3bf3d4f391 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 24 Aug 2020 11:48:40 +0200 Subject: [PATCH 161/813] do not show the attributes label --- .../config_gui_schema/studio_schema/1_tray_items.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_tray_items.json b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_tray_items.json index 0456e52985..e6f9a41e51 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_tray_items.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_tray_items.json @@ -58,8 +58,7 @@ ] }, { "key": "attributes", - "type": "dict-expanding", - "label": "Module attributes", + "type": "dict-invisible", "children": [ { "type": "dict-expanding", From 4a375737e346540d0c93422177b9a54c758079ee Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 24 Aug 2020 12:18:17 +0200 Subject: [PATCH 162/813] moved muster back to studio --- .../studio_schema/0_studio_gui_schema.json | 14 ++++++++++++-- .../config_gui_schema/studio_schema/1_muster.json | 12 ------------ 2 files changed, 12 insertions(+), 14 deletions(-) delete mode 100644 pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_muster.json diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/0_studio_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/0_studio_gui_schema.json index 9063dfcc8e..2fd63c7cdc 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/0_studio_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/0_studio_gui_schema.json @@ -13,10 +13,20 @@ "1_tray_items", "1_applications_gui_schema", "1_tools_gui_schema", - "1_intents_gui_schema", - "1_muster" + "1_intents_gui_schema" ] }] + }, { + "key": "muster", + "type": "dict-invisible", + "children": [{ + "is_group": true, + "is_file": true, + "key": "templates_mapping", + "label": "Muster - Templates mapping", + "type": "dict-modifiable", + "object_type": "int" + }] } ] } diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_muster.json b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_muster.json deleted file mode 100644 index 224f2efc71..0000000000 --- a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_muster.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "key": "muster", - "type": "dict-invisible", - "is_group": true, - "children": [{ - "key": "templates_mapping", - "label": "Muster", - "is_file": true, - "type": "dict-modifiable", - "object_type": "int" - }] -} From fce265af47a8bb297e0d4b86b1bef88eccc4d1f5 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 24 Aug 2020 12:52:46 +0200 Subject: [PATCH 163/813] renamed presets to configurations --- pype/config.py | 12 ++++----- .../config_setting/widgets/base.py | 26 +++++++++---------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/pype/config.py b/pype/config.py index 488492d722..454835e005 100644 --- a/pype/config.py +++ b/pype/config.py @@ -6,7 +6,7 @@ import copy log = logging.getLogger(__name__) STUDIO_PRESETS_PATH = os.path.normpath( - os.path.join(os.environ["PYPE_CONFIG"], "studio_presets") + os.path.join(os.environ["PYPE_CONFIG"], "studio_configurations") ) PROJECT_CONFIGURATION_DIR = "project_presets" PROJECT_PRESETS_PATH = os.path.normpath(os.path.join( @@ -128,11 +128,11 @@ def load_jsons_from_dir(path, *args, **kwargs): return output -def studio_presets(*args, **kwargs): +def studio_configurations(*args, **kwargs): return load_jsons_from_dir(STUDIO_PRESETS_PATH, *args, **kwargs) -def global_project_presets(**kwargs): +def global_project_configurations(**kwargs): return load_jsons_from_dir(PROJECT_PRESETS_PATH, **kwargs) @@ -142,7 +142,7 @@ def path_to_project_overrides(project_name): return os.path.join(dirpath, PROJECT_CONFIGURATION_DIR + ".json") -def project_preset_overrides(project_name, **kwargs): +def project_configurations_overrides(project_name, **kwargs): if not project_name: return {} @@ -184,10 +184,10 @@ def apply_overrides(global_presets, project_overrides): def project_presets(project_name=None, **kwargs): - global_presets = global_project_presets(**kwargs) + global_presets = global_project_configurations(**kwargs) if not project_name: project_name = os.environ.get("AVALON_PROJECT") - project_overrides = project_preset_overrides(project_name, **kwargs) + project_overrides = project_configurations_overrides(project_name, **kwargs) return apply_overrides(global_presets, project_overrides) diff --git a/pype/tools/config_setting/config_setting/widgets/base.py b/pype/tools/config_setting/config_setting/widgets/base.py index a794ea64f3..23eabdb531 100644 --- a/pype/tools/config_setting/config_setting/widgets/base.py +++ b/pype/tools/config_setting/config_setting/widgets/base.py @@ -61,7 +61,7 @@ class StudioWidget(QtWidgets.QWidget): widget.deleteLater() self.input_fields.clear() - values = {"studio": config.studio_presets()} + values = {"studio": config.studio_configurations()} schema = lib.gui_schema("studio_schema", "0_studio_gui_schema") self.keys = schema.get("keys", []) self.add_children_gui(schema, values) @@ -80,14 +80,14 @@ class StudioWidget(QtWidgets.QWidget): all_values = all_values["studio"] # Load studio data with metadata - current_presets = config.studio_presets() + current_configurations = config.studio_configurations() keys_to_file = lib.file_keys_from_schema(self.schema) for key_sequence in keys_to_file: # Skip first key key_sequence = key_sequence[1:] subpath = "/".join(key_sequence) + ".json" - origin_values = current_presets + origin_values = current_configurations for key in key_sequence: if key not in origin_values: origin_values = {} @@ -280,13 +280,13 @@ class ProjectWidget(QtWidgets.QWidget): footer_layout.addWidget(spacer_widget, 1) footer_layout.addWidget(save_btn, 0) - presets_widget = QtWidgets.QWidget() - presets_layout = QtWidgets.QVBoxLayout(presets_widget) - presets_layout.setContentsMargins(0, 0, 0, 0) - presets_layout.setSpacing(0) + configurations_widget = QtWidgets.QWidget() + configurations_layout = QtWidgets.QVBoxLayout(configurations_widget) + configurations_layout.setContentsMargins(0, 0, 0, 0) + configurations_layout.setSpacing(0) - presets_layout.addWidget(scroll_widget, 1) - presets_layout.addWidget(footer_widget, 0) + configurations_layout.addWidget(scroll_widget, 1) + configurations_layout.addWidget(footer_widget, 0) layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) @@ -294,7 +294,7 @@ class ProjectWidget(QtWidgets.QWidget): self.setLayout(layout) layout.addWidget(project_list_widget, 0) - layout.addWidget(presets_widget, 1) + layout.addWidget(configurations_widget, 1) save_btn.clicked.connect(self._save) project_list_widget.project_changed.connect(self._on_project_change) @@ -307,7 +307,7 @@ class ProjectWidget(QtWidgets.QWidget): self.reset() def reset(self): - values = config.global_project_presets() + values = config.global_project_configurations() schema = lib.gui_schema("projects_schema", "project_gui_schema") self.keys = schema.get("keys", []) self.add_children_gui(schema, values) @@ -390,14 +390,14 @@ class ProjectWidget(QtWidgets.QWidget): all_values = all_values["project"] # Load studio data with metadata - current_presets = config.global_project_presets() + current_configurations = config.global_project_configurations() keys_to_file = lib.file_keys_from_schema(self.schema) for key_sequence in keys_to_file: # Skip first key key_sequence = key_sequence[1:] subpath = "/".join(key_sequence) + ".json" - origin_values = current_presets + origin_values = current_configurations for key in key_sequence: if key not in origin_values: origin_values = {} From eb3406751119d6581bc9380b08110849f05217c6 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 24 Aug 2020 13:01:04 +0200 Subject: [PATCH 164/813] changed input from vertical to horizontal layout --- .../config_setting/widgets/inputs.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index ee8c800b29..3a862b7a3e 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -141,9 +141,9 @@ class BooleanWidget(QtWidgets.QWidget, InputWidget): super(BooleanWidget, self).__init__(parent) - layout = QtWidgets.QVBoxLayout(self) + layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(0) + layout.setSpacing(5) self.checkbox = QtWidgets.QCheckBox() self.checkbox.setAttribute(QtCore.Qt.WA_StyledBackground) @@ -270,9 +270,9 @@ class IntegerWidget(QtWidgets.QWidget, InputWidget): super(IntegerWidget, self).__init__(parent) - layout = QtWidgets.QVBoxLayout(self) + layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(0) + layout.setSpacing(5) self.int_input = ModifiedIntSpinBox() @@ -384,9 +384,9 @@ class FloatWidget(QtWidgets.QWidget, InputWidget): super(FloatWidget, self).__init__(parent) - layout = QtWidgets.QVBoxLayout(self) + layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(0) + layout.setSpacing(5) self.float_input = ModifiedFloatSpinBox() @@ -506,9 +506,9 @@ class TextSingleLineWidget(QtWidgets.QWidget, InputWidget): super(TextSingleLineWidget, self).__init__(parent) - layout = QtWidgets.QVBoxLayout(self) + layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(0) + layout.setSpacing(5) self.text_input = QtWidgets.QLineEdit() @@ -620,9 +620,9 @@ class TextMultiLineWidget(QtWidgets.QWidget, InputWidget): super(TextMultiLineWidget, self).__init__(parent) - layout = QtWidgets.QVBoxLayout(self) + layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(0) + layout.setSpacing(5) self.text_input = QtWidgets.QPlainTextEdit() if not label_widget: From f62d7b8cac476378fb109e64996eeb9d7a5d814c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 24 Aug 2020 14:42:25 +0200 Subject: [PATCH 165/813] removed temp configurations --- .../studio_presets/ftrack/server_plugins.json | 1 - .../studio_presets/ftrack/user_plugins.json | 5 --- .../studio_presets/global/applications.json | 43 ------------------- .../config/studio_presets/global/intents.json | 9 ---- .../config/studio_presets/global/tools.json | 6 --- .../studio_presets/global/tray_items.json | 25 ----------- .../muster/templates_mapping.json | 19 -------- 7 files changed, 108 deletions(-) delete mode 100644 pype/tools/config_setting/config/studio_presets/ftrack/server_plugins.json delete mode 100644 pype/tools/config_setting/config/studio_presets/ftrack/user_plugins.json delete mode 100644 pype/tools/config_setting/config/studio_presets/global/applications.json delete mode 100644 pype/tools/config_setting/config/studio_presets/global/intents.json delete mode 100644 pype/tools/config_setting/config/studio_presets/global/tools.json delete mode 100644 pype/tools/config_setting/config/studio_presets/global/tray_items.json delete mode 100644 pype/tools/config_setting/config/studio_presets/muster/templates_mapping.json diff --git a/pype/tools/config_setting/config/studio_presets/ftrack/server_plugins.json b/pype/tools/config_setting/config/studio_presets/ftrack/server_plugins.json deleted file mode 100644 index 0967ef424b..0000000000 --- a/pype/tools/config_setting/config/studio_presets/ftrack/server_plugins.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/pype/tools/config_setting/config/studio_presets/ftrack/user_plugins.json b/pype/tools/config_setting/config/studio_presets/ftrack/user_plugins.json deleted file mode 100644 index 1ba8e9b511..0000000000 --- a/pype/tools/config_setting/config/studio_presets/ftrack/user_plugins.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "TestAction": { - "ignore_me": true - } -} diff --git a/pype/tools/config_setting/config/studio_presets/global/applications.json b/pype/tools/config_setting/config/studio_presets/global/applications.json deleted file mode 100644 index 21693e5fee..0000000000 --- a/pype/tools/config_setting/config/studio_presets/global/applications.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "blender_2.80": true, - "blender_2.81": true, - "blender_2.82": true, - "blender_2.83": true, - "harmony_17": true, - "houdini_16": true, - "houdini_17": true, - "houdini_18": true, - "maya_2016": true, - "maya_2017": true, - "maya_2018": true, - "maya_2019": true, - "maya_2020": true, - "nuke_10.0": true, - "nuke_11.0": true, - "nuke_11.2": true, - "nuke_11.3": true, - "nuke_12.0": true, - "nukex_10.0": true, - "nukex_11.0": true, - "nukex_11.2": true, - "nukex_11.3": true, - "nukex_12.0": true, - "nukestudio_10.0": true, - "nukestudio_11.0": true, - "nukestudio_11.2": true, - "nukestudio_11.3": true, - "nukestudio_12.0": true, - "photoshop_2020": true, - "premiere_2019": true, - "premiere_2020": true, - "python_2": true, - "python_3": true, - "resolve_16": true, - "shell": true, - "storyboardpro_7": true, - "unreal_4.21": true, - "celaction_local": true, - "celaction_remote": true, - "houdini_16.5": false, - "unreal_4.24": true -} \ No newline at end of file diff --git a/pype/tools/config_setting/config/studio_presets/global/intents.json b/pype/tools/config_setting/config/studio_presets/global/intents.json deleted file mode 100644 index c853e27b7f..0000000000 --- a/pype/tools/config_setting/config/studio_presets/global/intents.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "default": "wip", - "items": { - "": "", - "wip": "WIP", - "test": "TEST", - "final": "FINAL" - } -} diff --git a/pype/tools/config_setting/config/studio_presets/global/tools.json b/pype/tools/config_setting/config/studio_presets/global/tools.json deleted file mode 100644 index 53aab7b2ca..0000000000 --- a/pype/tools/config_setting/config/studio_presets/global/tools.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "mtoa_3.0.1": false, - "mtoa_3.1.1": false, - "mtoa_3.2.0": false, - "yeti_2.1.2": false -} \ No newline at end of file diff --git a/pype/tools/config_setting/config/studio_presets/global/tray_items.json b/pype/tools/config_setting/config/studio_presets/global/tray_items.json deleted file mode 100644 index a42bf67c38..0000000000 --- a/pype/tools/config_setting/config/studio_presets/global/tray_items.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "usage": { - "User settings": false, - "Ftrack": false, - "Muster": false, - "Avalon": true, - "Clockify": false, - "Standalone Publish": true, - "Logging": true, - "Idle Manager": true, - "Timers Manager": true, - "Rest Api": true, - "Premiere Communicator": true - }, - "attributes": { - "Rest Api": { - "default_port": 8021, - "exclude_ports": [] - }, - "Timers Manager": { - "full_time": 15, - "message_time": 0.5 - } - } -} diff --git a/pype/tools/config_setting/config/studio_presets/muster/templates_mapping.json b/pype/tools/config_setting/config/studio_presets/muster/templates_mapping.json deleted file mode 100644 index 0c09113515..0000000000 --- a/pype/tools/config_setting/config/studio_presets/muster/templates_mapping.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "3delight": 41, - "arnold": 46, - "arnold_sf": 57, - "gelato": 30, - "harware": 3, - "krakatoa": 51, - "file_layers": 7, - "mentalray": 2, - "mentalray_sf": 6, - "redshift": 55, - "renderman": 29, - "software": 1, - "software_sf": 5, - "turtle": 10, - "vector": 4, - "vray": 37, - "ffmpeg": 48 -} \ No newline at end of file From b13c05919a50a4df8546a2b47ed7c1de0e39ede6 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 24 Aug 2020 14:58:07 +0200 Subject: [PATCH 166/813] changed names of project schemas --- ..._schema.json => 0_project_gui_schema.json} | 5 ++- ...json => 1_ftrack_projects_gui_schema.json} | 0 ..._schema.json => 1_plugins_gui_schema.json} | 10 +++--- .../projects_schema/test_project_schema.json | 34 ------------------- .../config_setting/widgets/base.py | 2 +- 5 files changed, 8 insertions(+), 43 deletions(-) rename pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/{project_gui_schema.json => 0_project_gui_schema.json} (58%) rename pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/{ftrack_projects_gui_schema.json => 1_ftrack_projects_gui_schema.json} (100%) rename pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/{plugins_gui_schema.json => 1_plugins_gui_schema.json} (100%) delete mode 100644 pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/test_project_schema.json diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/project_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/0_project_gui_schema.json similarity index 58% rename from pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/project_gui_schema.json rename to pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/0_project_gui_schema.json index 0405524b40..c281ce3c79 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/project_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/0_project_gui_schema.json @@ -5,9 +5,8 @@ { "type": "schema", "children": [ - "test_project_schema", - "ftrack_projects_gui_schema", - "plugins_gui_schema" + "1_ftrack_projects_gui_schema", + "1_plugins_gui_schema" ] } ] diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/ftrack_projects_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_ftrack_projects_gui_schema.json similarity index 100% rename from pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/ftrack_projects_gui_schema.json rename to pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_ftrack_projects_gui_schema.json diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/plugins_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json similarity index 100% rename from pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/plugins_gui_schema.json rename to pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json index 1eaecbb8e5..1d1f299b55 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/plugins_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json @@ -1,23 +1,23 @@ { - "key": "plugins", "type": "dict-expanding", + "key": "plugins", "label": "Plugins", "children": [ { - "key": "maya", "type": "dict-expanding", + "key": "maya", "label": "Maya", "children": [ { - "key": "publish", "type": "dict-expanding", + "key": "publish", "label": "Publish plugins", "is_group": true, "is_file": true, "children": [ { - "key": "ValidateModelName", "type": "dict-invisible", + "key": "ValidateModelName", "label": "Validate Model Name", "children": [ { @@ -27,8 +27,8 @@ } ] }, { - "key": "ValidateAssemblyName", "type": "dict-invisible", + "key": "ValidateAssemblyName", "label": "Validate Assembly Name", "children": [ { diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/test_project_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/test_project_schema.json deleted file mode 100644 index 43c9a647f4..0000000000 --- a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/test_project_schema.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "key": "test_inputs", - "type": "dict-expanding", - "label": "Test inputs", - "is_group": true, - "is_file": true, - "children": [ - { - "key": "boolean", - "type": "boolean", - "label": "Boolean" - }, { - "key": "test_singleline", - "type": "text-singleline", - "label": "Text singleline" - }, { - "key": "test_multiline", - "type": "text-multiline", - "label": "Text multiline" - }, { - "key": "raw_json", - "type": "raw-json", - "label": "Raw json" - }, { - "key": "int", - "type": "int", - "label": "Int" - }, { - "key": "float", - "type": "float", - "label": "Float" - } - ] -} diff --git a/pype/tools/config_setting/config_setting/widgets/base.py b/pype/tools/config_setting/config_setting/widgets/base.py index 23eabdb531..7bbd95b135 100644 --- a/pype/tools/config_setting/config_setting/widgets/base.py +++ b/pype/tools/config_setting/config_setting/widgets/base.py @@ -308,7 +308,7 @@ class ProjectWidget(QtWidgets.QWidget): def reset(self): values = config.global_project_configurations() - schema = lib.gui_schema("projects_schema", "project_gui_schema") + schema = lib.gui_schema("projects_schema", "0_project_gui_schema") self.keys = schema.get("keys", []) self.add_children_gui(schema, values) self.schema = schema From 332fe3f668b9eef7251539ae47d9b40b2d628b78 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 24 Aug 2020 14:58:24 +0200 Subject: [PATCH 167/813] DictExpandWidget inherits ExpandingWidget --- .../config_setting/widgets/inputs.py | 60 ++----------------- 1 file changed, 4 insertions(+), 56 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 3a862b7a3e..9bcc54655b 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1442,7 +1442,7 @@ class ModifiableDict(ExpandingWidget, InputWidget): # Dictionaries -class DictExpandWidget(QtWidgets.QWidget, ConfigWidget): +class DictExpandWidget(ExpandingWidget, ConfigWidget): value_changed = QtCore.Signal(object) def __init__( @@ -1469,31 +1469,8 @@ class DictExpandWidget(QtWidgets.QWidget, ConfigWidget): self._state = None self._child_state = None - super(DictExpandWidget, self).__init__(parent) + super(DictExpandWidget, self).__init__(input_data["label"], parent) self.setObjectName("DictExpandWidget") - top_part = ClickableWidget(parent=self) - - button_size = QtCore.QSize(5, 5) - button_toggle = QtWidgets.QToolButton(parent=top_part) - button_toggle.setProperty("btn-type", "expand-toggle") - button_toggle.setIconSize(button_size) - button_toggle.setArrowType(QtCore.Qt.RightArrow) - button_toggle.setCheckable(True) - button_toggle.setChecked(False) - - label = input_data["label"] - button_toggle_text = QtWidgets.QLabel(label, parent=top_part) - button_toggle_text.setObjectName("ExpandLabel") - - layout = QtWidgets.QHBoxLayout(top_part) - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(5) - layout.addWidget(button_toggle) - layout.addWidget(button_toggle_text) - top_part.setLayout(layout) - - main_layout = QtWidgets.QVBoxLayout(self) - main_layout.setContentsMargins(9, 9, 9, 9) content_widget = QtWidgets.QWidget(self) content_widget.setVisible(False) @@ -1501,22 +1478,13 @@ class DictExpandWidget(QtWidgets.QWidget, ConfigWidget): content_layout = QtWidgets.QVBoxLayout(content_widget) content_layout.setContentsMargins(3, 3, 3, 3) - main_layout.addWidget(top_part) - main_layout.addWidget(content_widget) - self.setLayout(main_layout) + self.set_content_widget(content_widget) self.setAttribute(QtCore.Qt.WA_StyledBackground) - self.top_part = top_part - self.button_toggle = button_toggle - self.button_toggle_text = button_toggle_text - self.content_widget = content_widget self.content_layout = content_layout - self.top_part.clicked.connect(self._top_part_clicked) - self.button_toggle.clicked.connect(self.toggle_content) - self.input_fields = [] self.key = input_data["key"] @@ -1527,26 +1495,6 @@ class DictExpandWidget(QtWidgets.QWidget, ConfigWidget): for child_data in input_data.get("children", []): self.add_children_gui(child_data, values) - def _top_part_clicked(self): - self.toggle_content(not self.button_toggle.isChecked()) - - def toggle_content(self, *args): - if len(args) > 0: - checked = args[0] - else: - checked = self.button_toggle.isChecked() - arrow_type = QtCore.Qt.RightArrow - if checked: - arrow_type = QtCore.Qt.DownArrow - self.button_toggle.setChecked(checked) - self.button_toggle.setArrowType(arrow_type) - self.content_widget.setVisible(checked) - self.parent().updateGeometry() - - def resizeEvent(self, event): - super(DictExpandWidget, self).resizeEvent(event) - self.content_widget.updateGeometry() - def apply_overrides(self, override_value): # Make sure this is set to False self._is_overriden = False @@ -1901,8 +1849,8 @@ TypeToKlass.types["text-multiline"] = TextMultiLineWidget TypeToKlass.types["raw-json"] = RawJsonWidget TypeToKlass.types["int"] = IntegerWidget TypeToKlass.types["float"] = FloatWidget +TypeToKlass.types["dict-modifiable"] = ModifiableDict TypeToKlass.types["dict-expanding"] = DictExpandWidget TypeToKlass.types["dict-form"] = DictFormWidget TypeToKlass.types["dict-invisible"] = DictInvisible -TypeToKlass.types["dict-modifiable"] = ModifiableDict TypeToKlass.types["list-text"] = TextListWidget From 93c30ed32049ecd8eb4b117b634049d6656ed8fe Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 24 Aug 2020 14:59:49 +0200 Subject: [PATCH 168/813] added expanding policy of default input labels --- .../config_setting/widgets/inputs.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 9bcc54655b..0497cb7763 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -151,9 +151,9 @@ class BooleanWidget(QtWidgets.QWidget, InputWidget): label = input_data["label"] label_widget = QtWidgets.QLabel(label) label_widget.setAttribute(QtCore.Qt.WA_StyledBackground) - layout.addWidget(label_widget) + layout.addWidget(label_widget, 0) - layout.addWidget(self.checkbox) + layout.addWidget(self.checkbox, 1) if not self._as_widget: self.label_widget = label_widget @@ -279,8 +279,8 @@ class IntegerWidget(QtWidgets.QWidget, InputWidget): if not self._as_widget and not label_widget: label = input_data["label"] label_widget = QtWidgets.QLabel(label) - layout.addWidget(label_widget) - layout.addWidget(self.int_input) + layout.addWidget(label_widget, 0) + layout.addWidget(self.int_input, 1) if not self._as_widget: self.label_widget = label_widget @@ -403,8 +403,8 @@ class FloatWidget(QtWidgets.QWidget, InputWidget): if not self._as_widget and not label_widget: label = input_data["label"] label_widget = QtWidgets.QLabel(label) - layout.addWidget(label_widget) - layout.addWidget(self.float_input) + layout.addWidget(label_widget, 0) + layout.addWidget(self.float_input, 1) if not self._as_widget: self.label_widget = label_widget @@ -515,8 +515,8 @@ class TextSingleLineWidget(QtWidgets.QWidget, InputWidget): if not self._as_widget and not label_widget: label = input_data["label"] label_widget = QtWidgets.QLabel(label) - layout.addWidget(label_widget) - layout.addWidget(self.text_input) + layout.addWidget(label_widget, 0) + layout.addWidget(self.text_input, 1) if not self._as_widget: self.label_widget = label_widget @@ -628,8 +628,8 @@ class TextMultiLineWidget(QtWidgets.QWidget, InputWidget): if not label_widget: label = input_data["label"] label_widget = QtWidgets.QLabel(label) - layout.addWidget(label_widget) - layout.addWidget(self.text_input) + layout.addWidget(label_widget, 0) + layout.addWidget(self.text_input, 1) self.label_widget = label_widget From f0c7dedb4d021860a0991a07e16b3c3814e11c5e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 24 Aug 2020 16:02:31 +0200 Subject: [PATCH 169/813] adde nonexpanding widget --- .../config_setting/widgets/inputs.py | 127 ++++++++++++++++++ 1 file changed, 127 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 0497cb7763..0ceec27d67 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1553,6 +1553,132 @@ class DictExpandWidget(ExpandingWidget, ConfigWidget): self.button_toggle_text.setProperty("state", state) self.button_toggle_text.style().polish(self.button_toggle_text) +class DictWidget(QtWidgets.QWidget, ConfigWidget): + value_changed = QtCore.Signal(object) + + def __init__( + self, input_data, values, parent_keys, parent, label_widget=None + ): + if values is AS_WIDGET: + raise TypeError("Can't use \"{}\" as widget item.".format( + self.__class__.__name__ + )) + self._parent = parent + + any_parent_is_group = parent.is_group + if not any_parent_is_group: + any_parent_is_group = parent.any_parent_is_group + + is_group = input_data.get("is_group", False) + if is_group and any_parent_is_group: + raise SchemeGroupHierarchyBug() + + self.any_parent_is_group = any_parent_is_group + + self.is_group = is_group + + self._state = None + self._child_state = None + + super(DictWidget, self).__init__(parent) + self.setObjectName("DictWidget") + + body_widget = QtWidgets.QWidget(self) + + label_widget = QtWidgets.QLabel( + input_data["label"], parent=body_widget + ) + label_widget.setObjectName("ExpandLabel") + + content_widget = QtWidgets.QWidget(body_widget) + content_layout = QtWidgets.QVBoxLayout(content_widget) + content_layout.setContentsMargins(3, 3, 3, 3) + + body_layout = QtWidgets.QVBoxLayout(body_widget) + body_layout.setContentsMargins(0, 0, 0, 0) + body_layout.setSpacing(5) + body_layout.addWidget(label_widget) + body_layout.addWidget(content_widget) + + self.setAttribute(QtCore.Qt.WA_StyledBackground) + + main_layout = QtWidgets.QHBoxLayout(self) + main_layout.setContentsMargins(5, 5, 5, 5) + main_layout.setSpacing(0) + main_layout.addWidget(body_widget) + + self.label_widget = label_widget + self.content_widget = content_widget + self.content_layout = content_layout + + self.input_fields = [] + + self.key = input_data["key"] + keys = list(parent_keys) + keys.append(self.key) + self.keys = keys + + for child_data in input_data.get("children", []): + self.add_children_gui(child_data, values) + + def apply_overrides(self, override_value): + # Make sure this is set to False + self._is_overriden = False + self._state = None + self._child_state = None + for item in self.input_fields: + if override_value is None: + child_value = None + else: + child_value = override_value.get(item.key) + + item.apply_overrides(child_value) + + self._is_overriden = ( + self.is_group + and self.is_overidable + and ( + override_value is not None + or self.child_overriden + ) + ) + self.update_style() + + def _on_value_change(self, item=None): + if self.ignore_value_changes: + return + + if self.is_group: + if self.is_overidable: + self._is_overriden = True + + # TODO update items + if item is not None: + for _item in self.input_fields: + if _item is not item: + _item.update_style() + + self.value_changed.emit(self) + + self.update_style() + + def update_style(self, is_overriden=None): + child_modified = self.child_modified + child_state = self.style_state(self.child_overriden, child_modified) + if child_state: + child_state = "child-{}".format(child_state) + + if child_state != self._child_state: + self.setProperty("state", child_state) + self.style().polish(self) + self._child_state = child_state + + state = self.style_state(self.is_overriden, self.is_modified) + if self._state == state: + return + + self.label_widget.setProperty("state", state) + self.label_widget.style().polish(self.label_widget) self._state = state @@ -1850,6 +1976,7 @@ TypeToKlass.types["raw-json"] = RawJsonWidget TypeToKlass.types["int"] = IntegerWidget TypeToKlass.types["float"] = FloatWidget TypeToKlass.types["dict-modifiable"] = ModifiableDict +TypeToKlass.types["dict"] = DictWidget TypeToKlass.types["dict-expanding"] = DictExpandWidget TypeToKlass.types["dict-form"] = DictFormWidget TypeToKlass.types["dict-invisible"] = DictInvisible From 8c6ff3b7f825b54a2ed23cc6b52707921cf13d8f Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 24 Aug 2020 16:04:04 +0200 Subject: [PATCH 170/813] fixed dict expanding --- .../config_setting/widgets/inputs.py | 65 ++++++++++++++++++- 1 file changed, 63 insertions(+), 2 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 0ceec27d67..61c6385323 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1551,8 +1551,69 @@ class DictExpandWidget(ExpandingWidget, ConfigWidget): if self._state == state: return - self.button_toggle_text.setProperty("state", state) - self.button_toggle_text.style().polish(self.button_toggle_text) + self.label_widget.setProperty("state", state) + self.label_widget.style().polish(self.label_widget) + + self._state = state + + @property + def is_modified(self): + if self.is_group: + return self.child_modified + return False + + @property + def child_modified(self): + for input_field in self.input_fields: + if input_field.child_modified: + return True + return False + + @property + def child_overriden(self): + for input_field in self.input_fields: + if input_field.child_overriden: + return True + return False + + def item_value(self): + output = {} + for input_field in self.input_fields: + # TODO maybe merge instead of update should be used + # NOTE merge is custom function which merges 2 dicts + output.update(input_field.config_value()) + return output + + def add_children_gui(self, child_configuration, values): + item_type = child_configuration["type"] + klass = TypeToKlass.types.get(item_type) + + item = klass( + child_configuration, values, self.keys, self + ) + item.value_changed.connect(self._on_value_change) + self.content_layout.addWidget(item) + + self.input_fields.append(item) + return item + + 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 + + class DictWidget(QtWidgets.QWidget, ConfigWidget): value_changed = QtCore.Signal(object) From 4776eaca79c5c3b62c793dfcadbce51d33d18172 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 24 Aug 2020 16:17:34 +0200 Subject: [PATCH 171/813] Added few plugins --- .../projects_schema/1_plugins_gui_schema.json | 129 +++++++++++++++++- 1 file changed, 124 insertions(+), 5 deletions(-) diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json index 1d1f299b55..e2717bca37 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json @@ -4,6 +4,21 @@ "label": "Plugins", "children": [ { + "type": "dict-expanding", + "key": "celaction", + "label": "CelAction", + "children": [] + }, { + "type": "dict-expanding", + "key": "ftrack", + "label": "Ftrack", + "children": [] + }, { + "type": "dict-expanding", + "key": "global", + "label": "Global", + "children": [] + }, { "type": "dict-expanding", "key": "maya", "label": "Maya", @@ -12,28 +27,29 @@ "type": "dict-expanding", "key": "publish", "label": "Publish plugins", - "is_group": true, "is_file": true, "children": [ { - "type": "dict-invisible", + "type": "dict", "key": "ValidateModelName", "label": "Validate Model Name", + "is_group": true, "children": [ { - "key": "enabled", "type": "boolean", + "key": "enabled", "label": "Enabled" } ] }, { - "type": "dict-invisible", + "type": "dict", "key": "ValidateAssemblyName", "label": "Validate Assembly Name", + "is_group": true, "children": [ { - "key": "enabled", "type": "boolean", + "key": "enabled", "label": "Enabled" } ] @@ -41,6 +57,109 @@ ] } ] + }, { + "type": "dict-expanding", + "key": "nuke", + "label": "Nuke", + "children": [] + }, { + "type": "dict-expanding", + "key": "nukestudio", + "label": "NukeStudio", + "children": [ + { + "type": "dict-expanding", + "key": "publish", + "label": "Publish plugins", + "is_file": true, + "children": [ + { + "type": "dict", + "key": "CollectInstanceVersion", + "label": "Collect Instance Version", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled", + "default": false + } + ] + }, { + "type": "dict", + "key": "ExtractReviewCutUpVideo", + "label": "Extract Review Cut Up Video", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled", + "default": true + }, { + "type": "list-text", + "key": "tags_addition", + "label": "Tags addition", + "default": [] + } + ] + } + ] + } + ] + }, { + "type": "dict-expanding", + "key": "resolve", + "label": "DaVinci Resolve", + "children": [ + { + "type": "dict-expanding", + "key": "create", + "label": "Creator plugins", + "is_file": true, + "children": [ + { + "type": "dict", + "key": "CreateShotClip", + "label": "Create Shot Clip", + "is_group": true, + "children": [ + { + "type": "text-singleline", + "key": "clipName", + "label": "Clip name template", + "default": "{track}{sequence}{shot}" + }, { + "type": "text-singleline", + "key": "folder", + "label": "Folder", + "default": "takes" + }, { + "type": "text-singleline", + "key": "steps", + "label": "Steps", + "default": 20 + } + ] + } + + ] + } + ] + }, { + "type": "dict-expanding", + "key": "standalonepublisher", + "label": "StandalonePublisher", + "children": [ + { + "type": "dict-expanding", + "key": "publish", + "label": "Publish plugins", + "is_file": true, + "children": [] + } + ] } ] } From 044c644c3bcd871af62bd6932e27d8341d3363ff Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 24 Aug 2020 16:21:11 +0200 Subject: [PATCH 172/813] project_presets changed to project_configurations --- pype/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/config.py b/pype/config.py index 454835e005..c65e336e00 100644 --- a/pype/config.py +++ b/pype/config.py @@ -8,7 +8,7 @@ log = logging.getLogger(__name__) STUDIO_PRESETS_PATH = os.path.normpath( os.path.join(os.environ["PYPE_CONFIG"], "studio_configurations") ) -PROJECT_CONFIGURATION_DIR = "project_presets" +PROJECT_CONFIGURATION_DIR = "project_configurations" PROJECT_PRESETS_PATH = os.path.normpath(os.path.join( os.environ["PYPE_CONFIG"], PROJECT_CONFIGURATION_DIR )) From f0217db5da872ffd07b0ea8919b86d15e230df49 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 24 Aug 2020 16:52:04 +0200 Subject: [PATCH 173/813] fixed overrides --- pype/tools/config_setting/config_setting/widgets/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/tools/config_setting/config_setting/widgets/base.py b/pype/tools/config_setting/config_setting/widgets/base.py index 7bbd95b135..a59b497b2b 100644 --- a/pype/tools/config_setting/config_setting/widgets/base.py +++ b/pype/tools/config_setting/config_setting/widgets/base.py @@ -329,7 +329,7 @@ class ProjectWidget(QtWidgets.QWidget): overrides = None self.is_overidable = False else: - overrides = config.project_preset_overrides(project_name) + overrides = config.project_configurations_overrides(project_name) self.is_overidable = True self.project_name = project_name From 7134b5b9fc2bbb61803c13ba2236d450b974ac78 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 24 Aug 2020 16:52:14 +0200 Subject: [PATCH 174/813] added few plugins --- .../projects_schema/1_plugins_gui_schema.json | 187 +++++++++++++++++- 1 file changed, 183 insertions(+), 4 deletions(-) diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json index e2717bca37..c12825c3ec 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json @@ -7,17 +7,41 @@ "type": "dict-expanding", "key": "celaction", "label": "CelAction", - "children": [] + "children": [ + { + "type": "dict-expanding", + "key": "publish", + "label": "Publish plugins", + "is_file": true, + "children": [] + } + ] }, { "type": "dict-expanding", "key": "ftrack", "label": "Ftrack", - "children": [] + "children": [ + { + "type": "dict-expanding", + "key": "publish", + "label": "Publish plugins", + "is_file": true, + "children": [] + } + ] }, { "type": "dict-expanding", "key": "global", "label": "Global", - "children": [] + "children": [ + { + "type": "dict-expanding", + "key": "publish", + "label": "Publish plugins", + "is_file": true, + "children": [] + } + ] }, { "type": "dict-expanding", "key": "maya", @@ -61,7 +85,162 @@ "type": "dict-expanding", "key": "nuke", "label": "Nuke", - "children": [] + "children": [ + { + "type": "dict-expanding", + "key": "create", + "label": "Create plugins", + "is_file": true, + "children": [ + { + "type": "dict", + "key": "CreateWriteRender", + "label": "CreateWriteRender", + "is_group": true, + "children": [ + { + "type": "text-singleline", + "key": "fpath_template", + "label": "Path template", + "default": "{work}/renders/nuke/{subset}/{subset}.{frame}.{ext}" + } + ] + }, { + "type": "dict", + "key": "CreateWritePrerender", + "label": "CreateWritePrerender", + "is_group": true, + "children": [ + { + "type": "text-singleline", + "key": "fpath_template", + "label": "Path template", + "default": "{work}/prerenders/nuke/{subset}/{subset}.{frame}.{ext}" + } + ] + } + ] + }, { + "type": "dict-expanding", + "key": "publish", + "label": "Publish plugins", + "is_file": true, + "children": [ + { + "type": "dict", + "key": "ExtractThumbnail", + "label": "ExtractThumbnail", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled", + "default": false + }, { + "type": "raw-json", + "key": "nodes", + "label": "Nodes" + } + ] + }, { + "type": "dict", + "key": "ValidateNukeWriteKnobs", + "label": "ValidateNukeWriteKnobs", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled", + "default": false + }, { + "type": "raw-json", + "key": "knobs", + "label": "Knobs" + } + ] + }, { + "type": "dict", + "key": "ExtractReviewDataLut", + "label": "ExtractReviewDataLut", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled", + "default": false + } + ] + }, { + "type": "dict", + "key": "ExtractReviewDataMov", + "label": "ExtractReviewDataMov", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled", + "default": true + }, { + "type": "boolean", + "key": "viewer_lut_raw", + "label": "Viewer LUT raw", + "default": false + } + ] + }, { + "type": "dict", + "key": "ExtractSlateFrame", + "label": "ExtractSlateFrame", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "viewer_lut_raw", + "label": "Viewer LUT raw", + "default": false + } + ] + }, { + "type": "dict", + "key": "NukeSubmitDeadline", + "label": "NukeSubmitDeadline", + "is_group": true, + "children": [ + { + "type": "int", + "key": "deadline_priority", + "label": "deadline_priority", + "default": 50 + }, { + "type": "text-singleline", + "key": "deadline_pool", + "label": "deadline_pool", + "default": "" + }, { + "type": "text-singleline", + "key": "deadline_pool_secondary", + "label": "deadline_pool_secondary", + "default": "" + }, { + "type": "int", + "key": "deadline_chunk_size", + "label": "deadline_chunk_size", + "default": 1 + } + ] + } + ] + }, { + "type": "raw-json", + "key": "workfile_build", + "label": "Workfile Build logic", + "is_file": true + } + ] }, { "type": "dict-expanding", "key": "nukestudio", From 1dea23f1cc407cbb1eb78c90c41e36f30f29e240 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 24 Aug 2020 17:09:42 +0200 Subject: [PATCH 175/813] added workfile build to maya plugins --- .../projects_schema/1_plugins_gui_schema.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json index c12825c3ec..4b6583dd63 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json @@ -79,6 +79,11 @@ ] } ] + }, { + "type": "raw-json", + "key": "workfile_build", + "label": "Workfile Build logic", + "is_file": true } ] }, { From f31fb3e03426830a1e1b3d10502230e453fa9e6f Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 24 Aug 2020 17:44:16 +0200 Subject: [PATCH 176/813] Removed com32 objects Refactore - changed names to highlight 'stub' approach Small fixes --- .../photoshop_server_stub.py} | 35 +++++++++++++++---- pype/plugins/photoshop/create/create_image.py | 18 +++++----- pype/plugins/photoshop/load/load_image.py | 12 +++---- .../photoshop/publish/collect_current_file.py | 7 ++-- .../photoshop/publish/collect_instances.py | 12 +++---- .../photoshop/publish/extract_image.py | 22 ++++-------- .../photoshop/publish/extract_review.py | 22 ++++-------- .../photoshop/publish/extract_save_scene.py | 7 +--- .../photoshop/publish/increment_workfile.py | 7 ++-- .../publish/validate_instance_asset.py | 10 ++---- .../photoshop/publish/validate_naming.py | 10 ++---- 11 files changed, 70 insertions(+), 92 deletions(-) rename pype/modules/websocket_server/{clients/photoshop_client.py => stubs/photoshop_server_stub.py} (89%) diff --git a/pype/modules/websocket_server/clients/photoshop_client.py b/pype/modules/websocket_server/stubs/photoshop_server_stub.py similarity index 89% rename from pype/modules/websocket_server/clients/photoshop_client.py rename to pype/modules/websocket_server/stubs/photoshop_server_stub.py index 4f0bca99cc..f798a09b92 100644 --- a/pype/modules/websocket_server/clients/photoshop_client.py +++ b/pype/modules/websocket_server/stubs/photoshop_server_stub.py @@ -7,17 +7,28 @@ import json from collections import namedtuple -class PhotoshopClientStub(): +class PhotoshopServerStub(): """ Stub for calling function on client (Photoshop js) side. Expects that client is already connected (started when avalon menu is opened). + 'self.websocketserver.call' is used as async wrapper """ def __init__(self): self.websocketserver = WebSocketServer.get_instance() self.client = self.websocketserver.get_client() + def open(self, path): + """ + Open file located at 'path' (local). + :param path: file path locally + :return: None + """ + self.websocketserver.call(self.client.call + ('Photoshop.open', path=path) + ) + def read(self, layer, layers_meta=None): """ Parses layer metadata from Headline field of active document @@ -107,9 +118,9 @@ class PhotoshopClientStub(): Create new group (eg. LayerSet) :return: """ - ret = self.websocketserver.call(self.client.call - ('Photoshop.create_group', - name=name)) + ret = self.websocketserver.call(self.client.call + ('Photoshop.create_group', + name=name)) # create group on PS is asynchronous, returns only id layer = {"id": ret, "name": name, "group": True} return namedtuple('Layer', layer.keys())(*layer.values()) @@ -168,6 +179,14 @@ class PhotoshopClientStub(): return res + def is_saved(self): + """ + Returns true if no changes in active document + :return: + """ + return self.websocketserver.call(self.client.call + ('Photoshop.is_saved')) + def save(self): """ Saves active document @@ -223,9 +242,11 @@ class PhotoshopClientStub(): Args: path (str): File path to import. """ - self.websocketserver.call(self.client.call - ('Photoshop.import_smart_object', - path=path)) + res = self.websocketserver.call(self.client.call + ('Photoshop.import_smart_object', + path=path)) + + return self._to_records(res).pop() def replace_smart_object(self, layer, path): """ diff --git a/pype/plugins/photoshop/create/create_image.py b/pype/plugins/photoshop/create/create_image.py index 0a019fe2f8..c1a7d92a2c 100644 --- a/pype/plugins/photoshop/create/create_image.py +++ b/pype/plugins/photoshop/create/create_image.py @@ -1,8 +1,6 @@ from avalon import api from avalon.vendor import Qt -from pype.modules.websocket_server.clients.photoshop_client import ( - PhotoshopClientStub -) +from avalon import photoshop class CreateImage(api.Creator): @@ -17,10 +15,10 @@ class CreateImage(api.Creator): layers = [] create_group = False - photoshopClient = PhotoshopClientStub() + stub = photoshop.stub() if (self.options or {}).get("useSelection"): multiple_instances = False - selection = photoshopClient.get_selected_layers() + selection = stub.get_selected_layers() self.log.info("selection {}".format(selection)) if len(selection) > 1: # Ask user whether to create one image or image per selected @@ -49,7 +47,7 @@ class CreateImage(api.Creator): else: layers.append(item) else: - group = photoshopClient.group_selected_layers(self.name) + group = stub.group_selected_layers(self.name) groups.append(group) elif len(selection) == 1: @@ -66,14 +64,14 @@ class CreateImage(api.Creator): create_group = True if create_group: - group = photoshopClient.create_group(self.name) + group = stub.create_group(self.name) groups.append(group) for layer in layers: - photoshopClient.select_layers([layer]) - group = photoshopClient.group_selected_layers(layer.name) + stub.select_layers([layer]) + group = stub.group_selected_layers(layer.name) groups.append(group) for group in groups: self.data.update({"subset": "image" + group.name}) - photoshopClient.imprint(group, self.data) + stub.imprint(group, self.data) diff --git a/pype/plugins/photoshop/load/load_image.py b/pype/plugins/photoshop/load/load_image.py index 1856155b2a..75c02bb327 100644 --- a/pype/plugins/photoshop/load/load_image.py +++ b/pype/plugins/photoshop/load/load_image.py @@ -1,10 +1,6 @@ from avalon import api, photoshop -from pype.modules.websocket_server.clients.photoshop_client import ( - PhotoshopClientStub -) - -photoshopClient = PhotoshopClientStub() +stub = photoshop.stub() class ImageLoader(api.Loader): @@ -18,7 +14,7 @@ class ImageLoader(api.Loader): def load(self, context, name=None, namespace=None, data=None): with photoshop.maintained_selection(): - layer = photoshopClient.import_smart_object(self.fname) + layer = stub.import_smart_object(self.fname) self[:] = [layer] @@ -34,11 +30,11 @@ class ImageLoader(api.Loader): layer = container.pop("layer") with photoshop.maintained_selection(): - photoshopClient.replace_smart_object( + stub.replace_smart_object( layer, api.get_representation_path(representation) ) - photoshopClient.imprint( + stub.imprint( layer, {"representation": str(representation["_id"])} ) diff --git a/pype/plugins/photoshop/publish/collect_current_file.py b/pype/plugins/photoshop/publish/collect_current_file.py index 7877caa137..3cc3e3f636 100644 --- a/pype/plugins/photoshop/publish/collect_current_file.py +++ b/pype/plugins/photoshop/publish/collect_current_file.py @@ -2,9 +2,7 @@ import os import pyblish.api -from pype.modules.websocket_server.clients.photoshop_client import ( - PhotoshopClientStub -) +from avalon import photoshop class CollectCurrentFile(pyblish.api.ContextPlugin): @@ -15,7 +13,6 @@ class CollectCurrentFile(pyblish.api.ContextPlugin): hosts = ["photoshop"] def process(self, context): - photoshop_client = PhotoshopClientStub() context.data["currentFile"] = os.path.normpath( - photoshop_client.get_active_document_full_name() + photoshop.stub().get_active_document_full_name() ).replace("\\", "/") diff --git a/pype/plugins/photoshop/publish/collect_instances.py b/pype/plugins/photoshop/publish/collect_instances.py index 7e433bc92f..81d1c80bf6 100644 --- a/pype/plugins/photoshop/publish/collect_instances.py +++ b/pype/plugins/photoshop/publish/collect_instances.py @@ -2,9 +2,7 @@ import pythoncom import pyblish.api -from pype.modules.websocket_server.clients.photoshop_client import ( - PhotoshopClientStub -) +from avalon import photoshop class CollectInstances(pyblish.api.ContextPlugin): @@ -29,11 +27,11 @@ class CollectInstances(pyblish.api.ContextPlugin): # can be. pythoncom.CoInitialize() - photoshop_client = PhotoshopClientStub() - layers = photoshop_client.get_layers() - layers_meta = photoshop_client.get_layers_metadata() + stub = photoshop.stub() + layers = stub.get_layers() + layers_meta = stub.get_layers_metadata() for layer in layers: - layer_data = photoshop_client.read(layer, layers_meta) + layer_data = stub.read(layer, layers_meta) # Skip layers without metadata. if layer_data is None: diff --git a/pype/plugins/photoshop/publish/extract_image.py b/pype/plugins/photoshop/publish/extract_image.py index e32444c641..38920b5557 100644 --- a/pype/plugins/photoshop/publish/extract_image.py +++ b/pype/plugins/photoshop/publish/extract_image.py @@ -3,10 +3,6 @@ import os import pype.api from avalon import photoshop -from pype.modules.websocket_server.clients.photoshop_client import ( - PhotoshopClientStub -) - class ExtractImage(pype.api.Extractor): """Produce a flattened image file from instance @@ -25,23 +21,21 @@ class ExtractImage(pype.api.Extractor): self.log.info("Outputting image to {}".format(staging_dir)) # Perform extraction - photoshop_client = PhotoshopClientStub() + stub = photoshop.stub() files = {} with photoshop.maintained_selection(): self.log.info("Extracting %s" % str(list(instance))) with photoshop.maintained_visibility(): # Hide all other layers. - extract_ids = set([ll.id for ll in photoshop_client. + extract_ids = set([ll.id for ll in stub. get_layers_in_layers([instance[0]])]) - for layer in photoshop_client.get_layers(): + for layer in stub.get_layers(): # limit unnecessary calls to client if layer.visible and layer.id not in extract_ids: - photoshop_client.set_visible(layer.id, - False) + stub.set_visible(layer.id, False) if not layer.visible and layer.id in extract_ids: - photoshop_client.set_visible(layer.id, - True) + stub.set_visible(layer.id, True) save_options = [] if "png" in self.formats: @@ -50,16 +44,14 @@ class ExtractImage(pype.api.Extractor): save_options.append('jpg') file_basename = os.path.splitext( - photoshop_client.get_active_document_name() + stub.get_active_document_name() )[0] for extension in save_options: _filename = "{}.{}".format(file_basename, extension) files[extension] = _filename full_filename = os.path.join(staging_dir, _filename) - photoshop_client.saveAs(full_filename, - extension, - True) + stub.saveAs(full_filename, extension, True) representations = [] for extension, filename in files.items(): diff --git a/pype/plugins/photoshop/publish/extract_review.py b/pype/plugins/photoshop/publish/extract_review.py index 806b59341b..6fb50bba9f 100644 --- a/pype/plugins/photoshop/publish/extract_review.py +++ b/pype/plugins/photoshop/publish/extract_review.py @@ -4,10 +4,6 @@ import pype.api import pype.lib from avalon import photoshop -from pype.modules.websocket_server.clients.photoshop_client import ( - PhotoshopClientStub -) - class ExtractReview(pype.api.Extractor): """Produce a flattened image file from all instances.""" @@ -20,7 +16,7 @@ class ExtractReview(pype.api.Extractor): staging_dir = self.staging_dir(instance) self.log.info("Outputting image to {}".format(staging_dir)) - photoshop_client = PhotoshopClientStub() + stub = photoshop.stub() layers = [] for image_instance in instance.context: @@ -30,26 +26,22 @@ class ExtractReview(pype.api.Extractor): # Perform extraction output_image = "{}.jpg".format( - os.path.splitext(photoshop_client.get_active_document_name())[0] + os.path.splitext(stub.get_active_document_name())[0] ) output_image_path = os.path.join(staging_dir, output_image) with photoshop.maintained_visibility(): # Hide all other layers. - extract_ids = set([ll.id for ll in photoshop_client. + extract_ids = set([ll.id for ll in stub. get_layers_in_layers(layers)]) self.log.info("extract_ids {}".format(extract_ids)) - for layer in photoshop_client.get_layers(): + for layer in stub.get_layers(): # limit unnecessary calls to client if layer.visible and layer.id not in extract_ids: - photoshop_client.set_visible(layer.id, - False) + stub.set_visible(layer.id, False) if not layer.visible and layer.id in extract_ids: - photoshop_client.set_visible(layer.id, - True) + stub.set_visible(layer.id, True) - photoshop_client.saveAs(output_image_path, - 'jpg', - True) + stub.saveAs(output_image_path, 'jpg', True) ffmpeg_path = pype.lib.get_ffmpeg_tool_path("ffmpeg") diff --git a/pype/plugins/photoshop/publish/extract_save_scene.py b/pype/plugins/photoshop/publish/extract_save_scene.py index c56e5418c9..63a4b7b7ea 100644 --- a/pype/plugins/photoshop/publish/extract_save_scene.py +++ b/pype/plugins/photoshop/publish/extract_save_scene.py @@ -1,10 +1,6 @@ import pype.api from avalon import photoshop -from pype.modules.websocket_server.clients.photoshop_client import ( - PhotoshopClientStub -) - class ExtractSaveScene(pype.api.Extractor): """Save scene before extraction.""" @@ -15,5 +11,4 @@ class ExtractSaveScene(pype.api.Extractor): families = ["workfile"] def process(self, instance): - photoshop_client = PhotoshopClientStub() - photoshop_client.save() + photoshop.stub().save() diff --git a/pype/plugins/photoshop/publish/increment_workfile.py b/pype/plugins/photoshop/publish/increment_workfile.py index af8ba0b6ae..eca2583595 100644 --- a/pype/plugins/photoshop/publish/increment_workfile.py +++ b/pype/plugins/photoshop/publish/increment_workfile.py @@ -2,9 +2,7 @@ import pyblish.api from pype.action import get_errored_plugins_from_data from pype.lib import version_up -from pype.modules.websocket_server.clients.photoshop_client import ( - PhotoshopClientStub -) +from avalon import photoshop class IncrementWorkfile(pyblish.api.InstancePlugin): @@ -27,7 +25,6 @@ class IncrementWorkfile(pyblish.api.InstancePlugin): ) scene_path = version_up(instance.context.data["currentFile"]) - photoshop_client = PhotoshopClientStub() - photoshop_client.saveAs(scene_path, 'psd', True) + photoshop.stub().saveAs(scene_path, 'psd', True) self.log.info("Incremented workfile to: {}".format(scene_path)) diff --git a/pype/plugins/photoshop/publish/validate_instance_asset.py b/pype/plugins/photoshop/publish/validate_instance_asset.py index aa8d2661ff..f05d9601dd 100644 --- a/pype/plugins/photoshop/publish/validate_instance_asset.py +++ b/pype/plugins/photoshop/publish/validate_instance_asset.py @@ -4,10 +4,6 @@ import pyblish.api import pype.api from avalon import photoshop -from pype.modules.websocket_server.clients.photoshop_client import ( - PhotoshopClientStub -) - class ValidateInstanceAssetRepair(pyblish.api.Action): """Repair the instance asset.""" @@ -27,12 +23,12 @@ class ValidateInstanceAssetRepair(pyblish.api.Action): # Apply pyblish.logic to get the instances for the plug-in instances = pyblish.api.instances_by_plugin(failed, plugin) - photoshop_client = PhotoshopClientStub() + stub = photoshop.stub() for instance in instances: - data = photoshop_client.read(instance[0]) + data = stub.read(instance[0]) data["asset"] = os.environ["AVALON_ASSET"] - photoshop_client.imprint(instance[0], data) + stub.imprint(instance[0], data) class ValidateInstanceAsset(pyblish.api.InstancePlugin): diff --git a/pype/plugins/photoshop/publish/validate_naming.py b/pype/plugins/photoshop/publish/validate_naming.py index c612270802..2483adcb5e 100644 --- a/pype/plugins/photoshop/publish/validate_naming.py +++ b/pype/plugins/photoshop/publish/validate_naming.py @@ -2,10 +2,6 @@ import pyblish.api import pype.api from avalon import photoshop -from pype.modules.websocket_server.clients.photoshop_client import ( - PhotoshopClientStub -) - class ValidateNamingRepair(pyblish.api.Action): """Repair the instance asset.""" @@ -25,14 +21,14 @@ class ValidateNamingRepair(pyblish.api.Action): # Apply pyblish.logic to get the instances for the plug-in instances = pyblish.api.instances_by_plugin(failed, plugin) - photoshop_client = PhotoshopClientStub() + stub = photoshop.stub() for instance in instances: self.log.info("validate_naming instance {}".format(instance)) name = instance.data["name"].replace(" ", "_") instance[0].Name = name - data = photoshop_client.read(instance[0]) + data = stub.read(instance[0]) data["subset"] = "image" + name - photoshop_client.imprint(instance[0], data) + stub.imprint(instance[0], data) return True From dbd50e0ad24e76f9c15a429f43620f0439c37031 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 24 Aug 2020 18:59:51 +0200 Subject: [PATCH 177/813] added global presets --- .../projects_schema/1_plugins_gui_schema.json | 218 +++++++++++++++++- 1 file changed, 215 insertions(+), 3 deletions(-) diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json index 4b6583dd63..458695da18 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json @@ -13,7 +13,52 @@ "key": "publish", "label": "Publish plugins", "is_file": true, - "children": [] + "children": [ + { + "type": "dict", + "key": "ExtractCelactionDeadline", + "label": "ExtractCelactionDeadline", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled", + "default": true + }, { + "type": "text-singleline", + "key": "deadline_department", + "label": "Deadline apartment", + "default": "" + }, { + "type": "int", + "key": "deadline_priority", + "label": "Deadline priority", + "default": 50 + }, { + "type": "text-singleline", + "key": "deadline_pool", + "label": "Deadline pool", + "default": "" + }, { + "type": "text-singleline", + "key": "deadline_pool_secondary", + "label": "Deadline pool (secondary)", + "default": "" + }, { + "type": "text-singleline", + "key": "deadline_group", + "label": "Deadline Group", + "default": "" + }, { + "type": "int", + "key": "deadline_chunk_size", + "label": "Deadline Chunk size", + "default": 10 + } + ] + } + ] } ] }, { @@ -26,7 +71,31 @@ "key": "publish", "label": "Publish plugins", "is_file": true, - "children": [] + "children": [ + { + "type": "dict", + "key": "IntegrateFtrackNote", + "label": "IntegrateFtrackNote", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, { + "type": "text-singleline", + "key": "note_with_intent_template", + "label": "Note with intent template", + "default": "{intent}: {comment}" + }, { + "type": "list-text", + "key": "note_labels", + "label": "Note labels", + "default": [] + } + ] + } + ] } ] }, { @@ -39,7 +108,150 @@ "key": "publish", "label": "Publish plugins", "is_file": true, - "children": [] + "children": [ + { + "type": "dict", + "key": "IntegrateMasterVersion", + "label": "IntegrateMasterVersion", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + } + ] + }, { + "type": "dict", + "key": "ExtractJpegEXR", + "label": "ExtractJpegEXR", + "is_group": true, + "children": [ + { + "type": "dict-invisible", + "key": "ffmpeg_args", + "children": [ + { + "type": "list-text", + "key": "input", + "label": "FFmpeg input arguments", + "default": [] + }, { + "type": "list-text", + "key": "output", + "label": "FFmpeg output arguments", + "default": [] + } + ] + } + ] + }, { + "type": "dict", + "key": "ExtractReview", + "label": "ExtractReview", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, { + "type": "raw-json", + "key": "profiles", + "label": "Profiles" + } + ] + }, { + "type": "dict", + "key": "ExtractBurnin", + "label": "ExtractBurnin", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, { + "type": "dict-expanding", + "key": "options", + "label": "Burnin formating options", + "children": [ + { + "type": "int", + "key": "font_size", + "label": "Font size", + "default": 42 + }, { + "type": "int", + "key": "opacity", + "label": "Font opacity", + "default": 1 + }, { + "type": "int", + "key": "bg_opacity", + "label": "Background opacity", + "default": 1 + }, { + "type": "int", + "key": "x_offset", + "label": "X Offset", + "default": 5 + }, { + "type": "int", + "key": "y_offset", + "label": "Y Offset", + "default": 5 + }, { + "type": "int", + "key": "bg_padding", + "label": "Padding aroung text", + "default": 5 + } + ] + }, { + "type": "raw-json", + "key": "profiles", + "label": "Burnin profiles" + } + ] + }, { + "type": "dict", + "key": "IntegrateAssetNew", + "label": "IntegrateAssetNew", + "is_group": true, + "children": [ + { + "type": "raw-json", + "key": "template_name_profiles", + "label": "template_name_profiles" + } + ] + }, { + "type": "dict", + "key": "ProcessSubmittedJobOnFarm", + "label": "ProcessSubmittedJobOnFarm", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, { + "type": "text-singleline", + "key": "deadline_department", + "label": "Deadline department" + }, { + "type": "text-singleline", + "key": "deadline_pool", + "label": "Deadline Pool" + }, { + "type": "text-singleline", + "key": "deadline_group", + "label": "Deadline Group" + } + ] + } + ] } ] }, { From 4301c5dcdcfee138deee8544c065b132113a3a89 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 24 Aug 2020 19:04:06 +0200 Subject: [PATCH 178/813] updated maya plugins --- .../projects_schema/1_plugins_gui_schema.json | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json index 458695da18..2b4dfafc56 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json @@ -275,6 +275,15 @@ "type": "boolean", "key": "enabled", "label": "Enabled" + }, { + "type": "text-singleline", + "key": "material_file", + "label": "Material File" + }, { + "type": "text-singleline", + "key": "regex", + "label": "Validation regex", + "default": "(.*)_(\\d)*_(?P.*)_(GEO)" } ] }, { @@ -289,6 +298,35 @@ "label": "Enabled" } ] + }, { + "type": "dict", + "key": "ValidateShaderName", + "label": "ValidateShaderName", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, { + "type": "text-singleline", + "key": "regex", + "label": "Validation regex", + "default": "(?P.*)_(.*)_SHD" + } + ] + }, { + "type": "dict", + "key": "ValidateMeshHasOverlappingUVs", + "label": "ValidateMeshHasOverlappingUVs", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + } + ] } ] }, { From 5bdee32e7ef7d7fec0909aad23a9703f8e82679e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 24 Aug 2020 19:12:46 +0200 Subject: [PATCH 179/813] few minor fixes --- .../config_setting/config_setting/widgets/base.py | 3 +-- .../config_setting/config_setting/widgets/inputs.py | 10 +--------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/base.py b/pype/tools/config_setting/config_setting/widgets/base.py index a59b497b2b..74e0543870 100644 --- a/pype/tools/config_setting/config_setting/widgets/base.py +++ b/pype/tools/config_setting/config_setting/widgets/base.py @@ -307,7 +307,7 @@ class ProjectWidget(QtWidgets.QWidget): self.reset() def reset(self): - values = config.global_project_configurations() + values = {"project": config.global_project_configurations()} schema = lib.gui_schema("projects_schema", "0_project_gui_schema") self.keys = schema.get("keys", []) self.add_children_gui(schema, values) @@ -316,7 +316,6 @@ class ProjectWidget(QtWidgets.QWidget): def add_children_gui(self, child_configuration, values): item_type = child_configuration["type"] klass = lib.TypeToKlass.types.get(item_type) - item = klass( child_configuration, values, self.keys, self ) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 61c6385323..0f1ef844d3 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1832,9 +1832,6 @@ class DictInvisible(QtWidgets.QWidget, ConfigWidget): self.input_fields = [] - if "key" not in input_data: - print(json.dumps(input_data, indent=4)) - self.key = input_data["key"] self.keys = list(parent_keys) self.keys.append(self.key) @@ -1869,13 +1866,8 @@ class DictInvisible(QtWidgets.QWidget, ConfigWidget): def add_children_gui(self, child_configuration, values): item_type = child_configuration["type"] - if item_type == "schema": - for _schema in child_configuration["children"]: - children = config.gui_schema(_schema) - self.add_children_gui(children, values) - return - klass = TypeToKlass.types.get(item_type) + item = klass( child_configuration, values, self.keys, self ) From a575ed37291fe80ce448f99085f0c51760ee0724 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 24 Aug 2020 19:17:06 +0200 Subject: [PATCH 180/813] minor fix in raw json --- .../projects_schema/1_plugins_gui_schema.json | 2 +- pype/tools/config_setting/config_setting/widgets/inputs.py | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json index 2b4dfafc56..e6582d82b1 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json @@ -570,7 +570,7 @@ "label": "Folder", "default": "takes" }, { - "type": "text-singleline", + "type": "int", "key": "steps", "label": "Steps", "default": 20 diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 0f1ef844d3..6100087ac7 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -719,6 +719,8 @@ class RawJsonInput(QtWidgets.QPlainTextEdit): self.is_valid = None def set_value(self, value, *, default_value=False): + if not isinstance(value, str): + value = json.dumps(value, indent=4) self.setPlainText(value) def setPlainText(self, *args, **kwargs): @@ -808,7 +810,7 @@ class RawJsonWidget(QtWidgets.QWidget, InputWidget): value = self.value_from_values(values) if value is not NOT_SET: - self.text_input.setPlainText(value) + self.text_input.set_value(value) self.default_value = self.item_value() self.override_value = None @@ -816,7 +818,7 @@ class RawJsonWidget(QtWidgets.QWidget, InputWidget): self.text_input.textChanged.connect(self._on_value_change) def set_value(self, value, *, default_value=False): - self.text_input.setPlainText(value) + self.text_input.set_value(value) if default_value: self.default_value = self.item_value() self._on_value_change() From 49a4e73fc65193c73d430d87409b641ac266dd7b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 24 Aug 2020 19:26:01 +0200 Subject: [PATCH 181/813] removed ftrack overrides as not ready yet --- .../config_gui_schema/projects_schema/0_project_gui_schema.json | 1 - 1 file changed, 1 deletion(-) diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/0_project_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/0_project_gui_schema.json index c281ce3c79..10641d5eda 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/0_project_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/0_project_gui_schema.json @@ -5,7 +5,6 @@ { "type": "schema", "children": [ - "1_ftrack_projects_gui_schema", "1_plugins_gui_schema" ] } From b648b3c72e1199a3f282f8030d9d5c47cfb35681 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 25 Aug 2020 08:35:09 +0100 Subject: [PATCH 182/813] Add "preview" to image plane representations --- pype/plugins/maya/load/load_image_plane.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pype/plugins/maya/load/load_image_plane.py b/pype/plugins/maya/load/load_image_plane.py index 08f7c99156..17a6866f80 100644 --- a/pype/plugins/maya/load/load_image_plane.py +++ b/pype/plugins/maya/load/load_image_plane.py @@ -12,7 +12,7 @@ class ImagePlaneLoader(api.Loader): families = ["plate", "render"] label = "Create imagePlane on selected camera." - representations = ["mov", "exr"] + representations = ["mov", "exr", "preview"] icon = "image" color = "orange" @@ -83,7 +83,8 @@ class ImagePlaneLoader(api.Loader): image_plane_shape.frameOut.set(end_frame) image_plane_shape.useFrameExtension.set(1) - if context["representation"]["name"] == "mov": + movie_representations = ["mov", "preview"] + if context["representation"]["name"] in movie_representations: # Need to get "type" by string, because its a method as well. pc.Attribute(image_plane_shape + ".type").set(2) From e35ef732b84f8d7e9603dc50c7aca70e3b957920 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 25 Aug 2020 10:49:02 +0200 Subject: [PATCH 183/813] fixed content margins on right side --- .../config_setting/config_setting/widgets/inputs.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 6100087ac7..2cb1be3c63 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -932,7 +932,7 @@ class TextListSubWidget(QtWidgets.QWidget, ConfigWidget): self.setObjectName("TextListSubWidget") layout = QtWidgets.QVBoxLayout(self) - layout.setContentsMargins(5, 5, 5, 5) + layout.setContentsMargins(5, 5, 0, 5) layout.setSpacing(5) self.setLayout(layout) @@ -1251,7 +1251,7 @@ class ModifiableDictSubWidget(QtWidgets.QWidget, ConfigWidget): self.setObjectName("ModifiableDictSubWidget") layout = QtWidgets.QVBoxLayout(self) - layout.setContentsMargins(5, 5, 5, 5) + layout.setContentsMargins(5, 5, 0, 5) layout.setSpacing(5) self.setLayout(layout) @@ -1478,7 +1478,7 @@ class DictExpandWidget(ExpandingWidget, ConfigWidget): content_widget.setVisible(False) content_layout = QtWidgets.QVBoxLayout(content_widget) - content_layout.setContentsMargins(3, 3, 3, 3) + content_layout.setContentsMargins(3, 3, 0, 3) self.set_content_widget(content_widget) @@ -1655,7 +1655,7 @@ class DictWidget(QtWidgets.QWidget, ConfigWidget): content_widget = QtWidgets.QWidget(body_widget) content_layout = QtWidgets.QVBoxLayout(content_widget) - content_layout.setContentsMargins(3, 3, 3, 3) + content_layout.setContentsMargins(3, 3, 0, 3) body_layout = QtWidgets.QVBoxLayout(body_widget) body_layout.setContentsMargins(0, 0, 0, 0) @@ -1666,7 +1666,7 @@ class DictWidget(QtWidgets.QWidget, ConfigWidget): self.setAttribute(QtCore.Qt.WA_StyledBackground) main_layout = QtWidgets.QHBoxLayout(self) - main_layout.setContentsMargins(5, 5, 5, 5) + main_layout.setContentsMargins(5, 5, 0, 5) main_layout.setSpacing(0) main_layout.addWidget(body_widget) From 7cd5b58b653b51379d675867c69a428a2a8b2194 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 25 Aug 2020 10:50:42 +0200 Subject: [PATCH 184/813] modified style --- .../config_setting/style/style.css | 34 +++++++++---------- .../config_setting/widgets/inputs.py | 3 +- .../config_setting/widgets/widgets.py | 4 +-- 3 files changed, 20 insertions(+), 21 deletions(-) diff --git a/pype/tools/config_setting/config_setting/style/style.css b/pype/tools/config_setting/config_setting/style/style.css index a0c5db86a1..9bd74de282 100644 --- a/pype/tools/config_setting/config_setting/style/style.css +++ b/pype/tools/config_setting/config_setting/style/style.css @@ -2,6 +2,7 @@ QWidget { color: #bfccd6; background-color: #293742; font-size: 12px; + border-radius: 0px; } QCheckBox::indicator { @@ -58,44 +59,43 @@ QPushButton[btn-type="expand-toggle"] { } #RawJsonInput[state="invalid"] { - border-color: #ff5511; + border-left-color: #ff5511; } #DictKey[state="modified"] { - border-color: #137cbd; + border-left-color: #137cbd; } #DictKey[state="overriden"] { - border-color: #00f; + border-left-color: #00f; } #DictKey[state="overriden-modified"] { - border-color: #0f0; + border-left-color: #0f0; } -#ExpandLabel { +#DictLabel { background: transparent; + font-weight: bold; } -#DictExpandWidget, #ModifiableDict, #ExpandingWidget { - border: 1px solid #455c6e; - border-radius: 3px; +#ExpandingWidget, #ModifiableDict, #DictWidget { + border-left: 3px solid #455c6e; background: #1d272f; } -#DictExpandWidget[state="child-modified"], #ModifiableDict[state="child-modified"] { - border-color: #137cbd; +#ExpandingWidget[state="child-modified"], #ModifiableDict[state="child-modified"], #DictWidget[state="child-modified"] { + border-left-color: #137cbd; } -#DictExpandWidget[state="child-overriden"], #ModifiableDict[state="child-overriden"] { - border-color: #ff8c1a; +#ExpandingWidget[state="child-overriden"], #ModifiableDict[state="child-overriden"], #DictWidget[state="child-overriden"] { + border-left-color: #ff8c1a; } -#DictExpandWidget[state="child-overriden-modified"], #ModifiableDict[state="child-overriden-modified"] { - border-color: #00b386; +#ExpandingWidget[state="child-overriden-modified"], #ModifiableDict[state="child-overriden-modified"], #DictWidget[state="child-overriden-modified"] { + border-left-color: #00b386; } #TextListSubWidget { - border: 1px solid #455c6e; - border-radius: 3px; - background: #1d272f; + border-left: 3px solid #455c6e; + /* background: #1d272f; */ } diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 2cb1be3c63..9023d36826 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1472,7 +1472,6 @@ class DictExpandWidget(ExpandingWidget, ConfigWidget): self._child_state = None super(DictExpandWidget, self).__init__(input_data["label"], parent) - self.setObjectName("DictExpandWidget") content_widget = QtWidgets.QWidget(self) content_widget.setVisible(False) @@ -1651,7 +1650,7 @@ class DictWidget(QtWidgets.QWidget, ConfigWidget): label_widget = QtWidgets.QLabel( input_data["label"], parent=body_widget ) - label_widget.setObjectName("ExpandLabel") + label_widget.setObjectName("DictLabel") content_widget = QtWidgets.QWidget(body_widget) content_layout = QtWidgets.QVBoxLayout(content_widget) diff --git a/pype/tools/config_setting/config_setting/widgets/widgets.py b/pype/tools/config_setting/config_setting/widgets/widgets.py index 3d5528e17a..a15edf58ff 100644 --- a/pype/tools/config_setting/config_setting/widgets/widgets.py +++ b/pype/tools/config_setting/config_setting/widgets/widgets.py @@ -54,7 +54,7 @@ class ExpandingWidget(QtWidgets.QWidget): button_toggle.setChecked(False) label_widget = QtWidgets.QLabel(label, parent=top_part) - label_widget.setObjectName("ExpandLabel") + label_widget.setObjectName("DictLabel") layout = QtWidgets.QHBoxLayout(top_part) layout.setContentsMargins(0, 0, 0, 0) @@ -74,7 +74,7 @@ class ExpandingWidget(QtWidgets.QWidget): def set_content_widget(self, content_widget): main_layout = QtWidgets.QVBoxLayout(self) - main_layout.setContentsMargins(9, 9, 9, 9) + main_layout.setContentsMargins(9, 9, 0, 9) content_widget.setVisible(False) From f4cdb85c14ef49987d4e154d28d567fbaeba687e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 25 Aug 2020 10:57:48 +0200 Subject: [PATCH 185/813] fixed project list styles --- .../config_setting/config_setting/style/style.css | 12 ++++++++++-- .../config_setting/config_setting/widgets/base.py | 4 +++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/pype/tools/config_setting/config_setting/style/style.css b/pype/tools/config_setting/config_setting/style/style.css index 9bd74de282..2ea765f442 100644 --- a/pype/tools/config_setting/config_setting/style/style.css +++ b/pype/tools/config_setting/config_setting/style/style.css @@ -56,6 +56,16 @@ QPushButton[btn-type="text-list"]:hover { QPushButton[btn-type="expand-toggle"] { background: transparent; + background: #1d272f; +} + +#ProjectListWidget QListView { + border: 1px solid #aaaaaa; + background: #1d272f; +} +#ProjectListWidget QLabel { + background: transparent; + font-weight: bold; } #RawJsonInput[state="invalid"] { @@ -80,7 +90,6 @@ QPushButton[btn-type="expand-toggle"] { #ExpandingWidget, #ModifiableDict, #DictWidget { border-left: 3px solid #455c6e; - background: #1d272f; } #ExpandingWidget[state="child-modified"], #ModifiableDict[state="child-modified"], #DictWidget[state="child-modified"] { @@ -97,5 +106,4 @@ QPushButton[btn-type="expand-toggle"] { #TextListSubWidget { border-left: 3px solid #455c6e; - /* background: #1d272f; */ } diff --git a/pype/tools/config_setting/config_setting/widgets/base.py b/pype/tools/config_setting/config_setting/widgets/base.py index 74e0543870..5f48b416c1 100644 --- a/pype/tools/config_setting/config_setting/widgets/base.py +++ b/pype/tools/config_setting/config_setting/widgets/base.py @@ -140,8 +140,10 @@ class ProjectListWidget(QtWidgets.QWidget): self.current_project = None super(ProjectListWidget, self).__init__(parent) + self.setObjectName("ProjectListWidget") label_widget = QtWidgets.QLabel("Projects") + project_list = ProjectListView(self) project_list.setModel(QtGui.QStandardItemModel()) @@ -282,7 +284,7 @@ class ProjectWidget(QtWidgets.QWidget): configurations_widget = QtWidgets.QWidget() configurations_layout = QtWidgets.QVBoxLayout(configurations_widget) - configurations_layout.setContentsMargins(0, 0, 0, 0) + configurations_layout.setContentsMargins(0, 0, 5, 0) configurations_layout.setSpacing(0) configurations_layout.addWidget(scroll_widget, 1) From 62ed17c597398fb6212ea48fd4fb230300d234b7 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 25 Aug 2020 10:58:11 +0200 Subject: [PATCH 186/813] removed unused imports --- pype/tools/config_setting/config_setting/widgets/inputs.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 9023d36826..0f106c6408 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1,8 +1,6 @@ import json from Qt import QtWidgets, QtCore, QtGui -from pype.api import config from .widgets import ( - ClickableWidget, ExpandingWidget, ModifiedIntSpinBox, ModifiedFloatSpinBox From e9551c7fc476e788f70b4b3c86371e6e0e8491ac Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 25 Aug 2020 11:03:37 +0200 Subject: [PATCH 187/813] group widgets has borders --- pype/tools/config_setting/config_setting/style/style.css | 4 ++++ pype/tools/config_setting/config_setting/widgets/base.py | 6 ++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/pype/tools/config_setting/config_setting/style/style.css b/pype/tools/config_setting/config_setting/style/style.css index 2ea765f442..aa1f6293d7 100644 --- a/pype/tools/config_setting/config_setting/style/style.css +++ b/pype/tools/config_setting/config_setting/style/style.css @@ -59,6 +59,10 @@ QPushButton[btn-type="expand-toggle"] { background: #1d272f; } +#GroupWidget { + border: 1px solid #aaaaaa; +} + #ProjectListWidget QListView { border: 1px solid #aaaaaa; background: #1d272f; diff --git a/pype/tools/config_setting/config_setting/widgets/base.py b/pype/tools/config_setting/config_setting/widgets/base.py index 5f48b416c1..4fe3866096 100644 --- a/pype/tools/config_setting/config_setting/widgets/base.py +++ b/pype/tools/config_setting/config_setting/widgets/base.py @@ -20,6 +20,7 @@ class StudioWidget(QtWidgets.QWidget): self.input_fields = [] scroll_widget = QtWidgets.QScrollArea(self) + scroll_widget.setObjectName("GroupWidget") content_widget = QtWidgets.QWidget(scroll_widget) content_layout = QtWidgets.QVBoxLayout(content_widget) content_layout.setContentsMargins(3, 3, 3, 3) @@ -43,7 +44,7 @@ class StudioWidget(QtWidgets.QWidget): footer_layout.addWidget(save_btn, 0) layout = QtWidgets.QVBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) + layout.setContentsMargins(5, 0, 5, 0) layout.setSpacing(0) self.setLayout(layout) @@ -261,6 +262,7 @@ class ProjectWidget(QtWidgets.QWidget): self.input_fields = [] scroll_widget = QtWidgets.QScrollArea(self) + scroll_widget.setObjectName("GroupWidget") content_widget = QtWidgets.QWidget(scroll_widget) content_layout = QtWidgets.QVBoxLayout(content_widget) content_layout.setContentsMargins(3, 3, 3, 3) @@ -284,7 +286,7 @@ class ProjectWidget(QtWidgets.QWidget): configurations_widget = QtWidgets.QWidget() configurations_layout = QtWidgets.QVBoxLayout(configurations_widget) - configurations_layout.setContentsMargins(0, 0, 5, 0) + configurations_layout.setContentsMargins(5, 0, 5, 0) configurations_layout.setSpacing(0) configurations_layout.addWidget(scroll_widget, 1) From b51e24318efb2dcb1bc0a3145877fed73bafda69 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 25 Aug 2020 11:14:17 +0200 Subject: [PATCH 188/813] overriden-modified has same color as modified --- .../config_setting/style/style.css | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/pype/tools/config_setting/config_setting/style/style.css b/pype/tools/config_setting/config_setting/style/style.css index aa1f6293d7..961bfa0d03 100644 --- a/pype/tools/config_setting/config_setting/style/style.css +++ b/pype/tools/config_setting/config_setting/style/style.css @@ -25,7 +25,7 @@ QLabel[state="modified"] { } QLabel[state="overriden-modified"] { - color: #00b386; + color: #137cbd; } QLabel[state="overriden"] { @@ -37,13 +37,18 @@ QWidget[input-state="modified"] { } QWidget[input-state="overriden-modified"] { - border-color: #00b386; + border-color: #137cbd; } QWidget[input-state="overriden"] { border-color: #ff8c1a; } +QPushButton { + border: 1px solid #aaaaaa; + border-radius: 3px; + padding: 5px; +} QPushButton[btn-type="text-list"] { border: 1px solid #bfccd6; border-radius: 10px; @@ -93,19 +98,24 @@ QPushButton[btn-type="expand-toggle"] { } #ExpandingWidget, #ModifiableDict, #DictWidget { - border-left: 3px solid #455c6e; + border-style: solid; + border-color: #455c6e; + border-left-width: 3px; + border-bottom-width: 0px; + border-right-width: 0px; + border-top-width: 0px; } #ExpandingWidget[state="child-modified"], #ModifiableDict[state="child-modified"], #DictWidget[state="child-modified"] { - border-left-color: #137cbd; + border-color: #137cbd; } #ExpandingWidget[state="child-overriden"], #ModifiableDict[state="child-overriden"], #DictWidget[state="child-overriden"] { - border-left-color: #ff8c1a; + border-color: #ff8c1a; } #ExpandingWidget[state="child-overriden-modified"], #ModifiableDict[state="child-overriden-modified"], #DictWidget[state="child-overriden-modified"] { - border-left-color: #00b386; + border-color: #137cbd; } #TextListSubWidget { From 43d2a70d0d10a7872fd8d86ce854ec5dde96ac8c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 25 Aug 2020 11:32:21 +0200 Subject: [PATCH 189/813] inputs has different background --- pype/tools/config_setting/config_setting/style/style.css | 1 + pype/tools/config_setting/config_setting/widgets/inputs.py | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pype/tools/config_setting/config_setting/style/style.css b/pype/tools/config_setting/config_setting/style/style.css index 961bfa0d03..b040425a02 100644 --- a/pype/tools/config_setting/config_setting/style/style.css +++ b/pype/tools/config_setting/config_setting/style/style.css @@ -14,6 +14,7 @@ QCheckBox::indicator:focus { QLineEdit, QSpinBox, QDoubleSpinBox, QPlainTextEdit { border: 1px solid #aaaaaa; border-radius: 3px; + background: #1d272f; } QLineEdit:focus, QSpinBox:focus, QDoubleSpinBox:focus, QPlainTextEdit:focus { diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 0f106c6408..c64e0e43cc 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -796,8 +796,8 @@ class RawJsonWidget(QtWidgets.QWidget, InputWidget): if not label_widget: label = input_data["label"] label_widget = QtWidgets.QLabel(label) - layout.addWidget(label_widget) - layout.addWidget(self.text_input) + layout.addWidget(label_widget, 0) + layout.addWidget(self.text_input, 1) self.label_widget = label_widget From 509776ddc074af2571716308a2eb7fea5ee9d8b3 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 25 Aug 2020 11:16:07 +0100 Subject: [PATCH 190/813] Optional camera creation on image plane loading. --- pype/plugins/maya/load/load_image_plane.py | 38 +++++++++++++--------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/pype/plugins/maya/load/load_image_plane.py b/pype/plugins/maya/load/load_image_plane.py index 08f7c99156..7ec56aab3a 100644 --- a/pype/plugins/maya/load/load_image_plane.py +++ b/pype/plugins/maya/load/load_image_plane.py @@ -29,6 +29,8 @@ class ImagePlaneLoader(api.Loader): # Getting camera from selection. selection = pc.ls(selection=True) + camera = None + if len(selection) > 1: QtWidgets.QMessageBox.critical( None, @@ -39,25 +41,29 @@ class ImagePlaneLoader(api.Loader): return if len(selection) < 1: - QtWidgets.QMessageBox.critical( + result = QtWidgets.QMessageBox.critical( None, "Error!", - "No camera selected.", - QtWidgets.QMessageBox.Ok + "No camera selected. Do you want to create a camera?", + QtWidgets.QMessageBox.Ok, + QtWidgets.QMessageBox.Cancel ) - return - - relatives = pc.listRelatives(selection[0], shapes=True) - if not pc.ls(relatives, type="camera"): - QtWidgets.QMessageBox.critical( - None, - "Error!", - "Selected node is not a camera.", - QtWidgets.QMessageBox.Ok - ) - return - - camera = selection[0] + if result == QtWidgets.QMessageBox.Ok: + camera = pc.createNode("camera") + else: + return + else: + relatives = pc.listRelatives(selection[0], shapes=True) + if pc.ls(relatives, type="camera"): + camera = selection[0] + else: + QtWidgets.QMessageBox.critical( + None, + "Error!", + "Selected node is not a camera.", + QtWidgets.QMessageBox.Ok + ) + return try: camera.displayResolution.set(1) From 34416f46ddeeb16ec44dee7873341a14da9e38ee Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 25 Aug 2020 13:20:07 +0200 Subject: [PATCH 191/813] adde more transparency --- .../config_setting/config_setting/style/style.css | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/pype/tools/config_setting/config_setting/style/style.css b/pype/tools/config_setting/config_setting/style/style.css index b040425a02..e7224533da 100644 --- a/pype/tools/config_setting/config_setting/style/style.css +++ b/pype/tools/config_setting/config_setting/style/style.css @@ -11,15 +11,18 @@ QCheckBox::indicator:focus { color: #ff0000; } -QLineEdit, QSpinBox, QDoubleSpinBox, QPlainTextEdit { +QLineEdit, QSpinBox, QDoubleSpinBox, QPlainTextEdit, QTextEdit { border: 1px solid #aaaaaa; border-radius: 3px; - background: #1d272f; + background-color: #1d272f; } -QLineEdit:focus, QSpinBox:focus, QDoubleSpinBox:focus, QPlainTextEdit:focus { +QLineEdit:focus, QSpinBox:focus, QDoubleSpinBox:focus, QPlainTextEdit:focus, QTextEdit:focus { border: 1px solid #ffffff; } +QLabel, QToolButton { + background: transparent; +} QLabel[state="modified"] { color: #137cbd; @@ -61,7 +64,6 @@ QPushButton[btn-type="text-list"]:hover { } QPushButton[btn-type="expand-toggle"] { - background: transparent; background: #1d272f; } @@ -94,7 +96,6 @@ QPushButton[btn-type="expand-toggle"] { } #DictLabel { - background: transparent; font-weight: bold; } From 1390637bb1ce6949935c97b862eb72a011e4f822 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 25 Aug 2020 13:21:23 +0200 Subject: [PATCH 192/813] tried to extend raw json input --- .../config_setting/config_setting/widgets/inputs.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index c64e0e43cc..4e63a4a501 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -752,6 +752,11 @@ class RawJsonInput(QtWidgets.QPlainTextEdit): self.setProperty("state", state) self.style().polish(self) + def resizeEvent(self, event): + self.updateGeometry() + super().resizeEvent(event) + + def value(self): return self.toPlainText() @@ -791,7 +796,11 @@ class RawJsonWidget(QtWidgets.QWidget, InputWidget): layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) - self.text_input = RawJsonInput() + self.text_input = RawJsonInput(self) + self.text_input.setSizePolicy( + QtWidgets.QSizePolicy.Minimum, + QtWidgets.QSizePolicy.MinimumExpanding + ) if not label_widget: label = input_data["label"] From cdc407f7ad341132d1b4b1d55809901b37cfa60f Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 25 Aug 2020 13:24:48 +0200 Subject: [PATCH 193/813] Hound fixes --- pype/config.py | 4 +++- pype/tools/config_setting/config_setting/widgets/inputs.py | 1 - pype/tools/config_setting/config_setting/widgets/tests.py | 2 -- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/pype/config.py b/pype/config.py index c65e336e00..416704649c 100644 --- a/pype/config.py +++ b/pype/config.py @@ -188,6 +188,8 @@ def project_presets(project_name=None, **kwargs): if not project_name: project_name = os.environ.get("AVALON_PROJECT") - project_overrides = project_configurations_overrides(project_name, **kwargs) + project_overrides = project_configurations_overrides( + project_name, **kwargs + ) return apply_overrides(global_presets, project_overrides) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 4e63a4a501..19286a19b5 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -756,7 +756,6 @@ class RawJsonInput(QtWidgets.QPlainTextEdit): self.updateGeometry() super().resizeEvent(event) - def value(self): return self.toPlainText() diff --git a/pype/tools/config_setting/config_setting/widgets/tests.py b/pype/tools/config_setting/config_setting/widgets/tests.py index 53b67de3a1..16c97a85ef 100644 --- a/pype/tools/config_setting/config_setting/widgets/tests.py +++ b/pype/tools/config_setting/config_setting/widgets/tests.py @@ -63,8 +63,6 @@ class AddibleComboBox(QtWidgets.QComboBox): # self.addItems([text]) # index = self.findText(text) - - def populate(self, items): self.clear() # self.addItems([""]) # ensure first item is placeholder From 6d31920609220ad8e584a7d96f34a405314d660b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 25 Aug 2020 13:31:05 +0200 Subject: [PATCH 194/813] added hovering on labels --- .../config_setting/style/style.css | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/pype/tools/config_setting/config_setting/style/style.css b/pype/tools/config_setting/config_setting/style/style.css index e7224533da..af4808b443 100644 --- a/pype/tools/config_setting/config_setting/style/style.css +++ b/pype/tools/config_setting/config_setting/style/style.css @@ -24,17 +24,14 @@ QLabel, QToolButton { background: transparent; } -QLabel[state="modified"] { - color: #137cbd; -} +QLabel:hover {color: #ffffff;} -QLabel[state="overriden-modified"] { - color: #137cbd; -} - -QLabel[state="overriden"] { - color: #ff8c1a; -} +QLabel[state="modified"] {color: #137cbd;} +QLabel[state="modified"]:hover {color: #1798e8;} +QLabel[state="overriden-modified"] {color: #137cbd;} +QLabel[state="overriden-modified"]:hover {color: #1798e8;} +QLabel[state="overriden"] {color: #ff8c1a;} +QLabel[state="overriden"]:hover {color: #ffa64d;} QWidget[input-state="modified"] { border-color: #137cbd; From 422930ea8782a68f5ed008b9be55c1c72deed9aa Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 25 Aug 2020 13:54:17 +0200 Subject: [PATCH 195/813] fixed raw json height sizes --- .../config_setting/widgets/inputs.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 19286a19b5..bd945f18e8 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -716,6 +716,21 @@ class RawJsonInput(QtWidgets.QPlainTextEdit): self.is_valid = None + def sizeHint(self): + document = self.document() + layout = document.documentLayout() + + height = document.documentMargin() + 2 * self.frameWidth() + 1 + block = document.begin() + while block != document.end(): + height += layout.blockBoundingRect(block).height() + block = block.next() + + value = super().sizeHint() + value.setHeight(height) + + return value + def set_value(self, value, *, default_value=False): if not isinstance(value, str): value = json.dumps(value, indent=4) From 1ac0c781338ab5734016cab12b8a3ae771fb3dfa Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 25 Aug 2020 14:16:21 +0200 Subject: [PATCH 196/813] text list widget does not have modification line next to it --- .../config_setting/config_setting/style/style.css | 4 ---- .../config_setting/config_setting/widgets/inputs.py | 10 +++++----- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/pype/tools/config_setting/config_setting/style/style.css b/pype/tools/config_setting/config_setting/style/style.css index af4808b443..0099e6ed76 100644 --- a/pype/tools/config_setting/config_setting/style/style.css +++ b/pype/tools/config_setting/config_setting/style/style.css @@ -116,7 +116,3 @@ QPushButton[btn-type="expand-toggle"] { #ExpandingWidget[state="child-overriden-modified"], #ModifiableDict[state="child-overriden-modified"], #DictWidget[state="child-overriden-modified"] { border-color: #137cbd; } - -#TextListSubWidget { - border-left: 3px solid #455c6e; -} diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index bd945f18e8..0d07383d3c 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -953,8 +953,8 @@ class TextListSubWidget(QtWidgets.QWidget, ConfigWidget): self.setObjectName("TextListSubWidget") layout = QtWidgets.QVBoxLayout(self) - layout.setContentsMargins(5, 5, 0, 5) - layout.setSpacing(5) + layout.setContentsMargins(0, 5, 0, 5) + layout.setSpacing(3) self.setLayout(layout) self.input_fields = [] @@ -1088,9 +1088,9 @@ class TextListWidget(QtWidgets.QWidget, InputWidget): layout.addWidget(label_widget) self.label_widget = label_widget - # keys = list(parent_keys) - # keys.append(input_data["key"]) - # self.keys = keys + keys = list(parent_keys) + keys.append(input_data["key"]) + self.keys = keys self.value_widget = TextListSubWidget( input_data, values, parent_keys, self From b7807f81f05697fc70a8a65a5cd2877a4720717e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 25 Aug 2020 15:07:25 +0200 Subject: [PATCH 197/813] fix super --- pype/tools/config_setting/config_setting/widgets/inputs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 0d07383d3c..e9104e6c0c 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -726,7 +726,7 @@ class RawJsonInput(QtWidgets.QPlainTextEdit): height += layout.blockBoundingRect(block).height() block = block.next() - value = super().sizeHint() + value = super(RawJsonInput, self).sizeHint() value.setHeight(height) return value @@ -769,7 +769,7 @@ class RawJsonInput(QtWidgets.QPlainTextEdit): def resizeEvent(self, event): self.updateGeometry() - super().resizeEvent(event) + super(RawJsonInput, self).resizeEvent(event) def value(self): return self.toPlainText() From 6a585b082a744157addb859487cc2f06d27284b2 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 25 Aug 2020 15:29:47 +0200 Subject: [PATCH 198/813] renamed default_value to global_value --- .../config_setting/widgets/inputs.py | 122 +++++++++--------- 1 file changed, 61 insertions(+), 61 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index e9104e6c0c..140d67c0ad 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -164,18 +164,18 @@ class BooleanWidget(QtWidgets.QWidget, InputWidget): if value is not NOT_SET: self.checkbox.setChecked(value) - self.default_value = self.item_value() + self.global_value = self.item_value() self.override_value = None self.checkbox.stateChanged.connect(self._on_value_change) - def set_value(self, value, *, default_value=False): + def set_value(self, value, *, global_value=False): # Ignore value change because if `self.isChecked()` has same # value as `value` the `_on_value_change` is not triggered self.checkbox.setChecked(value) - if default_value: - self.default_value = self.item_value() + if global_value: + self.global_value = self.item_value() self._on_value_change() @@ -183,7 +183,7 @@ class BooleanWidget(QtWidgets.QWidget, InputWidget): if self.is_overidable and self.override_value is not None: self.set_value(self.override_value) else: - self.set_value(self.default_value) + self.set_value(self.global_value) def clear_value(self): self.reset_value() @@ -195,7 +195,7 @@ class BooleanWidget(QtWidgets.QWidget, InputWidget): if override_value is None: self._is_overriden = False self._was_overriden = False - value = self.default_value + value = self.global_value else: self._is_overriden = True self._was_overriden = True @@ -216,7 +216,7 @@ class BooleanWidget(QtWidgets.QWidget, InputWidget): is_modified = _value != self.override_value if is_modified is None: - is_modified = _value != self.default_value + is_modified = _value != self.global_value self._is_modified = is_modified @@ -292,22 +292,22 @@ class IntegerWidget(QtWidgets.QWidget, InputWidget): if value is not NOT_SET: self.int_input.setValue(value) - self.default_value = self.item_value() + self.global_value = self.item_value() self.override_value = None self.int_input.valueChanged.connect(self._on_value_change) - def set_value(self, value, *, default_value=False): + def set_value(self, value, *, global_value=False): self.int_input.setValue(value) - if default_value: - self.default_value = self.item_value() + if global_value: + self.global_value = self.item_value() self._on_value_change() def clear_value(self): self.set_value(0) def reset_value(self): - self.set_value(self.default_value) + self.set_value(self.global_value) def apply_overrides(self, override_value): self._is_modified = False @@ -316,7 +316,7 @@ class IntegerWidget(QtWidgets.QWidget, InputWidget): if override_value is None: self._is_overriden = False self._was_overriden = False - value = self.default_value + value = self.global_value else: self._is_overriden = True self._was_overriden = True @@ -329,7 +329,7 @@ class IntegerWidget(QtWidgets.QWidget, InputWidget): if self.ignore_value_changes: return - self._is_modified = self.item_value() != self.default_value + self._is_modified = self.item_value() != self.global_value if self.is_overidable: self._is_overriden = True @@ -416,19 +416,19 @@ class FloatWidget(QtWidgets.QWidget, InputWidget): if value is not NOT_SET: self.float_input.setValue(value) - self.default_value = self.item_value() + self.global_value = self.item_value() self.override_value = None self.float_input.valueChanged.connect(self._on_value_change) - def set_value(self, value, *, default_value=False): + def set_value(self, value, *, global_value=False): self.float_input.setValue(value) - if default_value: - self.default_value = self.item_value() + if global_value: + self.global_value = self.item_value() self._on_value_change() def reset_value(self): - self.set_value(self.default_value) + self.set_value(self.global_value) def apply_overrides(self, override_value): self._is_modified = False @@ -436,7 +436,7 @@ class FloatWidget(QtWidgets.QWidget, InputWidget): self.override_value = override_value if override_value is None: self._is_overriden = False - value = self.default_value + value = self.global_value else: self._is_overriden = True value = override_value @@ -451,7 +451,7 @@ class FloatWidget(QtWidgets.QWidget, InputWidget): if self.ignore_value_changes: return - self._is_modified = self.item_value() != self.default_value + self._is_modified = self.item_value() != self.global_value if self.is_overidable: self._is_overriden = True @@ -528,19 +528,19 @@ class TextSingleLineWidget(QtWidgets.QWidget, InputWidget): if value is not NOT_SET: self.text_input.setText(value) - self.default_value = self.item_value() + self.global_value = self.item_value() self.override_value = None self.text_input.textChanged.connect(self._on_value_change) - def set_value(self, value, *, default_value=False): + def set_value(self, value, *, global_value=False): self.text_input.setText(value) - if default_value: - self.default_value = self.item_value() + if global_value: + self.global_value = self.item_value() self._on_value_change() def reset_value(self): - self.set_value(self.default_value) + self.set_value(self.global_value) def apply_overrides(self, override_value): self._is_modified = False @@ -549,7 +549,7 @@ class TextSingleLineWidget(QtWidgets.QWidget, InputWidget): if override_value is None: self._is_overriden = False self._was_overriden = False - value = self.default_value + value = self.global_value else: self._is_overriden = True self._was_overriden = True @@ -565,7 +565,7 @@ class TextSingleLineWidget(QtWidgets.QWidget, InputWidget): if self.ignore_value_changes: return - self._is_modified = self.item_value() != self.default_value + self._is_modified = self.item_value() != self.global_value if self.is_overidable: self._is_overriden = True @@ -640,19 +640,19 @@ class TextMultiLineWidget(QtWidgets.QWidget, InputWidget): if value is not NOT_SET: self.text_input.setPlainText(value) - self.default_value = self.item_value() + self.global_value = self.item_value() self.override_value = None self.text_input.textChanged.connect(self._on_value_change) - def set_value(self, value, *, default_value=False): + def set_value(self, value, *, global_value=False): self.text_input.setPlainText(value) - if default_value: - self.default_value = self.item_value() + if global_value: + self.global_value = self.item_value() self._on_value_change() def reset_value(self): - self.set_value(self.default_value) + self.set_value(self.global_value) def apply_overrides(self, override_value): self._is_modified = False @@ -660,7 +660,7 @@ class TextMultiLineWidget(QtWidgets.QWidget, InputWidget): self.override_value = override_value if override_value is None: self._is_overriden = False - value = self.default_value + value = self.global_value else: self._is_overriden = True value = override_value @@ -675,7 +675,7 @@ class TextMultiLineWidget(QtWidgets.QWidget, InputWidget): if self.ignore_value_changes: return - self._is_modified = self.item_value() != self.default_value + self._is_modified = self.item_value() != self.global_value if self.is_overidable: self._is_overriden = True @@ -731,7 +731,7 @@ class RawJsonInput(QtWidgets.QPlainTextEdit): return value - def set_value(self, value, *, default_value=False): + def set_value(self, value, *, global_value=False): if not isinstance(value, str): value = json.dumps(value, indent=4) self.setPlainText(value) @@ -833,19 +833,19 @@ class RawJsonWidget(QtWidgets.QWidget, InputWidget): if value is not NOT_SET: self.text_input.set_value(value) - self.default_value = self.item_value() + self.global_value = self.item_value() self.override_value = None self.text_input.textChanged.connect(self._on_value_change) - def set_value(self, value, *, default_value=False): + def set_value(self, value, *, global_value=False): self.text_input.set_value(value) - if default_value: - self.default_value = self.item_value() + if global_value: + self.global_value = self.item_value() self._on_value_change() def reset_value(self): - self.set_value(self.default_value) + self.set_value(self.global_value) def clear_value(self): self.set_value("") @@ -856,7 +856,7 @@ class RawJsonWidget(QtWidgets.QWidget, InputWidget): self.override_value = override_value if override_value is None: self._is_overriden = False - value = self.default_value + value = self.global_value else: self._is_overriden = True value = override_value @@ -868,7 +868,7 @@ class RawJsonWidget(QtWidgets.QWidget, InputWidget): if self.ignore_value_changes: return - self._is_modified = self.item_value() != self.default_value + self._is_modified = self.item_value() != self.global_value if self.is_overidable: self._is_overriden = True @@ -969,22 +969,22 @@ class TextListSubWidget(QtWidgets.QWidget, ConfigWidget): if value is not NOT_SET: self.set_value(value) - self.default_value = self.item_value() + self.global_value = self.item_value() self.override_value = None - def set_value(self, value, *, default_value=False): + def set_value(self, value, *, global_value=False): for input_field in self.input_fields: self.remove_row(input_field) for item_text in value: self.add_row(text=item_text) - if default_value: - self.default_value = self.item_value() + if global_value: + self.global_value = self.item_value() self._on_value_change() def reset_value(self): - self.set_value(self.default_value) + self.set_value(self.global_value) def clear_value(self): self.set_value([]) @@ -1103,13 +1103,13 @@ class TextListWidget(QtWidgets.QWidget, InputWidget): layout.addWidget(self.value_widget) self.setLayout(layout) - self.default_value = self.item_value() + self.global_value = self.item_value() self.override_value = None def _on_value_change(self, item=None): if self.ignore_value_changes: return - self._is_modified = self.item_value() != self.default_value + self._is_modified = self.item_value() != self.global_value if self.is_overidable: self._is_overriden = True @@ -1117,14 +1117,14 @@ class TextListWidget(QtWidgets.QWidget, InputWidget): self.value_changed.emit(self) - def set_value(self, value, *, default_value=False): + def set_value(self, value, *, global_value=False): self.value_widget.set_value(value) - if default_value: - self.default_value = self.item_value() + if global_value: + self.global_value = self.item_value() self._on_value_change() def reset_value(self): - self.set_value(self.default_value) + self.set_value(self.global_value) def clear_value(self): self.set_value([]) @@ -1135,7 +1135,7 @@ class TextListWidget(QtWidgets.QWidget, InputWidget): self.override_value = override_value if override_value is None: self._is_overriden = False - value = self.default_value + value = self.global_value else: self._is_overriden = True value = override_value @@ -1200,7 +1200,7 @@ class ModifiableDictItem(QtWidgets.QWidget, ConfigWidget): self.value_input.value_changed.connect(self._on_value_change) self.default_key = self._key() - self.default_value = self.value_input.item_value() + self.global_value = self.value_input.item_value() self.override_key = None self.override_value = None @@ -1292,7 +1292,7 @@ class ModifiableDictSubWidget(QtWidgets.QWidget, ConfigWidget): if self.count() == 0: self.add_row() - self.default_value = self.config_value() + self.global_value = self.config_value() self.override_value = None @property @@ -1335,7 +1335,7 @@ class ModifiableDictSubWidget(QtWidgets.QWidget, ConfigWidget): if value is not None and key is not None: item_widget.default_key = key item_widget.key_input.setText(key) - item_widget.value_input.set_value(value, default_value=True) + item_widget.value_input.set_value(value, global_value=True) else: self._on_value_change() self.parent().updateGeometry() @@ -1407,7 +1407,7 @@ class ModifiableDict(ExpandingWidget, InputWidget): self.key = input_data["key"] - self.default_value = self.item_value() + self.global_value = self.item_value() self.override_value = None def _on_value_change(self, item=None): @@ -1420,7 +1420,7 @@ class ModifiableDict(ExpandingWidget, InputWidget): if self.is_overriden: self._is_modified = self.item_value() != self.override_value else: - self._is_modified = self.item_value() != self.default_value + self._is_modified = self.item_value() != self.global_value self.value_changed.emit(self) @@ -1433,7 +1433,7 @@ class ModifiableDict(ExpandingWidget, InputWidget): if override_value is None: self._is_overriden = False self._was_overriden = False - value = self.default_value + value = self.global_value else: self._is_overriden = True self._was_overriden = True From 4aca6fd3378615ae8adcf237a05dafce28194ca5 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 25 Aug 2020 16:08:14 +0200 Subject: [PATCH 199/813] override value is NOT_SET by default instead of None --- .../config_setting/widgets/base.py | 2 +- .../config_setting/widgets/inputs.py | 62 +++++++++---------- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/base.py b/pype/tools/config_setting/config_setting/widgets/base.py index 4fe3866096..94754bda0d 100644 --- a/pype/tools/config_setting/config_setting/widgets/base.py +++ b/pype/tools/config_setting/config_setting/widgets/base.py @@ -329,7 +329,7 @@ class ProjectWidget(QtWidgets.QWidget): def _on_project_change(self): project_name = self.project_list_widget.project_name() if project_name is None: - overrides = None + overrides = lib.NOT_SET self.is_overidable = False else: overrides = config.project_configurations_overrides(project_name) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 140d67c0ad..1697271b4d 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -165,7 +165,7 @@ class BooleanWidget(QtWidgets.QWidget, InputWidget): self.checkbox.setChecked(value) self.global_value = self.item_value() - self.override_value = None + self.override_value = NOT_SET self.checkbox.stateChanged.connect(self._on_value_change) @@ -180,7 +180,7 @@ class BooleanWidget(QtWidgets.QWidget, InputWidget): self._on_value_change() def reset_value(self): - if self.is_overidable and self.override_value is not None: + if self.is_overidable and self.override_value is not NOT_SET: self.set_value(self.override_value) else: self.set_value(self.global_value) @@ -192,7 +192,7 @@ class BooleanWidget(QtWidgets.QWidget, InputWidget): self._is_modified = False self._state = None self.override_value = override_value - if override_value is None: + if override_value is NOT_SET: self._is_overriden = False self._was_overriden = False value = self.global_value @@ -293,7 +293,7 @@ class IntegerWidget(QtWidgets.QWidget, InputWidget): self.int_input.setValue(value) self.global_value = self.item_value() - self.override_value = None + self.override_value = NOT_SET self.int_input.valueChanged.connect(self._on_value_change) @@ -313,7 +313,7 @@ class IntegerWidget(QtWidgets.QWidget, InputWidget): self._is_modified = False self._state = None self.override_value = override_value - if override_value is None: + if override_value is NOT_SET: self._is_overriden = False self._was_overriden = False value = self.global_value @@ -417,7 +417,7 @@ class FloatWidget(QtWidgets.QWidget, InputWidget): self.float_input.setValue(value) self.global_value = self.item_value() - self.override_value = None + self.override_value = NOT_SET self.float_input.valueChanged.connect(self._on_value_change) @@ -434,7 +434,7 @@ class FloatWidget(QtWidgets.QWidget, InputWidget): self._is_modified = False self._state = None self.override_value = override_value - if override_value is None: + if override_value is NOT_SET: self._is_overriden = False value = self.global_value else: @@ -529,7 +529,7 @@ class TextSingleLineWidget(QtWidgets.QWidget, InputWidget): self.text_input.setText(value) self.global_value = self.item_value() - self.override_value = None + self.override_value = NOT_SET self.text_input.textChanged.connect(self._on_value_change) @@ -546,7 +546,7 @@ class TextSingleLineWidget(QtWidgets.QWidget, InputWidget): self._is_modified = False self._state = None self.override_value = override_value - if override_value is None: + if override_value is NOT_SET: self._is_overriden = False self._was_overriden = False value = self.global_value @@ -641,7 +641,7 @@ class TextMultiLineWidget(QtWidgets.QWidget, InputWidget): self.text_input.setPlainText(value) self.global_value = self.item_value() - self.override_value = None + self.override_value = NOT_SET self.text_input.textChanged.connect(self._on_value_change) @@ -658,7 +658,7 @@ class TextMultiLineWidget(QtWidgets.QWidget, InputWidget): self._is_modified = False self._state = None self.override_value = override_value - if override_value is None: + if override_value is NOT_SET: self._is_overriden = False value = self.global_value else: @@ -834,7 +834,7 @@ class RawJsonWidget(QtWidgets.QWidget, InputWidget): self.text_input.set_value(value) self.global_value = self.item_value() - self.override_value = None + self.override_value = NOT_SET self.text_input.textChanged.connect(self._on_value_change) @@ -854,7 +854,7 @@ class RawJsonWidget(QtWidgets.QWidget, InputWidget): self._is_modified = False self._state = None self.override_value = override_value - if override_value is None: + if override_value is NOT_SET: self._is_overriden = False value = self.global_value else: @@ -970,7 +970,7 @@ class TextListSubWidget(QtWidgets.QWidget, ConfigWidget): self.set_value(value) self.global_value = self.item_value() - self.override_value = None + self.override_value = NOT_SET def set_value(self, value, *, global_value=False): for input_field in self.input_fields: @@ -1104,7 +1104,7 @@ class TextListWidget(QtWidgets.QWidget, InputWidget): self.setLayout(layout) self.global_value = self.item_value() - self.override_value = None + self.override_value = NOT_SET def _on_value_change(self, item=None): if self.ignore_value_changes: @@ -1133,7 +1133,7 @@ class TextListWidget(QtWidgets.QWidget, InputWidget): self._is_modified = False self._state = None self.override_value = override_value - if override_value is None: + if override_value is NOT_SET: self._is_overriden = False value = self.global_value else: @@ -1203,7 +1203,7 @@ class ModifiableDictItem(QtWidgets.QWidget, ConfigWidget): self.global_value = self.value_input.item_value() self.override_key = None - self.override_value = None + self.override_value = NOT_SET self.is_single = False @@ -1293,7 +1293,7 @@ class ModifiableDictSubWidget(QtWidgets.QWidget, ConfigWidget): self.add_row() self.global_value = self.config_value() - self.override_value = None + self.override_value = NOT_SET @property def is_group(self): @@ -1408,7 +1408,7 @@ class ModifiableDict(ExpandingWidget, InputWidget): self.key = input_data["key"] self.global_value = self.item_value() - self.override_value = None + self.override_value = NOT_SET def _on_value_change(self, item=None): if self.ignore_value_changes: @@ -1430,7 +1430,7 @@ class ModifiableDict(ExpandingWidget, InputWidget): self._state = None self._is_modified = False self.override_value = override_value - if override_value is None: + if override_value is NOT_SET: self._is_overriden = False self._was_overriden = False value = self.global_value @@ -1523,10 +1523,10 @@ class DictExpandWidget(ExpandingWidget, ConfigWidget): self._state = None self._child_state = None for item in self.input_fields: - if override_value is None: - child_value = None + if override_value is NOT_SET: + child_value = NOT_SET else: - child_value = override_value.get(item.key) + child_value = override_value.get(item.key, NOT_SET) item.apply_overrides(child_value) @@ -1534,7 +1534,7 @@ class DictExpandWidget(ExpandingWidget, ConfigWidget): self.is_group and self.is_overidable and ( - override_value is not None + override_value is not NOT_SET or self.child_overriden ) ) @@ -1710,10 +1710,10 @@ class DictWidget(QtWidgets.QWidget, ConfigWidget): self._state = None self._child_state = None for item in self.input_fields: - if override_value is None: - child_value = None + if override_value is NOT_SET: + child_value = NOT_SET else: - child_value = override_value.get(item.key) + child_value = override_value.get(item.key, NOT_SET) item.apply_overrides(child_value) @@ -1721,7 +1721,7 @@ class DictWidget(QtWidgets.QWidget, ConfigWidget): self.is_group and self.is_overidable and ( - override_value is not None + override_value is not NOT_SET or self.child_overriden ) ) @@ -1919,10 +1919,10 @@ class DictInvisible(QtWidgets.QWidget, ConfigWidget): def apply_overrides(self, override_value): self._is_overriden = False for item in self.input_fields: - if override_value is None: - child_value = None + if override_value is NOT_SET: + child_value = NOT_SET else: - child_value = override_value.get(item.key) + child_value = override_value.get(item.key, NOT_SET) item.apply_overrides(child_value) self._is_overriden = ( From 6342f02574ec0a0560e659161953d793bf9bd1cc Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 25 Aug 2020 17:00:03 +0200 Subject: [PATCH 200/813] it is possible to right click on widgets, actions do nothing --- .../config_setting/widgets/inputs.py | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 1697271b4d..baf524d5da 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -94,6 +94,41 @@ class ConfigWidget: "Method `add_children_gui` is not implemented for `{}`." ).format(self.__class__.__name__)) + def _discard_changes(self): + print("_discard_changes") + + def _remove_overrides(self): + print("_remove_overrides") + + def mouseReleaseEvent(self, event): + if event.button() == QtCore.Qt.RightButton: + menu = QtWidgets.QMenu() + + actions_mapping = {} + if self.child_modified: + action = QtWidgets.QAction("Discard changes") + actions_mapping[action] = self._discard_changes + menu.addAction(action) + + if self.child_overriden: + # TODO better label + action = QtWidgets.QAction("Remove override") + actions_mapping[action] = self._remove_overrides + menu.addAction(action) + + if not actions_mapping: + action = QtWidgets.QAction("< No action >") + actions_mapping[action] = None + menu.addAction(action) + + result = menu.exec_(QtGui.QCursor.pos()) + if result: + to_run = actions_mapping[result] + if to_run: + to_run() + return + super(self.__class__, self).mouseReleaseEvent(event) + class InputWidget(ConfigWidget): def overrides(self): From 31d75aa5cc24c7fdcf86ff5c9f10941d2b3ecd46 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 25 Aug 2020 18:09:02 +0200 Subject: [PATCH 201/813] styles are automatically updated on _ignore_value_changes changed to False --- .../config_setting/widgets/base.py | 33 +++++++++++++++++-- .../config_setting/widgets/inputs.py | 20 +++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/base.py b/pype/tools/config_setting/config_setting/widgets/base.py index 94754bda0d..2018b4bde3 100644 --- a/pype/tools/config_setting/config_setting/widgets/base.py +++ b/pype/tools/config_setting/config_setting/widgets/base.py @@ -12,11 +12,12 @@ class StudioWidget(QtWidgets.QWidget): is_overriden = False is_group = False any_parent_is_group = False - ignore_value_changes = False def __init__(self, parent=None): super(StudioWidget, self).__init__(parent) + self._ignore_value_changes = False + self.input_fields = [] scroll_widget = QtWidgets.QScrollArea(self) @@ -55,6 +56,20 @@ class StudioWidget(QtWidgets.QWidget): self.reset() + @property + def ignore_value_changes(self): + return self._ignore_value_changes + + @ignore_value_changes.setter + def ignore_value_changes(self, value): + self._ignore_value_changes = value + if value is False: + self.hierarchical_style_update() + + def hierarchical_style_update(self): + for input_field in self.input_fields: + input_field.hierarchical_style_update() + def reset(self): if self.content_layout.count() != 0: for widget in self.input_fields: @@ -256,7 +271,7 @@ class ProjectWidget(QtWidgets.QWidget): super(ProjectWidget, self).__init__(parent) self.is_overidable = False - self.ignore_value_changes = False + self._ignore_value_changes = False self.project_name = None self.input_fields = [] @@ -310,6 +325,20 @@ class ProjectWidget(QtWidgets.QWidget): self.reset() + @property + def ignore_value_changes(self): + return self._ignore_value_changes + + @ignore_value_changes.setter + def ignore_value_changes(self, value): + self._ignore_value_changes = value + if value is False: + self.hierarchical_style_update() + + def hierarchical_style_update(self): + for input_field in self.input_fields: + input_field.hierarchical_style_update() + def reset(self): values = {"project": config.global_project_configurations()} schema = lib.gui_schema("projects_schema", "0_project_gui_schema") diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index baf524d5da..c5afe1a7e7 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -53,6 +53,9 @@ class ConfigWidget: raise NotImplementedError( "Method `reset_children_attributes` not implemented!" ) + @ignore_value_changes.setter + def ignore_value_changes(self, value): + self._parent.ignore_value_changes = value def item_value(self): raise NotImplementedError( @@ -136,6 +139,9 @@ class InputWidget(ConfigWidget): return NOT_SET, False return self.config_value(), self.is_group + def hierarchical_style_update(self): + self.update_style() + @property def child_modified(self): return self.is_modified @@ -1780,6 +1786,11 @@ class DictWidget(QtWidgets.QWidget, ConfigWidget): self.update_style() + def hierarchical_style_update(self): + self.update_style() + for input_field in self.input_fields: + input_field.hierarchical_style_update() + def update_style(self, is_overriden=None): child_modified = self.child_modified child_state = self.style_state(self.child_overriden, child_modified) @@ -1951,6 +1962,11 @@ class DictInvisible(QtWidgets.QWidget, ConfigWidget): self.value_changed.emit(self) + def hierarchical_style_update(self): + self.update_style() + for input_field in self.input_fields: + input_field.hierarchical_style_update() + def apply_overrides(self, override_value): self._is_overriden = False for item in self.input_fields: @@ -2051,6 +2067,10 @@ class DictFormWidget(QtWidgets.QWidget, ConfigWidget): self.input_fields[key] = item return item + def hierarchical_style_update(self): + for input_field in self.input_fields.items(): + input_field.hierarchical_style_update() + def item_value(self): output = {} for input_field in self.input_fields.values(): From 6a6a50978460f5302b782b653d08c0cbfb7ac9cf Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 25 Aug 2020 19:09:19 +0200 Subject: [PATCH 202/813] global_value split into global_value and start_value --- .../config_setting/widgets/inputs.py | 144 +++++++++++------- 1 file changed, 91 insertions(+), 53 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index c5afe1a7e7..2bda38e207 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -194,6 +194,7 @@ class BooleanWidget(QtWidgets.QWidget, InputWidget): layout.addWidget(self.checkbox, 1) + value = NOT_SET if not self._as_widget: self.label_widget = label_widget self.key = input_data["key"] @@ -201,11 +202,16 @@ class BooleanWidget(QtWidgets.QWidget, InputWidget): keys.append(self.key) self.keys = keys + default_value = input_data.get("default", NOT_SET) value = self.value_from_values(values) if value is not NOT_SET: self.checkbox.setChecked(value) - self.global_value = self.item_value() + elif default_value is not NOT_SET: + self.checkbox.setChecked(default_value) + + self.global_value = value + self.start_value = self.item_value() self.override_value = NOT_SET self.checkbox.stateChanged.connect(self._on_value_change) @@ -236,7 +242,7 @@ class BooleanWidget(QtWidgets.QWidget, InputWidget): if override_value is NOT_SET: self._is_overriden = False self._was_overriden = False - value = self.global_value + value = self.start_value else: self._is_overriden = True self._was_overriden = True @@ -321,6 +327,7 @@ class IntegerWidget(QtWidgets.QWidget, InputWidget): layout.addWidget(label_widget, 0) layout.addWidget(self.int_input, 1) + value = NOT_SET if not self._as_widget: self.label_widget = label_widget @@ -333,7 +340,8 @@ class IntegerWidget(QtWidgets.QWidget, InputWidget): if value is not NOT_SET: self.int_input.setValue(value) - self.global_value = self.item_value() + self.global_value = value + self.start_value = self.item_value() self.override_value = NOT_SET self.int_input.valueChanged.connect(self._on_value_change) @@ -341,6 +349,7 @@ class IntegerWidget(QtWidgets.QWidget, InputWidget): def set_value(self, value, *, global_value=False): self.int_input.setValue(value) if global_value: + self.start_value = self.item_value() self.global_value = self.item_value() self._on_value_change() @@ -348,7 +357,7 @@ class IntegerWidget(QtWidgets.QWidget, InputWidget): self.set_value(0) def reset_value(self): - self.set_value(self.global_value) + self.set_value(self.start_value) def apply_overrides(self, override_value): self._is_modified = False @@ -445,6 +454,7 @@ class FloatWidget(QtWidgets.QWidget, InputWidget): layout.addWidget(label_widget, 0) layout.addWidget(self.float_input, 1) + value = NOT_SET if not self._as_widget: self.label_widget = label_widget @@ -457,7 +467,8 @@ class FloatWidget(QtWidgets.QWidget, InputWidget): if value is not NOT_SET: self.float_input.setValue(value) - self.global_value = self.item_value() + self.start_value = self.item_value() + self.global_value = value self.override_value = NOT_SET self.float_input.valueChanged.connect(self._on_value_change) @@ -465,6 +476,7 @@ class FloatWidget(QtWidgets.QWidget, InputWidget): def set_value(self, value, *, global_value=False): self.float_input.setValue(value) if global_value: + self.start_value = self.item_value() self.global_value = self.item_value() self._on_value_change() @@ -477,7 +489,7 @@ class FloatWidget(QtWidgets.QWidget, InputWidget): self.override_value = override_value if override_value is NOT_SET: self._is_overriden = False - value = self.global_value + value = self.start_value else: self._is_overriden = True value = override_value @@ -557,6 +569,7 @@ class TextSingleLineWidget(QtWidgets.QWidget, InputWidget): layout.addWidget(label_widget, 0) layout.addWidget(self.text_input, 1) + value = NOT_SET if not self._as_widget: self.label_widget = label_widget @@ -569,7 +582,8 @@ class TextSingleLineWidget(QtWidgets.QWidget, InputWidget): if value is not NOT_SET: self.text_input.setText(value) - self.global_value = self.item_value() + self.global_value = value + self.start_value = self.item_value() self.override_value = NOT_SET self.text_input.textChanged.connect(self._on_value_change) @@ -577,11 +591,12 @@ class TextSingleLineWidget(QtWidgets.QWidget, InputWidget): def set_value(self, value, *, global_value=False): self.text_input.setText(value) if global_value: + self.start_value = self.item_value() self.global_value = self.item_value() self._on_value_change() def reset_value(self): - self.set_value(self.global_value) + self.set_value(self.start_value) def apply_overrides(self, override_value): self._is_modified = False @@ -590,7 +605,7 @@ class TextSingleLineWidget(QtWidgets.QWidget, InputWidget): if override_value is NOT_SET: self._is_overriden = False self._was_overriden = False - value = self.global_value + value = self.start_value else: self._is_overriden = True self._was_overriden = True @@ -664,24 +679,27 @@ class TextMultiLineWidget(QtWidgets.QWidget, InputWidget): layout.setSpacing(5) self.text_input = QtWidgets.QPlainTextEdit() - if not label_widget: + if not self._as_widget and not label_widget: label = input_data["label"] label_widget = QtWidgets.QLabel(label) layout.addWidget(label_widget, 0) layout.addWidget(self.text_input, 1) - self.label_widget = label_widget + value = NOT_SET + if not self._as_widget: + self.label_widget = label_widget - self.key = input_data["key"] - keys = list(parent_keys) - keys.append(self.key) - self.keys = keys + self.key = input_data["key"] + keys = list(parent_keys) + keys.append(self.key) + self.keys = keys - value = self.value_from_values(values) - if value is not NOT_SET: - self.text_input.setPlainText(value) + value = self.value_from_values(values) + if value is not NOT_SET: + self.text_input.setPlainText(value) - self.global_value = self.item_value() + self.global_value = value + self.start_value = self.item_value() self.override_value = NOT_SET self.text_input.textChanged.connect(self._on_value_change) @@ -689,11 +707,12 @@ class TextMultiLineWidget(QtWidgets.QWidget, InputWidget): def set_value(self, value, *, global_value=False): self.text_input.setPlainText(value) if global_value: + self.start_value = self.item_value() self.global_value = self.item_value() self._on_value_change() def reset_value(self): - self.set_value(self.global_value) + self.set_value(self.start_value) def apply_overrides(self, override_value): self._is_modified = False @@ -701,7 +720,7 @@ class TextMultiLineWidget(QtWidgets.QWidget, InputWidget): self.override_value = override_value if override_value is NOT_SET: self._is_overriden = False - value = self.global_value + value = self.start_value else: self._is_overriden = True value = override_value @@ -857,24 +876,27 @@ class RawJsonWidget(QtWidgets.QWidget, InputWidget): QtWidgets.QSizePolicy.MinimumExpanding ) - if not label_widget: + if not self._as_widget and not label_widget: label = input_data["label"] label_widget = QtWidgets.QLabel(label) layout.addWidget(label_widget, 0) layout.addWidget(self.text_input, 1) - self.label_widget = label_widget + value = NOT_SET + if not self._as_widget: + self.label_widget = label_widget - self.key = input_data["key"] - keys = list(parent_keys) - keys.append(self.key) - self.keys = keys + self.key = input_data["key"] + keys = list(parent_keys) + keys.append(self.key) + self.keys = keys - value = self.value_from_values(values) - if value is not NOT_SET: - self.text_input.set_value(value) + value = self.value_from_values(values) + if value is not NOT_SET: + self.text_input.set_value(value) - self.global_value = self.item_value() + self.global_value = value + self.start_value = self.item_value() self.override_value = NOT_SET self.text_input.textChanged.connect(self._on_value_change) @@ -882,11 +904,12 @@ class RawJsonWidget(QtWidgets.QWidget, InputWidget): def set_value(self, value, *, global_value=False): self.text_input.set_value(value) if global_value: + self.start_value = self.item_value() self.global_value = self.item_value() self._on_value_change() def reset_value(self): - self.set_value(self.global_value) + self.set_value(self.start_value) def clear_value(self): self.set_value("") @@ -897,7 +920,7 @@ class RawJsonWidget(QtWidgets.QWidget, InputWidget): self.override_value = override_value if override_value is NOT_SET: self._is_overriden = False - value = self.global_value + value = self.start_value else: self._is_overriden = True value = override_value @@ -1010,7 +1033,8 @@ class TextListSubWidget(QtWidgets.QWidget, ConfigWidget): if value is not NOT_SET: self.set_value(value) - self.global_value = self.item_value() + self.global_value = value + self.start_value = self.item_value() self.override_value = NOT_SET def set_value(self, value, *, global_value=False): @@ -1021,11 +1045,23 @@ class TextListSubWidget(QtWidgets.QWidget, ConfigWidget): self.add_row(text=item_text) if global_value: - self.global_value = self.item_value() + self.global_value = value + self.start_value = self.item_value() self._on_value_change() + def apply_overrides(self, override_value): + self.override_value = override_value + if override_value is NOT_SET: + self._is_overriden = False + value = self.start_value + else: + self._is_overriden = True + value = override_value + + self.set_value(value) + def reset_value(self): - self.set_value(self.global_value) + self.set_value(self.start_value) def clear_value(self): self.set_value([]) @@ -1144,8 +1180,17 @@ class TextListWidget(QtWidgets.QWidget, InputWidget): layout.addWidget(self.value_widget) self.setLayout(layout) - self.global_value = self.item_value() - self.override_value = NOT_SET + @property + def start_value(self): + return self.value_widget.start_value + + @property + def global_value(self): + return self.value_widget.global_value + + @property + def override_value(self): + return self.value_widget.override_value def _on_value_change(self, item=None): if self.ignore_value_changes: @@ -1159,29 +1204,20 @@ class TextListWidget(QtWidgets.QWidget, InputWidget): self.value_changed.emit(self) def set_value(self, value, *, global_value=False): - self.value_widget.set_value(value) + self.value_widget.set_value(value, global_value=global_value) if global_value: - self.global_value = self.item_value() self._on_value_change() def reset_value(self): - self.set_value(self.global_value) + self.value_widget.reset_value() def clear_value(self): - self.set_value([]) + self.value_widget.clear_value() def apply_overrides(self, override_value): + self.value_widget.apply_overrides(override_value) self._is_modified = False self._state = None - self.override_value = override_value - if override_value is NOT_SET: - self._is_overriden = False - value = self.global_value - else: - self._is_overriden = True - value = override_value - - self.set_value(value) self.update_style() def update_style(self): @@ -1240,10 +1276,11 @@ class ModifiableDictItem(QtWidgets.QWidget, ConfigWidget): self.key_input.textChanged.connect(self._on_value_change) self.value_input.value_changed.connect(self._on_value_change) + # TODO This doesn't make sence! self.default_key = self._key() self.global_value = self.value_input.item_value() - self.override_key = None + self.override_key = NOT_SET self.override_value = NOT_SET self.is_single = False @@ -1333,7 +1370,8 @@ class ModifiableDictSubWidget(QtWidgets.QWidget, ConfigWidget): if self.count() == 0: self.add_row() - self.global_value = self.config_value() + self.global_value = value + self.start_value = self.config_value() self.override_value = NOT_SET @property From 8361db1f79620e77ebb484c79bc3d4b4a770d822 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 25 Aug 2020 19:09:38 +0200 Subject: [PATCH 203/813] removed unused methods --- .../config_setting/config_setting/widgets/inputs.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 2bda38e207..7abbd87863 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -42,17 +42,6 @@ class ConfigWidget: def ignore_value_changes(self): return self._parent.ignore_value_changes - def reset_attributes(self): - self._is_overriden = False - self._is_modified = False - self._was_overriden = False - - self.reset_children_attributes() - - def reset_children_attributes(self): - raise NotImplementedError( - "Method `reset_children_attributes` not implemented!" - ) @ignore_value_changes.setter def ignore_value_changes(self, value): self._parent.ignore_value_changes = value From e1209b2f13e29a8f5cdef2dc158cbaf847e1ca86 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 25 Aug 2020 19:09:51 +0200 Subject: [PATCH 204/813] fix value_from_values --- pype/tools/config_setting/config_setting/widgets/inputs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 7abbd87863..04a13ca487 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -55,7 +55,7 @@ class ConfigWidget: return {self.key: self.item_value()} def value_from_values(self, values, keys=None): - if not values: + if not values or values is AS_WIDGET: return NOT_SET if keys is None: From 6c77e43d6b334d5a3c3937b41b221ca1437e6a42 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 25 Aug 2020 19:10:30 +0200 Subject: [PATCH 205/813] made preparation for abstract methods --- .../config_setting/widgets/inputs.py | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 04a13ca487..3b07bb1c99 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -86,11 +86,23 @@ class ConfigWidget: "Method `add_children_gui` is not implemented for `{}`." ).format(self.__class__.__name__)) - def _discard_changes(self): - print("_discard_changes") + def discard_changes(self, is_source=False): + print("discard_changes") + # raise NotImplementedError( + # "Method `discard_changes` not implemented!" + # ) - def _remove_overrides(self): - print("_remove_overrides") + def remove_overrides(self, is_source=False): + print("remove_overrides") + # raise NotImplementedError( + # "Method `remove_overrides` not implemented!" + # ) + + def hierarchical_style_update(self): + print("hierarchical_style_update") + # raise NotImplementedError( + # "Method `hierarchical_style_update` not implemented!" + # ) def mouseReleaseEvent(self, event): if event.button() == QtCore.Qt.RightButton: From 403c2f4888a9e7c6401032e22c62847ec8e9e42d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 25 Aug 2020 19:10:46 +0200 Subject: [PATCH 206/813] preparation for discard changes --- .../config_setting/widgets/inputs.py | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 3b07bb1c99..7fce3606f5 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -111,13 +111,13 @@ class ConfigWidget: actions_mapping = {} if self.child_modified: action = QtWidgets.QAction("Discard changes") - actions_mapping[action] = self._discard_changes + actions_mapping[action] = self.discard_changes menu.addAction(action) if self.child_overriden: # TODO better label action = QtWidgets.QAction("Remove override") - actions_mapping[action] = self._remove_overrides + actions_mapping[action] = self.remove_overrides menu.addAction(action) if not actions_mapping: @@ -129,7 +129,7 @@ class ConfigWidget: if result: to_run = actions_mapping[result] if to_run: - to_run() + to_run(True) return super(self.__class__, self).mouseReleaseEvent(event) @@ -143,6 +143,24 @@ class InputWidget(ConfigWidget): def hierarchical_style_update(self): self.update_style() + def discard_changes(self, is_source=False): + if ( + self.is_overidable + and self.override_value is not NOT_SET + and self._was_overriden is True + ): + self.set_value(self.override_value) + else: + self.set_value(self.start_value) + + if not self.is_overidable: + self._is_modified = self.global_value != self.item_value() + self._is_overriden = False + return + + self._is_modified = False + self._is_overriden = self._was_overriden + @property def child_modified(self): return self.is_modified From 82949978488a627f4031c36d242fb4b3c9c7f2c4 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 25 Aug 2020 19:11:39 +0200 Subject: [PATCH 207/813] implemented discard changes for most of input widgets --- .../config_setting/widgets/inputs.py | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 7fce3606f5..b593248470 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1615,6 +1615,19 @@ class DictExpandWidget(ExpandingWidget, ConfigWidget): for child_data in input_data.get("children", []): self.add_children_gui(child_data, values) + def discard_changes(self, is_source=False): + if is_source: + self.ignore_value_changes = True + + for item in self.input_fields: + item.discard_changes() + + if is_source: + self.ignore_value_changes = False + + self._is_modified = self.child_modified + self._is_overriden = self._was_overriden + def apply_overrides(self, override_value): # Make sure this is set to False self._is_overriden = False @@ -1636,6 +1649,7 @@ class DictExpandWidget(ExpandingWidget, ConfigWidget): or self.child_overriden ) ) + self._was_overriden = bool(self._is_overriden) self.update_style() def _on_value_change(self, item=None): @@ -1802,6 +1816,19 @@ class DictWidget(QtWidgets.QWidget, ConfigWidget): for child_data in input_data.get("children", []): self.add_children_gui(child_data, values) + def discard_changes(self, is_source=False): + if is_source: + self.ignore_value_changes = True + + for item in self.input_fields: + item.discard_changes() + + self._is_modified = self.child_modified + self._is_overriden = self._was_overriden + + if is_source: + self.ignore_value_changes = False + def apply_overrides(self, override_value): # Make sure this is set to False self._is_overriden = False From 43da05a6183b6270b252c08c613e84fb164ed315 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 25 Aug 2020 19:12:09 +0200 Subject: [PATCH 208/813] added forgotten hierarchical style update --- .../config_setting/config_setting/widgets/inputs.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index b593248470..5e4fe0bbf3 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -245,14 +245,8 @@ class BooleanWidget(QtWidgets.QWidget, InputWidget): self._on_value_change() - def reset_value(self): - if self.is_overidable and self.override_value is not NOT_SET: - self.set_value(self.override_value) - else: - self.set_value(self.global_value) - def clear_value(self): - self.reset_value() + self.set_value(False) def apply_overrides(self, override_value): self._is_modified = False @@ -1670,6 +1664,11 @@ class DictExpandWidget(ExpandingWidget, ConfigWidget): self.update_style() + def hierarchical_style_update(self): + self.update_style() + for input_field in self.input_fields: + input_field.hierarchical_style_update() + def update_style(self, is_overriden=None): child_modified = self.child_modified child_state = self.style_state(self.child_overriden, child_modified) From 82570533d0512f7c263fd02892feeb850bff2fca Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 25 Aug 2020 19:14:46 +0200 Subject: [PATCH 209/813] hierarchical_style_update and discard_changes are abstract methods now --- .../config_setting/widgets/inputs.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 5e4fe0bbf3..33facc1a18 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -87,10 +87,9 @@ class ConfigWidget: ).format(self.__class__.__name__)) def discard_changes(self, is_source=False): - print("discard_changes") - # raise NotImplementedError( - # "Method `discard_changes` not implemented!" - # ) + raise NotImplementedError( + "Method `discard_changes` not implemented!" + ) def remove_overrides(self, is_source=False): print("remove_overrides") @@ -99,10 +98,9 @@ class ConfigWidget: # ) def hierarchical_style_update(self): - print("hierarchical_style_update") - # raise NotImplementedError( - # "Method `hierarchical_style_update` not implemented!" - # ) + raise NotImplementedError( + "Method `hierarchical_style_update` not implemented!" + ) def mouseReleaseEvent(self, event): if event.button() == QtCore.Qt.RightButton: From e343cd65cfb6c3682d41ff110fe6ce795fbbc1d3 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 25 Aug 2020 19:36:27 +0200 Subject: [PATCH 210/813] wrapped discard_changes remove_overrides to proxy methods setting ignore value changes --- .../config_setting/widgets/inputs.py | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 33facc1a18..379b27cdb6 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -86,12 +86,22 @@ class ConfigWidget: "Method `add_children_gui` is not implemented for `{}`." ).format(self.__class__.__name__)) - def discard_changes(self, is_source=False): + def _discard_changes(self): + self.ignore_value_changes = True + self.discard_changes() + self.ignore_value_changes = False + + def discard_changes(self): raise NotImplementedError( - "Method `discard_changes` not implemented!" + "{} Method `discard_changes` not implemented!".format(repr(self)) ) - def remove_overrides(self, is_source=False): + def _remove_overrides(self): + self.ignore_value_changes = True + self.remove_overrides() + self.ignore_value_changes = False + + def remove_overrides(self): print("remove_overrides") # raise NotImplementedError( # "Method `remove_overrides` not implemented!" @@ -109,13 +119,16 @@ class ConfigWidget: actions_mapping = {} if self.child_modified: action = QtWidgets.QAction("Discard changes") - actions_mapping[action] = self.discard_changes + actions_mapping[action] = self._discard_changes menu.addAction(action) - if self.child_overriden: + if ( + not self.any_parent_overriden() + and (self.is_overriden or self.child_overriden) + ): # TODO better label action = QtWidgets.QAction("Remove override") - actions_mapping[action] = self.remove_overrides + actions_mapping[action] = self._remove_overrides menu.addAction(action) if not actions_mapping: @@ -127,7 +140,7 @@ class ConfigWidget: if result: to_run = actions_mapping[result] if to_run: - to_run(True) + to_run() return super(self.__class__, self).mouseReleaseEvent(event) From f80cd9492e2338cb80d3d9940cdfb70af8818b75 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 25 Aug 2020 19:37:09 +0200 Subject: [PATCH 211/813] implemented missing discard changes --- .../config_setting/widgets/inputs.py | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 379b27cdb6..feae2d2943 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -154,7 +154,7 @@ class InputWidget(ConfigWidget): def hierarchical_style_update(self): self.update_style() - def discard_changes(self, is_source=False): + def discard_changes(self): if ( self.is_overidable and self.override_value is not NOT_SET @@ -1620,16 +1620,10 @@ class DictExpandWidget(ExpandingWidget, ConfigWidget): for child_data in input_data.get("children", []): self.add_children_gui(child_data, values) - def discard_changes(self, is_source=False): - if is_source: - self.ignore_value_changes = True - + def discard_changes(self): for item in self.input_fields: item.discard_changes() - if is_source: - self.ignore_value_changes = False - self._is_modified = self.child_modified self._is_overriden = self._was_overriden @@ -1826,19 +1820,13 @@ class DictWidget(QtWidgets.QWidget, ConfigWidget): for child_data in input_data.get("children", []): self.add_children_gui(child_data, values) - def discard_changes(self, is_source=False): - if is_source: - self.ignore_value_changes = True - + def discard_changes(self): for item in self.input_fields: item.discard_changes() self._is_modified = self.child_modified self._is_overriden = self._was_overriden - if is_source: - self.ignore_value_changes = False - def apply_overrides(self, override_value): # Make sure this is set to False self._is_overriden = False @@ -2061,6 +2049,13 @@ class DictInvisible(QtWidgets.QWidget, ConfigWidget): for input_field in self.input_fields: input_field.hierarchical_style_update() + def discard_changes(self): + for item in self.input_fields: + item.discard_changes() + + self._is_modified = self.child_modified + self._is_overriden = self._was_overriden + def apply_overrides(self, override_value): self._is_overriden = False for item in self.input_fields: From dba9c98060de3a650e54412e4b82ffcf3d68de89 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 25 Aug 2020 19:37:37 +0200 Subject: [PATCH 212/813] added any_parent_overriden for remove overrides action --- .../config_setting/widgets/base.py | 42 ++++++++++++++++--- .../config_setting/widgets/inputs.py | 5 +++ 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/base.py b/pype/tools/config_setting/config_setting/widgets/base.py index 2018b4bde3..c0246dd8a2 100644 --- a/pype/tools/config_setting/config_setting/widgets/base.py +++ b/pype/tools/config_setting/config_setting/widgets/base.py @@ -9,9 +9,9 @@ from avalon import io class StudioWidget(QtWidgets.QWidget): is_overidable = False - is_overriden = False - is_group = False - any_parent_is_group = False + _is_overriden = False + _is_group = False + _any_parent_is_group = False def __init__(self, parent=None): super(StudioWidget, self).__init__(parent) @@ -56,6 +56,21 @@ class StudioWidget(QtWidgets.QWidget): self.reset() + def any_parent_overriden(self): + return False + + @property + def is_overriden(self): + return self._is_overriden + + @property + def is_group(self): + return self._is_group + + @property + def any_parent_is_group(self): + return self._any_parent_is_group + @property def ignore_value_changes(self): return self._ignore_value_changes @@ -263,9 +278,9 @@ class ProjectListWidget(QtWidgets.QWidget): class ProjectWidget(QtWidgets.QWidget): - is_overriden = False - is_group = False - any_parent_is_group = False + _is_overriden = False + _is_group = False + _any_parent_is_group = False def __init__(self, parent=None): super(ProjectWidget, self).__init__(parent) @@ -325,6 +340,21 @@ class ProjectWidget(QtWidgets.QWidget): self.reset() + def any_parent_overriden(self): + return False + + @property + def is_overriden(self): + return self._is_overriden + + @property + def is_group(self): + return self._is_group + + @property + def any_parent_is_group(self): + return self._any_parent_is_group + @property def ignore_value_changes(self): return self._ignore_value_changes diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index feae2d2943..f946b4cac8 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -38,6 +38,11 @@ class ConfigWidget: def is_overidable(self): return self._parent.is_overidable + def any_parent_overriden(self): + if self._parent._is_overriden: + return True + return self._parent.any_parent_overriden() + @property def ignore_value_changes(self): return self._parent.ignore_value_changes From 9923427df8e5d285bc89df945830fe73d182cfb7 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 25 Aug 2020 19:37:56 +0200 Subject: [PATCH 213/813] added remove overrides for inputs --- pype/tools/config_setting/config_setting/widgets/inputs.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index f946b4cac8..ff0a871e17 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -159,6 +159,12 @@ class InputWidget(ConfigWidget): def hierarchical_style_update(self): self.update_style() + def remove_overrides(self): + self.set_value(self.start_value) + self._is_overriden = False + self._is_modified = False + self._was_overriden = False + def discard_changes(self): if ( self.is_overidable From 71e61a6c68d86ac2b9c253fd46a8d7665d028372 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 25 Aug 2020 19:40:16 +0200 Subject: [PATCH 214/813] remove_overrides is abstract --- .../config_setting/widgets/inputs.py | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index ff0a871e17..d67d32990a 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -87,9 +87,11 @@ class ConfigWidget: return "-".join(items) or self.default_state def add_children_gui(self, child_configuration, values): - raise NotImplementedError(( - "Method `add_children_gui` is not implemented for `{}`." - ).format(self.__class__.__name__)) + raise NotImplementedError( + "{} Method `add_children_gui` is not implemented!.".format( + repr(self) + ) + ) def _discard_changes(self): self.ignore_value_changes = True @@ -98,7 +100,9 @@ class ConfigWidget: def discard_changes(self): raise NotImplementedError( - "{} Method `discard_changes` not implemented!".format(repr(self)) + "{} Method `discard_changes` not implemented!".format( + repr(self) + ) ) def _remove_overrides(self): @@ -107,10 +111,11 @@ class ConfigWidget: self.ignore_value_changes = False def remove_overrides(self): - print("remove_overrides") - # raise NotImplementedError( - # "Method `remove_overrides` not implemented!" - # ) + raise NotImplementedError( + "{} Method `remove_overrides` not implemented!".format( + repr(self) + ) + ) def hierarchical_style_update(self): raise NotImplementedError( From 2a65a2ccb97a42b4bcaed9925a259fba1a011d46 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 25 Aug 2020 19:42:31 +0200 Subject: [PATCH 215/813] remove overrides should work now --- .../config_setting/widgets/inputs.py | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index d67d32990a..3e344cd703 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1636,6 +1636,13 @@ class DictExpandWidget(ExpandingWidget, ConfigWidget): for child_data in input_data.get("children", []): self.add_children_gui(child_data, values) + def remove_overrides(self): + self._is_overriden = False + self._is_modified = False + self._was_overriden = False + for item in self.input_fields: + item.remove_overrides() + def discard_changes(self): for item in self.input_fields: item.discard_changes() @@ -1836,6 +1843,13 @@ class DictWidget(QtWidgets.QWidget, ConfigWidget): for child_data in input_data.get("children", []): self.add_children_gui(child_data, values) + def remove_overrides(self): + self._is_overriden = False + self._is_modified = False + self._was_overriden = False + for item in self.input_fields: + item.remove_overrides() + def discard_changes(self): for item in self.input_fields: item.discard_changes() @@ -2065,6 +2079,13 @@ class DictInvisible(QtWidgets.QWidget, ConfigWidget): for input_field in self.input_fields: input_field.hierarchical_style_update() + def remove_overrides(self): + self._is_overriden = False + self._is_modified = False + self._was_overriden = False + for item in self.input_fields: + item.remove_overrides() + def discard_changes(self): for item in self.input_fields: item.discard_changes() From e1a203125833d96844be994ee993724e8b532c50 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Mon, 24 Aug 2020 20:27:03 +0100 Subject: [PATCH 216/813] Handle original file missing and destination file existing. --- pype/plugins/global/publish/integrate_new.py | 36 ++++++++++++++------ 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/pype/plugins/global/publish/integrate_new.py b/pype/plugins/global/publish/integrate_new.py index a3c2ffe52b..24f5b7bddc 100644 --- a/pype/plugins/global/publish/integrate_new.py +++ b/pype/plugins/global/publish/integrate_new.py @@ -6,6 +6,8 @@ import copy import clique import errno import six +import re +import shutil from pymongo import DeleteOne, InsertOne import pyblish.api @@ -952,21 +954,35 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin): """ if integrated_file_sizes: for file_url, _file_size in integrated_file_sizes.items(): + if not os.path.exists(file_url): + self.log.debug( + "File {} was not found.".format(file_url) + ) + continue + try: if mode == 'remove': - self.log.debug("Removing file ...{}".format(file_url)) + self.log.debug("Removing file {}".format(file_url)) os.remove(file_url) if mode == 'finalize': - self.log.debug("Renaming file ...{}".format(file_url)) - import re - os.rename(file_url, - re.sub('\.{}$'.format(self.TMP_FILE_EXT), - '', - file_url) - ) + new_name = re.sub( + r'\.{}$'.format(self.TMP_FILE_EXT), + '', + file_url + ) - except FileNotFoundError: - pass # file not there, nothing to delete + if os.path.exists(new_name): + self.log.debug( + "Overwriting file {} to {}".format( + file_url, new_name + ) + ) + shutil.copy(file_url, new_name) + else: + self.log.debug( + "Renaming file {} to {}".format(file_url, new_name) + ) + os.rename(file_url, new_name) except OSError: self.log.error("Cannot {} file {}".format(mode, file_url), exc_info=True) From 432d715d2fe3fd8254d0e02bb5d3f910c4a7ecfb Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Mon, 24 Aug 2020 20:28:58 +0100 Subject: [PATCH 217/813] Houd --- pype/plugins/global/publish/integrate_new.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pype/plugins/global/publish/integrate_new.py b/pype/plugins/global/publish/integrate_new.py index 24f5b7bddc..f92968e554 100644 --- a/pype/plugins/global/publish/integrate_new.py +++ b/pype/plugins/global/publish/integrate_new.py @@ -980,7 +980,9 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin): shutil.copy(file_url, new_name) else: self.log.debug( - "Renaming file {} to {}".format(file_url, new_name) + "Renaming file {} to {}".format( + file_url, new_name + ) ) os.rename(file_url, new_name) except OSError: From cdf32eb15051b75b17ed2187daefabc0a5fff43a Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 26 Aug 2020 10:16:20 +0100 Subject: [PATCH 218/813] Containerize audio loading. --- pype/plugins/maya/load/load_audio.py | 49 +++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/pype/plugins/maya/load/load_audio.py b/pype/plugins/maya/load/load_audio.py index e1860d0ca6..ca38082ed0 100644 --- a/pype/plugins/maya/load/load_audio.py +++ b/pype/plugins/maya/load/load_audio.py @@ -1,6 +1,9 @@ from maya import cmds, mel +import pymel.core as pc from avalon import api +from avalon.maya.pipeline import containerise +from avalon.maya import lib class AudioLoader(api.Loader): @@ -24,4 +27,48 @@ class AudioLoader(api.Loader): displaySound=True ) - return [sound_node] + asset = context["asset"]["name"] + namespace = namespace or lib.unique_namespace( + asset + "_", + prefix="_" if asset[0].isdigit() else "", + suffix="_", + ) + + return containerise( + name=name, + namespace=namespace, + nodes=[sound_node], + context=context, + loader=self.__class__.__name__ + ) + + def update(self, container, representation): + audio_node = None + for node in pc.PyNode(container["objectName"]).members(): + if node.nodeType() == "audio": + audio_node = node + + assert audio_node is not None, "Audio node not found." + + path = api.get_representation_path(representation) + audio_node.filename.set(path) + cmds.setAttr( + container["objectName"] + ".representation", + str(representation["_id"]), + type="string" + ) + + def switch(self, container, representation): + self.update(container, representation) + + def remove(self, container): + members = cmds.sets(container['objectName'], query=True) + cmds.lockNode(members, lock=False) + cmds.delete([container['objectName']] + members) + + # Clean up the namespace + try: + cmds.namespace(removeNamespace=container['namespace'], + deleteNamespaceContent=True) + except RuntimeError: + pass From a3d82fc92c5ac27f348077685d36b7acb3517cc3 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 26 Aug 2020 11:03:00 +0100 Subject: [PATCH 219/813] Enable previews for Ftrack review. --- pype/plugins/nukestudio/publish/collect_reviews.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/plugins/nukestudio/publish/collect_reviews.py b/pype/plugins/nukestudio/publish/collect_reviews.py index aa8c60767c..c158dee876 100644 --- a/pype/plugins/nukestudio/publish/collect_reviews.py +++ b/pype/plugins/nukestudio/publish/collect_reviews.py @@ -99,7 +99,7 @@ class CollectReviews(api.InstancePlugin): "step": 1, "fps": rev_inst.data.get("fps"), "name": "preview", - "tags": ["preview"], + "tags": ["preview", "ftrackreview"], "ext": ext } From 76d65ca6d3dcb8772800a3013005dec25c93fe00 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 26 Aug 2020 11:06:49 +0100 Subject: [PATCH 220/813] Thumbnail parent Assetversion was not found when its not linked to a task. assetversion["task"] returns None instead of raising exception. --- .../ftrack/actions/action_thumbnail_to_parent.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/pype/modules/ftrack/actions/action_thumbnail_to_parent.py b/pype/modules/ftrack/actions/action_thumbnail_to_parent.py index 8710fa9dcf..fb473f9aa5 100644 --- a/pype/modules/ftrack/actions/action_thumbnail_to_parent.py +++ b/pype/modules/ftrack/actions/action_thumbnail_to_parent.py @@ -41,9 +41,9 @@ class ThumbToParent(BaseAction): parent = None thumbid = None if entity.entity_type.lower() == 'assetversion': - try: - parent = entity['task'] - except Exception: + parent = entity['task'] + + if parent is None: par_ent = entity['link'][-2] parent = session.get(par_ent['type'], par_ent['id']) else: @@ -51,7 +51,7 @@ class ThumbToParent(BaseAction): parent = entity['parent'] except Exception as e: msg = ( - "Durin Action 'Thumb to Parent'" + "During Action 'Thumb to Parent'" " went something wrong" ) self.log.error(msg) @@ -62,7 +62,10 @@ class ThumbToParent(BaseAction): parent['thumbnail_id'] = thumbid status = 'done' else: - status = 'failed' + raise Exception( + "Parent or thumbnail id not found. Parent: {}. " + "Thumbnail id: {}".format(parent, thumbid) + ) # inform the user that the job is done job['status'] = status or 'done' From 76e0b5a7ae1037f24816fe1094e88b52997570ce Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 26 Aug 2020 12:44:20 +0200 Subject: [PATCH 221/813] allow show icon in bar instead of python icon --- pype/tools/tray/pype_tray.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pype/tools/tray/pype_tray.py b/pype/tools/tray/pype_tray.py index 9537b62581..a4cf4eabfe 100644 --- a/pype/tools/tray/pype_tray.py +++ b/pype/tools/tray/pype_tray.py @@ -537,6 +537,14 @@ class PypeTrayApplication(QtWidgets.QApplication): super(self.__class__, self).__init__(sys.argv) # Allows to close widgets without exiting app self.setQuitOnLastWindowClosed(False) + + # Allow show icon istead of python icon in task bar (Windows) + if os.name == "nt": + import ctypes + ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID( + u"pype_tray" + ) + # Sets up splash splash_widget = self.set_splash() From bda8cb88017a7c61b225d3bcc4187323d3c27e7f Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 26 Aug 2020 12:44:32 +0200 Subject: [PATCH 222/813] login thread is not qthread based --- pype/modules/ftrack/tray/login_tools.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/pype/modules/ftrack/tray/login_tools.py b/pype/modules/ftrack/tray/login_tools.py index 02982294f2..e7d22fbc19 100644 --- a/pype/modules/ftrack/tray/login_tools.py +++ b/pype/modules/ftrack/tray/login_tools.py @@ -2,7 +2,7 @@ from http.server import BaseHTTPRequestHandler, HTTPServer from urllib import parse import webbrowser import functools -from Qt import QtCore +import threading from pype.api import resources @@ -55,20 +55,17 @@ class LoginServerHandler(BaseHTTPRequestHandler): ) -class LoginServerThread(QtCore.QThread): +class LoginServerThread(threading.Thread): '''Login server thread.''' - # Login signal. - loginSignal = QtCore.Signal(object, object, object) - - def start(self, url): - '''Start thread.''' + def __init__(self, url, callback): self.url = url - super(LoginServerThread, self).start() + self.callback = callback + super(LoginServerThread, self).__init__() def _handle_login(self, api_user, api_key): '''Login to server with *api_user* and *api_key*.''' - self.loginSignal.emit(self.url, api_user, api_key) + self.callback(api_user, api_key) def run(self): '''Listen for events.''' From e0e4b4eb9f1050ed6778af3b5860447c7c78b738 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 26 Aug 2020 12:44:50 +0200 Subject: [PATCH 223/813] login dialog was rewriten from base --- pype/modules/ftrack/tray/login_dialog.py | 446 ++++++++++++----------- 1 file changed, 224 insertions(+), 222 deletions(-) diff --git a/pype/modules/ftrack/tray/login_dialog.py b/pype/modules/ftrack/tray/login_dialog.py index e0614513a3..9ffd21fd30 100644 --- a/pype/modules/ftrack/tray/login_dialog.py +++ b/pype/modules/ftrack/tray/login_dialog.py @@ -7,309 +7,311 @@ from pype.api import resources from Qt import QtCore, QtGui, QtWidgets -class Login_Dialog_ui(QtWidgets.QWidget): - +class CredentialsDialog(QtWidgets.QDialog): SIZE_W = 300 SIZE_H = 230 - loginSignal = QtCore.Signal(object, object, object) - _login_server_thread = None - inputs = [] - buttons = [] - labels = [] + login_changed = QtCore.Signal() + logout_signal = QtCore.Signal() - def __init__(self, parent=None, is_event=False): + def __init__(self, parent=None): + super(CredentialsDialog, self).__init__(parent) - super(Login_Dialog_ui, self).__init__() + self.setWindowTitle("Pype - Ftrack Login") - self.parent = parent - self.is_event = is_event + self._login_server_thread = None + self._is_logged = False + self._in_advance_mode = False - if hasattr(parent, 'icon'): - self.setWindowIcon(self.parent.icon) - elif hasattr(parent, 'parent') and hasattr(parent.parent, 'icon'): - self.setWindowIcon(self.parent.parent.icon) - else: - icon = QtGui.QIcon(resources.pype_icon_filepath()) - self.setWindowIcon(icon) + icon = QtGui.QIcon(resources.pype_icon_filepath()) + self.setWindowIcon(icon) self.setWindowFlags( QtCore.Qt.WindowCloseButtonHint | QtCore.Qt.WindowMinimizeButtonHint ) - self.loginSignal.connect(self.loginWithCredentials) - self._translate = QtCore.QCoreApplication.translate - - self.font = QtGui.QFont() - self.font.setFamily("DejaVu Sans Condensed") - self.font.setPointSize(9) - self.font.setBold(True) - self.font.setWeight(50) - self.font.setKerning(True) - - self.resize(self.SIZE_W, self.SIZE_H) self.setMinimumSize(QtCore.QSize(self.SIZE_W, self.SIZE_H)) - self.setMaximumSize(QtCore.QSize(self.SIZE_W+100, self.SIZE_H+100)) + self.setMaximumSize(QtCore.QSize(self.SIZE_W + 100, self.SIZE_H + 100)) self.setStyleSheet(style.load_stylesheet()) - self.setLayout(self._main()) - self.setWindowTitle('Pype - Ftrack Login') + self.ui_init() - def _main(self): - self.main = QtWidgets.QVBoxLayout() - self.main.setObjectName("main") - - self.form = QtWidgets.QFormLayout() - self.form.setContentsMargins(10, 15, 10, 5) - self.form.setObjectName("form") - - self.ftsite_label = QtWidgets.QLabel("FTrack URL:") - self.ftsite_label.setFont(self.font) - self.ftsite_label.setCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor)) - self.ftsite_label.setTextFormat(QtCore.Qt.RichText) - self.ftsite_label.setObjectName("user_label") + def ui_init(self): + self.ftsite_label = QtWidgets.QLabel("Ftrack URL:") + self.user_label = QtWidgets.QLabel("Username:") + self.api_label = QtWidgets.QLabel("API Key:") self.ftsite_input = QtWidgets.QLineEdit() - self.ftsite_input.setEnabled(True) - self.ftsite_input.setFrame(True) - self.ftsite_input.setEnabled(False) self.ftsite_input.setReadOnly(True) - self.ftsite_input.setObjectName("ftsite_input") - - self.user_label = QtWidgets.QLabel("Username:") - self.user_label.setFont(self.font) - self.user_label.setCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor)) - self.user_label.setTextFormat(QtCore.Qt.RichText) - self.user_label.setObjectName("user_label") + self.ftsite_input.setCursor(QtGui.QCursor(QtCore.Qt.IBeamCursor)) self.user_input = QtWidgets.QLineEdit() - self.user_input.setEnabled(True) - self.user_input.setFrame(True) - self.user_input.setObjectName("user_input") - self.user_input.setPlaceholderText( - self._translate("main", "user.name") - ) + self.user_input.setPlaceholderText("user.name") self.user_input.textChanged.connect(self._user_changed) - self.api_label = QtWidgets.QLabel("API Key:") - self.api_label.setFont(self.font) - self.api_label.setCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor)) - self.api_label.setTextFormat(QtCore.Qt.RichText) - self.api_label.setObjectName("api_label") - self.api_input = QtWidgets.QLineEdit() - self.api_input.setEnabled(True) - self.api_input.setFrame(True) - self.api_input.setObjectName("api_input") - self.api_input.setPlaceholderText(self._translate( - "main", "e.g. xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" - )) + self.api_input.setPlaceholderText( + "e.g. xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + ) self.api_input.textChanged.connect(self._api_changed) + input_layout = QtWidgets.QFormLayout() + input_layout.setContentsMargins(10, 15, 10, 5) + + input_layout.addRow(self.ftsite_label, self.ftsite_input) + input_layout.addRow(self.user_label, self.user_input) + input_layout.addRow(self.api_label, self.api_input) + + self.btn_advanced = QtWidgets.QPushButton("Advanced") + self.btn_advanced.clicked.connect(self._on_advanced_clicked) + + self.btn_simple = QtWidgets.QPushButton("Simple") + self.btn_simple.clicked.connect(self._on_simple_clicked) + + self.btn_login = QtWidgets.QPushButton("Login") + self.btn_login.setToolTip( + "Set Username and API Key with entered values" + ) + self.btn_login.clicked.connect(self._on_login_clicked) + + self.btn_ftrack_login = QtWidgets.QPushButton("Ftrack login") + self.btn_ftrack_login.setToolTip("Open browser for Login to Ftrack") + self.btn_ftrack_login.clicked.connect(self._on_ftrack_login_clicked) + + self.btn_logout = QtWidgets.QPushButton("Logout") + self.btn_logout.clicked.connect(self._on_logout_clicked) + + self.btn_close = QtWidgets.QPushButton("Close") + self.btn_close.setToolTip("Close this window") + self.btn_close.clicked.connect(self._close_widget) + + btns_layout = QtWidgets.QHBoxLayout() + btns_layout.addWidget(self.btn_advanced) + btns_layout.addWidget(self.btn_simple) + btns_layout.addStretch(1) + btns_layout.addWidget(self.btn_ftrack_login) + btns_layout.addWidget(self.btn_login) + btns_layout.addWidget(self.btn_logout) + btns_layout.addWidget(self.btn_close) + + self.note_label = QtWidgets.QLabel(( + "NOTE: Click on \"{}\" button to log with your default browser" + " or click on \"{}\" button to enter API key manually." + ).format(self.btn_ftrack_login.text(), self.btn_advanced.text())) + + self.note_label.setWordWrap(True) + self.note_label.hide() + self.error_label = QtWidgets.QLabel("") - self.error_label.setFont(self.font) - self.error_label.setTextFormat(QtCore.Qt.RichText) - self.error_label.setObjectName("error_label") self.error_label.setWordWrap(True) self.error_label.hide() - self.form.addRow(self.ftsite_label, self.ftsite_input) - self.form.addRow(self.user_label, self.user_input) - self.form.addRow(self.api_label, self.api_input) - self.form.addRow(self.error_label) + label_layout = QtWidgets.QVBoxLayout() + label_layout.setContentsMargins(10, 5, 10, 5) + label_layout.addWidget(self.note_label) + label_layout.addWidget(self.error_label) - self.btnGroup = QtWidgets.QHBoxLayout() - self.btnGroup.addStretch(1) - self.btnGroup.setObjectName("btnGroup") + main = QtWidgets.QVBoxLayout(self) + main.addLayout(input_layout) + main.addLayout(label_layout) + main.addStretch(1) + main.addLayout(btns_layout) - self.btnEnter = QtWidgets.QPushButton("Login") - self.btnEnter.setToolTip( - 'Set Username and API Key with entered values' - ) - self.btnEnter.clicked.connect(self.enter_credentials) + self.fill_ftrack_url() - self.btnClose = QtWidgets.QPushButton("Close") - self.btnClose.setToolTip('Close this window') - self.btnClose.clicked.connect(self._close_widget) + self.set_is_logged(self._is_logged) - self.btnFtrack = QtWidgets.QPushButton("Ftrack") - self.btnFtrack.setToolTip('Open browser for Login to Ftrack') - self.btnFtrack.clicked.connect(self.open_ftrack) + self.setLayout(main) - self.btnGroup.addWidget(self.btnFtrack) - self.btnGroup.addWidget(self.btnEnter) - self.btnGroup.addWidget(self.btnClose) + def fill_ftrack_url(self): + url = os.getenv("FTRACK_SERVER") + checked_url = self.check_url(url) - self.main.addLayout(self.form) - self.main.addLayout(self.btnGroup) + if checked_url is None: + checked_url = "" + self.btn_login.setEnabled(False) + self.btn_ftrack_login.setEnabled(False) - self.inputs.append(self.api_input) - self.inputs.append(self.user_input) - self.inputs.append(self.ftsite_input) + self.api_input.setEnabled(False) + self.user_input.setEnabled(False) + self.ftsite_input.setEnabled(False) - self.enter_site() - return self.main + self.ftsite_input.setText(checked_url) - def enter_site(self): - try: - url = os.getenv('FTRACK_SERVER') - newurl = self.checkUrl(url) + def set_advanced_mode(self, is_advanced): + self._in_advance_mode = is_advanced - if newurl is None: - self.btnEnter.setEnabled(False) - self.btnFtrack.setEnabled(False) - for input in self.inputs: - input.setEnabled(False) - newurl = url + self.error_label.setVisible(False) - self.ftsite_input.setText(newurl) + is_logged = self._is_logged - except Exception: - self.setError("FTRACK_SERVER is not set in templates") - self.btnEnter.setEnabled(False) - self.btnFtrack.setEnabled(False) - for input in self.inputs: - input.setEnabled(False) + self.note_label.setVisible(not is_logged and not is_advanced) + self.btn_ftrack_login.setVisible(not is_logged and not is_advanced) + self.btn_advanced.setVisible(not is_logged and not is_advanced) - def setError(self, msg): + self.btn_login.setVisible(not is_logged and is_advanced) + self.btn_simple.setVisible(not is_logged and is_advanced) + + self.user_label.setVisible(is_logged or is_advanced) + self.user_input.setVisible(is_logged or is_advanced) + self.api_label.setVisible(is_logged or is_advanced) + self.api_input.setVisible(is_logged or is_advanced) + if is_advanced: + self.user_input.setFocus() + else: + self.btn_ftrack_login.setFocus() + + def set_is_logged(self, is_logged): + self._is_logged = is_logged + + self.user_input.setReadOnly(is_logged) + self.api_input.setReadOnly(is_logged) + self.user_input.setCursor(QtGui.QCursor(QtCore.Qt.IBeamCursor)) + self.api_input.setCursor(QtGui.QCursor(QtCore.Qt.IBeamCursor)) + + self.btn_logout.setVisible(is_logged) + + self.set_advanced_mode(self._in_advance_mode) + + def set_error(self, msg): self.error_label.setText(msg) self.error_label.show() + def _on_logout_clicked(self): + self.user_input.setText("") + self.api_input.setText("") + self.set_is_logged(False) + self.logout_signal.emit() + + def _on_simple_clicked(self): + self.set_advanced_mode(False) + + def _on_advanced_clicked(self): + self.set_advanced_mode(True) + def _user_changed(self): - self.user_input.setStyleSheet("") + self._not_invalid_input(self.user_input) def _api_changed(self): - self.api_input.setStyleSheet("") + self._not_invalid_input(self.api_input) - def _invalid_input(self, entity): - entity.setStyleSheet("border: 1px solid red;") + def _not_invalid_input(self, input_widget): + input_widget.setStyleSheet("") - def enter_credentials(self): + def _invalid_input(self, input_widget): + input_widget.setStyleSheet("border: 1px solid red;") + + def _on_login_clicked(self): username = self.user_input.text().strip() - apiKey = self.api_input.text().strip() - msg = "You didn't enter " + api_key = self.api_input.text().strip() missing = [] if username == "": missing.append("Username") self._invalid_input(self.user_input) - if apiKey == "": + if api_key == "": missing.append("API Key") self._invalid_input(self.api_input) if len(missing) > 0: - self.setError("{0} {1}".format(msg, " and ".join(missing))) + self.set_error("You didn't enter {}".format(" and ".join(missing))) return - verification = credentials.check_credentials(username, apiKey) - - if verification: - credentials.save_credentials(username, apiKey, self.is_event) - credentials.set_env(username, apiKey) - if self.parent is not None: - self.parent.loginChange() - self._close_widget() - else: + if not self.login_with_credentials(username, api_key): self._invalid_input(self.user_input) self._invalid_input(self.api_input) - self.setError( + self.set_error( "We're unable to sign in to Ftrack with these credentials" ) - def open_ftrack(self): - url = self.ftsite_input.text() - self.loginWithCredentials(url, None, None) - - def checkUrl(self, url): - url = url.strip('/ ') - + def _on_ftrack_login_clicked(self): + url = self.check_url(self.ftsite_input.text()) if not url: - self.setError("There is no URL set in Templates") - return - - if 'http' not in url: - if url.endswith('ftrackapp.com'): - url = 'https://' + url - else: - url = 'https://{0}.ftrackapp.com'.format(url) - try: - result = requests.get( - url, - # Old python API will not work with redirect. - allow_redirects=False - ) - except requests.exceptions.RequestException: - self.setError( - 'The server URL set in Templates could not be reached.' - ) - return - - if ( - result.status_code != 200 or 'FTRACK_VERSION' not in result.headers - ): - self.setError( - 'The server URL set in Templates is not a valid ftrack server.' - ) - return - return url - - def loginWithCredentials(self, url, username, apiKey): - url = url.strip('/ ') - - if not url: - self.setError( - 'You need to specify a valid server URL, ' - 'for example https://server-name.ftrackapp.com' - ) - return - - if 'http' not in url: - if url.endswith('ftrackapp.com'): - url = 'https://' + url - else: - url = 'https://{0}.ftrackapp.com'.format(url) - try: - result = requests.get( - url, - # Old python API will not work with redirect. - allow_redirects=False - ) - except requests.exceptions.RequestException: - self.setError( - 'The server URL you provided could not be reached.' - ) - return - - if ( - result.status_code != 200 or 'FTRACK_VERSION' not in result.headers - ): - self.setError( - 'The server URL you provided is not a valid ftrack server.' - ) return # If there is an existing server thread running we need to stop it. if self._login_server_thread: - self._login_server_thread.quit() + self._login_server_thread.stop() + self._login_server_thread.join() self._login_server_thread = None # If credentials are not properly set, try to get them using a http # server. - if not username or not apiKey: - self._login_server_thread = login_tools.LoginServerThread() - self._login_server_thread.loginSignal.connect(self.loginSignal) - self._login_server_thread.start(url) + self._login_server_thread = login_tools.LoginServerThread( + url, self._result_of_ftrack_thread + ) + self._login_server_thread.start() + + def _result_of_ftrack_thread(self, username, api_key): + if not self.login_with_credentials(username, api_key): + self._invalid_input(self.api_input) + self.set_error(( + "Somthing happened with Ftrack login." + " Try enter Username and API key manually." + )) + else: + self.set_is_logged(True) + + def login_with_credentials(self, username, api_key): + verification = credentials.check_credentials(username, api_key) + if verification: + credentials.save_credentials(username, api_key, False) + credentials.set_env(username, api_key) + self.set_credentials(username, api_key) + self.login_changed.emit() + return verification + + def set_credentials(self, username, api_key, is_logged=True): + self.user_input.setText(username) + self.api_input.setText(api_key) + + self.error_label.hide() + + self._not_invalid_input(self.ftsite_input) + self._not_invalid_input(self.user_input) + self._not_invalid_input(self.api_input) + + if is_logged is not None: + self.set_is_logged(is_logged) + + def check_url(self, url): + if url is not None: + url = url.strip("/ ") + + if not url: + self.set_error(( + "You need to specify a valid server URL, " + "for example https://server-name.ftrackapp.com" + )) return - verification = credentials.check_credentials(username, apiKey) + if "http" not in url: + if url.endswith("ftrackapp.com"): + url = "https://" + url + else: + url = "https://{}.ftrackapp.com".format(url) + try: + result = requests.get( + url, + # Old python API will not work with redirect. + allow_redirects=False + ) + except requests.exceptions.RequestException: + self.set_error( + "Specified URL could not be reached." + ) + return - if verification is True: - credentials.save_credentials(username, apiKey, self.is_event) - credentials.set_env(username, apiKey) - if self.parent is not None: - self.parent.loginChange() - self._close_widget() + if ( + result.status_code != 200 + or "FTRACK_VERSION" not in result.headers + ): + self.set_error( + "Specified URL does not lead to a valid Ftrack server." + ) + return + return url def closeEvent(self, event): event.ignore() From 15cb7393297f3ba30a50e2d610ddfd2cd7a1efe1 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 26 Aug 2020 12:45:16 +0200 Subject: [PATCH 224/813] ftrack module modified to be able handle new ftrack login dialog --- pype/modules/ftrack/tray/ftrack_module.py | 110 +++++++++++++--------- 1 file changed, 66 insertions(+), 44 deletions(-) diff --git a/pype/modules/ftrack/tray/ftrack_module.py b/pype/modules/ftrack/tray/ftrack_module.py index 674e8cbd4f..99f382b11e 100644 --- a/pype/modules/ftrack/tray/ftrack_module.py +++ b/pype/modules/ftrack/tray/ftrack_module.py @@ -2,7 +2,7 @@ import os import time import datetime import threading -from Qt import QtCore, QtWidgets +from Qt import QtCore, QtWidgets, QtGui import ftrack_api from ..ftrack_server.lib import check_ftrack_url @@ -10,7 +10,7 @@ from ..ftrack_server import socket_thread from ..lib import credentials from . import login_dialog -from pype.api import Logger +from pype.api import Logger, resources log = Logger().get_logger("FtrackModule", "ftrack") @@ -19,7 +19,7 @@ log = Logger().get_logger("FtrackModule", "ftrack") class FtrackModule: def __init__(self, main_parent=None, parent=None): self.parent = parent - self.widget_login = login_dialog.Login_Dialog_ui(self) + self.thread_action_server = None self.thread_socket_server = None self.thread_timer = None @@ -29,8 +29,22 @@ class FtrackModule: self.bool_action_thread_running = False self.bool_timer_event = False + self.widget_login = login_dialog.CredentialsDialog() + self.widget_login.login_changed.connect(self.on_login_change) + self.widget_login.logout_signal.connect(self.on_logout) + + self.action_credentials = None + self.icon_logged = QtGui.QIcon( + resources.get_resource("icons", "circle_green.png") + ) + self.icon_not_logged = QtGui.QIcon( + resources.get_resource("icons", "circle_orange.png") + ) + def show_login_widget(self): self.widget_login.show() + self.widget_login.activateWindow() + self.widget_login.raise_() def validate(self): validation = False @@ -39,9 +53,10 @@ class FtrackModule: ft_api_key = cred.get("api_key") validation = credentials.check_credentials(ft_user, ft_api_key) if validation: + self.widget_login.set_credentials(ft_user, ft_api_key) credentials.set_env(ft_user, ft_api_key) log.info("Connected to Ftrack successfully") - self.loginChange() + self.on_login_change() return validation @@ -60,15 +75,24 @@ class FtrackModule: return validation # Necessary - login_dialog works with this method after logging in - def loginChange(self): + def on_login_change(self): self.bool_logged = True + + self.action_credentials.setIcon(self.icon_logged) + self.action_credentials.setToolTip( + "Logged as user \"{}\"".format(self.widget_login.user_input.text()) + ) + self.set_menu_visibility() self.start_action_server() - def logout(self): + def on_logout(self): credentials.clear_credentials() self.stop_action_server() + self.action_credentials.setIcon(self.icon_not_logged) + self.action_credentials.setToolTip("Logged out") + log.info("Logged out of Ftrack") self.bool_logged = False self.set_menu_visibility() @@ -218,43 +242,45 @@ class FtrackModule: # Definition of Tray menu def tray_menu(self, parent_menu): # Menu for Tray App - self.menu = QtWidgets.QMenu('Ftrack', parent_menu) - self.menu.setProperty('submenu', 'on') - - # Actions - server - self.smActionS = self.menu.addMenu("Action server") - - self.aRunActionS = QtWidgets.QAction( - "Run action server", self.smActionS - ) - self.aResetActionS = QtWidgets.QAction( - "Reset action server", self.smActionS - ) - self.aStopActionS = QtWidgets.QAction( - "Stop action server", self.smActionS - ) - - self.aRunActionS.triggered.connect(self.start_action_server) - self.aResetActionS.triggered.connect(self.reset_action_server) - self.aStopActionS.triggered.connect(self.stop_action_server) - - self.smActionS.addAction(self.aRunActionS) - self.smActionS.addAction(self.aResetActionS) - self.smActionS.addAction(self.aStopActionS) + tray_menu = QtWidgets.QMenu("Ftrack", parent_menu) # Actions - basic - self.aLogin = QtWidgets.QAction("Login", self.menu) - self.aLogin.triggered.connect(self.validate) - self.aLogout = QtWidgets.QAction("Logout", self.menu) - self.aLogout.triggered.connect(self.logout) + action_credentials = QtWidgets.QAction("Credentials", tray_menu) + action_credentials.triggered.connect(self.show_login_widget) + if self.bool_logged: + icon = self.icon_logged + else: + icon = self.icon_not_logged + action_credentials.setIcon(icon) + tray_menu.addAction(action_credentials) + self.action_credentials = action_credentials - self.menu.addAction(self.aLogin) - self.menu.addAction(self.aLogout) + # Actions - server + tray_server_menu = tray_menu.addMenu("Action server") + self.action_server_run = QtWidgets.QAction( + "Run action server", tray_server_menu + ) + self.action_server_reset = QtWidgets.QAction( + "Reset action server", tray_server_menu + ) + self.action_server_stop = QtWidgets.QAction( + "Stop action server", tray_server_menu + ) + + self.action_server_run.triggered.connect(self.start_action_server) + self.action_server_reset.triggered.connect(self.reset_action_server) + self.action_server_stop.triggered.connect(self.stop_action_server) + + tray_server_menu.addAction(self.action_server_run) + tray_server_menu.addAction(self.action_server_reset) + tray_server_menu.addAction(self.action_server_stop) + + self.tray_server_menu = tray_server_menu self.bool_logged = False self.set_menu_visibility() - parent_menu.addMenu(self.menu) + parent_menu.addMenu(tray_menu) def tray_start(self): self.validate() @@ -264,19 +290,15 @@ class FtrackModule: # Definition of visibility of each menu actions def set_menu_visibility(self): - - self.smActionS.menuAction().setVisible(self.bool_logged) - self.aLogin.setVisible(not self.bool_logged) - self.aLogout.setVisible(self.bool_logged) - + self.tray_server_menu.menuAction().setVisible(self.bool_logged) if self.bool_logged is False: if self.bool_timer_event is True: self.stop_timer_thread() return - self.aRunActionS.setVisible(not self.bool_action_server_running) - self.aResetActionS.setVisible(self.bool_action_thread_running) - self.aStopActionS.setVisible(self.bool_action_server_running) + self.action_server_run.setVisible(not self.bool_action_server_running) + self.action_server_reset.setVisible(self.bool_action_thread_running) + self.action_server_stop.setVisible(self.bool_action_server_running) if self.bool_timer_event is False: self.start_timer_thread() From c49ad39965dfdf9d8cce447128ae5f399635d6c5 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 26 Aug 2020 12:46:21 +0200 Subject: [PATCH 225/813] more secure icon changes --- pype/modules/ftrack/tray/ftrack_module.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/pype/modules/ftrack/tray/ftrack_module.py b/pype/modules/ftrack/tray/ftrack_module.py index 99f382b11e..0b011c5b33 100644 --- a/pype/modules/ftrack/tray/ftrack_module.py +++ b/pype/modules/ftrack/tray/ftrack_module.py @@ -78,10 +78,13 @@ class FtrackModule: def on_login_change(self): self.bool_logged = True - self.action_credentials.setIcon(self.icon_logged) - self.action_credentials.setToolTip( - "Logged as user \"{}\"".format(self.widget_login.user_input.text()) - ) + if self.action_credentials: + self.action_credentials.setIcon(self.icon_logged) + self.action_credentials.setToolTip( + "Logged as user \"{}\"".format( + self.widget_login.user_input.text() + ) + ) self.set_menu_visibility() self.start_action_server() @@ -90,8 +93,9 @@ class FtrackModule: credentials.clear_credentials() self.stop_action_server() - self.action_credentials.setIcon(self.icon_not_logged) - self.action_credentials.setToolTip("Logged out") + if self.action_credentials: + self.action_credentials.setIcon(self.icon_not_logged) + self.action_credentials.setToolTip("Logged out") log.info("Logged out of Ftrack") self.bool_logged = False From ca05176ce5d1becbf3972be6d87cf0b856bb5369 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 26 Aug 2020 12:54:40 +0200 Subject: [PATCH 226/813] thread does not have stop method --- pype/modules/ftrack/tray/login_dialog.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pype/modules/ftrack/tray/login_dialog.py b/pype/modules/ftrack/tray/login_dialog.py index 9ffd21fd30..b142f31891 100644 --- a/pype/modules/ftrack/tray/login_dialog.py +++ b/pype/modules/ftrack/tray/login_dialog.py @@ -232,7 +232,6 @@ class CredentialsDialog(QtWidgets.QDialog): # If there is an existing server thread running we need to stop it. if self._login_server_thread: - self._login_server_thread.stop() self._login_server_thread.join() self._login_server_thread = None From b98db04a8671abff9fd1f21f4f7cb47b224892c4 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 26 Aug 2020 13:01:03 +0200 Subject: [PATCH 227/813] close widget when successfully logged in --- pype/modules/ftrack/tray/login_dialog.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pype/modules/ftrack/tray/login_dialog.py b/pype/modules/ftrack/tray/login_dialog.py index b142f31891..b703c4cd14 100644 --- a/pype/modules/ftrack/tray/login_dialog.py +++ b/pype/modules/ftrack/tray/login_dialog.py @@ -224,6 +224,8 @@ class CredentialsDialog(QtWidgets.QDialog): self.set_error( "We're unable to sign in to Ftrack with these credentials" ) + else: + self._close_widget() def _on_ftrack_login_clicked(self): url = self.check_url(self.ftsite_input.text()) @@ -251,6 +253,7 @@ class CredentialsDialog(QtWidgets.QDialog): )) else: self.set_is_logged(True) + self._close_widget() def login_with_credentials(self, username, api_key): verification = credentials.check_credentials(username, api_key) From 2f05d7cb197aa7c91ec0ac37585cbf36f882b373 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 26 Aug 2020 13:08:37 +0200 Subject: [PATCH 228/813] fixed closing widget --- pype/modules/ftrack/tray/login_dialog.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/pype/modules/ftrack/tray/login_dialog.py b/pype/modules/ftrack/tray/login_dialog.py index b703c4cd14..7730ee1609 100644 --- a/pype/modules/ftrack/tray/login_dialog.py +++ b/pype/modules/ftrack/tray/login_dialog.py @@ -35,6 +35,8 @@ class CredentialsDialog(QtWidgets.QDialog): self.setMaximumSize(QtCore.QSize(self.SIZE_W + 100, self.SIZE_H + 100)) self.setStyleSheet(style.load_stylesheet()) + self.login_changed.connect(self._on_login) + self.ui_init() def ui_init(self): @@ -202,6 +204,10 @@ class CredentialsDialog(QtWidgets.QDialog): def _invalid_input(self, input_widget): input_widget.setStyleSheet("border: 1px solid red;") + def _on_login(self): + self.set_is_logged(True) + self._close_widget() + def _on_login_clicked(self): username = self.user_input.text().strip() api_key = self.api_input.text().strip() @@ -224,8 +230,6 @@ class CredentialsDialog(QtWidgets.QDialog): self.set_error( "We're unable to sign in to Ftrack with these credentials" ) - else: - self._close_widget() def _on_ftrack_login_clicked(self): url = self.check_url(self.ftsite_input.text()) @@ -251,9 +255,6 @@ class CredentialsDialog(QtWidgets.QDialog): "Somthing happened with Ftrack login." " Try enter Username and API key manually." )) - else: - self.set_is_logged(True) - self._close_widget() def login_with_credentials(self, username, api_key): verification = credentials.check_credentials(username, api_key) From a00b11d31fe2e1c58dab6b124a1a62a2d377a768 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 26 Aug 2020 14:29:57 +0200 Subject: [PATCH 229/813] Added pulling websocket server port from environment variable WEBSOCKET_URL --- .../websocket_server/websocket_server.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/pype/modules/websocket_server/websocket_server.py b/pype/modules/websocket_server/websocket_server.py index 6b730d4eb3..777bcf1f61 100644 --- a/pype/modules/websocket_server/websocket_server.py +++ b/pype/modules/websocket_server/websocket_server.py @@ -9,6 +9,7 @@ import os import sys import pyclbr import importlib +import urllib log = Logger().get_logger("WebsocketServer") @@ -28,19 +29,16 @@ class WebSocketServer(): self.qaction = None self.failed_icon = None self._is_running = False - default_port = 8099 WebSocketServer._instance = self self.client = None self.handlers = {} - try: - self.presets = config.get_presets()["services"]["websocket_server"] - except Exception: - self.presets = {"default_port": default_port, "exclude_ports": []} - log.debug(( - "There are not set presets for WebsocketServer." - " Using defaults \"{}\"" - ).format(str(self.presets))) + websocket_url = os.getenv("WEBSOCKET_URL") + if websocket_url: + parsed = urllib.parse.urlparse(websocket_url) + port = parsed.port + if not port: + port = 8099 # try default port self.app = web.Application() @@ -52,7 +50,7 @@ class WebSocketServer(): directories_with_routes = ['hosts'] self.add_routes_for_directories(directories_with_routes) - self.websocket_thread = WebsocketServerThread(self, default_port) + self.websocket_thread = WebsocketServerThread(self, port) def add_routes_for_directories(self, directories_with_routes): """ Loops through selected directories to find all modules and From f66d14e582d540bf262dc3d3d5cacf60f96bede0 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 26 Aug 2020 17:55:53 +0200 Subject: [PATCH 230/813] scrollers are a littlebit better --- .../config_setting/style/style.css | 115 ++++++++++++++++++ 1 file changed, 115 insertions(+) diff --git a/pype/tools/config_setting/config_setting/style/style.css b/pype/tools/config_setting/config_setting/style/style.css index 0099e6ed76..73715d892d 100644 --- a/pype/tools/config_setting/config_setting/style/style.css +++ b/pype/tools/config_setting/config_setting/style/style.css @@ -116,3 +116,118 @@ QPushButton[btn-type="expand-toggle"] { #ExpandingWidget[state="child-overriden-modified"], #ModifiableDict[state="child-overriden-modified"], #DictWidget[state="child-overriden-modified"] { border-color: #137cbd; } + +QScrollBar:horizontal { + height: 15px; + margin: 3px 15px 3px 15px; + border: 1px transparent #1d272f; + border-radius: 4px; + background-color: #1d272f; +} + +QScrollBar::handle:horizontal { + background-color: #61839e; + min-width: 5px; + border-radius: 4px; +} + +QScrollBar::add-line:horizontal { + margin: 0px 3px 0px 3px; + border-image: url(:/qss_icons/rc/right_arrow_disabled.png); + width: 10px; + height: 10px; + subcontrol-position: right; + subcontrol-origin: margin; +} + +QScrollBar::sub-line:horizontal { + margin: 0px 3px 0px 3px; + border-image: url(:/qss_icons/rc/left_arrow_disabled.png); + height: 10px; + width: 10px; + subcontrol-position: left; + subcontrol-origin: margin; +} + +QScrollBar::add-line:horizontal:hover,QScrollBar::add-line:horizontal:on { + border-image: url(:/qss_icons/rc/right_arrow.png); + height: 10px; + width: 10px; + subcontrol-position: right; + subcontrol-origin: margin; +} + +QScrollBar::sub-line:horizontal:hover, QScrollBar::sub-line:horizontal:on { + border-image: url(:/qss_icons/rc/left_arrow.png); + height: 10px; + width: 10px; + subcontrol-position: left; + subcontrol-origin: margin; +} + +QScrollBar::up-arrow:horizontal, QScrollBar::down-arrow:horizontal { + background: none; +} + +QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal { + background: none; +} + +QScrollBar:vertical { + background-color: #1d272f; + width: 15px; + margin: 15px 3px 15px 3px; + border: 1px transparent #1d272f; + border-radius: 4px; +} + +QScrollBar::handle:vertical { + background-color: #61839e; + min-height: 5px; + border-radius: 4px; +} + +QScrollBar::sub-line:vertical { + margin: 3px 0px 3px 0px; + border-image: url(:/qss_icons/rc/up_arrow_disabled.png); + height: 10px; + width: 10px; + subcontrol-position: top; + subcontrol-origin: margin; +} + +QScrollBar::add-line:vertical { + margin: 3px 0px 3px 0px; + border-image: url(:/qss_icons/rc/down_arrow_disabled.png); + height: 10px; + width: 10px; + subcontrol-position: bottom; + subcontrol-origin: margin; +} + +QScrollBar::sub-line:vertical:hover,QScrollBar::sub-line:vertical:on { + + border-image: url(:/qss_icons/rc/up_arrow.png); + height: 10px; + width: 10px; + subcontrol-position: top; + subcontrol-origin: margin; +} + + +QScrollBar::add-line:vertical:hover, QScrollBar::add-line:vertical:on { + border-image: url(:/qss_icons/rc/down_arrow.png); + height: 10px; + width: 10px; + subcontrol-position: bottom; + subcontrol-origin: margin; +} + +QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical { + background: none; +} + + +QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { + background: none; +} From d1ffd17f5f925be45d66d499478ea112285418da Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 26 Aug 2020 17:56:03 +0200 Subject: [PATCH 231/813] qmenu items are visible --- .../config_setting/config_setting/style/style.css | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pype/tools/config_setting/config_setting/style/style.css b/pype/tools/config_setting/config_setting/style/style.css index 73715d892d..9bf8828920 100644 --- a/pype/tools/config_setting/config_setting/style/style.css +++ b/pype/tools/config_setting/config_setting/style/style.css @@ -5,6 +5,19 @@ QWidget { border-radius: 0px; } +QMenu { + border: 1px solid #555555; + background-color: #1d272f; +} + +QMenu::item { + padding: 5px 10px 5px 10px; +} + +QMenu::item:selected { + background-color: #202e3a; +} + QCheckBox::indicator { } QCheckBox::indicator:focus { From b274cfed16dc39c5945914f8e7bd8626872abf3c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 26 Aug 2020 18:04:39 +0200 Subject: [PATCH 232/813] mae qmenu in same style --- pype/tools/config_setting/config_setting/style/style.css | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pype/tools/config_setting/config_setting/style/style.css b/pype/tools/config_setting/config_setting/style/style.css index 9bf8828920..c5f1f33500 100644 --- a/pype/tools/config_setting/config_setting/style/style.css +++ b/pype/tools/config_setting/config_setting/style/style.css @@ -12,10 +12,12 @@ QMenu { QMenu::item { padding: 5px 10px 5px 10px; + border-left: 5px solid #313131; } QMenu::item:selected { - background-color: #202e3a; + border-left-color: #61839e; + background-color: #222d37; } QCheckBox::indicator { From e5960d7fa0fab9b294b611cd3b98ca2987564f78 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 26 Aug 2020 18:42:08 +0200 Subject: [PATCH 233/813] added validation for is_group --- .../config_setting/widgets/lib.py | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/lib.py b/pype/tools/config_setting/config_setting/widgets/lib.py index c6379b4816..e7173f1c56 100644 --- a/pype/tools/config_setting/config_setting/widgets/lib.py +++ b/pype/tools/config_setting/config_setting/widgets/lib.py @@ -94,6 +94,19 @@ class SchemaMissingFileInfo(Exception): super(SchemaMissingFileInfo, self).__init__(msg) +class SchemeGroupHierarchyBug(Exception): + def __init__(self, invalid): + full_path_keys = [] + for item in invalid: + full_path_keys.append("\"{}\"".format("/".join(item))) + + msg = ( + "Items with attribute \"is_group\" can't have another item with" + " \"is_group\" attribute as child. Error happened for keys: [{}]" + ).format(", ".join(full_path_keys)) + super(SchemeGroupHierarchyBug, self).__init__(msg) + + def file_keys_from_schema(schema_data): output = [] keys = [] @@ -147,11 +160,55 @@ def validate_all_has_ending_file(schema_data, is_top=True): raise SchemaMissingFileInfo(invalid) +def validate_is_group_is_unique_in_hierarchy( + schema_data, any_parent_is_group=False, keys=None +): + is_top = keys is None + if keys is None: + keys = [] + + keyless = "key" not in schema_data + + if not keyless: + keys.append(schema_data["key"]) + + invalid = [] + is_group = schema_data.get("is_group") + if is_group and any_parent_is_group: + invalid.append(copy.deepcopy(keys)) + + if is_group: + any_parent_is_group = is_group + + children = schema_data.get("children") + if not children: + return invalid + + for child in children: + result = validate_is_group_is_unique_in_hierarchy( + child, any_parent_is_group, copy.deepcopy(keys) + ) + if not result: + continue + + invalid.extend(result) + + if invalid and is_group and keys not in invalid: + invalid.append(copy.deepcopy(keys)) + + if not is_top: + return invalid + + if invalid: + raise SchemeGroupHierarchyBug(invalid) + + def validate_schema(schema_data): # TODO validator for key uniquenes # TODO validator that is_group key is not before is_file child # TODO validator that is_group or is_file is not on child without key validate_all_has_ending_file(schema_data) + validate_is_group_is_unique_in_hierarchy(schema_data) def gui_schema(subfolder, main_schema_name): From 28c4a69ea13804b96744a3a56afeb5cd0a24ea7b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 26 Aug 2020 19:52:35 +0200 Subject: [PATCH 234/813] added ConfigWidget for reimplementing Qt methods --- .../config_setting/widgets/widgets.py | 40 ++++++++++++++++++- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/widgets.py b/pype/tools/config_setting/config_setting/widgets/widgets.py index a15edf58ff..89f6782cfd 100644 --- a/pype/tools/config_setting/config_setting/widgets/widgets.py +++ b/pype/tools/config_setting/config_setting/widgets/widgets.py @@ -1,4 +1,4 @@ -from Qt import QtWidgets, QtCore +from Qt import QtWidgets, QtCore, QtGui class ModifiedIntSpinBox(QtWidgets.QSpinBox): @@ -38,7 +38,43 @@ class ClickableWidget(QtWidgets.QLabel): super(ClickableWidget, self).mouseReleaseEvent(event) -class ExpandingWidget(QtWidgets.QWidget): +class ConfigWidget(QtWidgets.QWidget): + allow_actions = True + + def mouseReleaseEvent(self, event): + if self.allow_actions and event.button() == QtCore.Qt.RightButton: + menu = QtWidgets.QMenu() + + actions_mapping = {} + if self.child_modified: + action = QtWidgets.QAction("Discard changes") + actions_mapping[action] = self._discard_changes + menu.addAction(action) + + if ( + not self.any_parent_overriden() + and (self.is_overriden or self.child_overriden) + ): + # TODO better label + action = QtWidgets.QAction("Remove override") + actions_mapping[action] = self._remove_overrides + menu.addAction(action) + + if not actions_mapping: + action = QtWidgets.QAction("< No action >") + actions_mapping[action] = None + menu.addAction(action) + + result = menu.exec_(QtGui.QCursor.pos()) + if result: + to_run = actions_mapping[result] + if to_run: + to_run() + return + super(ConfigWidget, self).mouseReleaseEvent(event) + + +class ExpandingWidget(ConfigWidget): def __init__(self, label, parent): super(ExpandingWidget, self).__init__(parent) self.setObjectName("ExpandingWidget") From d320924b9e9831d41fe49a204805e8c7ec9855b2 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 26 Aug 2020 19:55:02 +0200 Subject: [PATCH 235/813] ConfigWidget and InputWidget renamed to ConfigObject and InputWidget --- .../config_setting/widgets/inputs.py | 72 ++++++------------- 1 file changed, 21 insertions(+), 51 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 3e344cd703..e30f437cd9 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1,6 +1,7 @@ import json from Qt import QtWidgets, QtCore, QtGui from .widgets import ( + ConfigWidget, ExpandingWidget, ModifiedIntSpinBox, ModifiedFloatSpinBox @@ -16,7 +17,7 @@ class SchemeGroupHierarchyBug(Exception): super(SchemeGroupHierarchyBug, self).__init__(msg) -class ConfigWidget: +class ConfigObject: default_state = "" _is_overriden = False _is_modified = False @@ -122,40 +123,8 @@ class ConfigWidget: "Method `hierarchical_style_update` not implemented!" ) - def mouseReleaseEvent(self, event): - if event.button() == QtCore.Qt.RightButton: - menu = QtWidgets.QMenu() - actions_mapping = {} - if self.child_modified: - action = QtWidgets.QAction("Discard changes") - actions_mapping[action] = self._discard_changes - menu.addAction(action) - - if ( - not self.any_parent_overriden() - and (self.is_overriden or self.child_overriden) - ): - # TODO better label - action = QtWidgets.QAction("Remove override") - actions_mapping[action] = self._remove_overrides - menu.addAction(action) - - if not actions_mapping: - action = QtWidgets.QAction("< No action >") - actions_mapping[action] = None - menu.addAction(action) - - result = menu.exec_(QtGui.QCursor.pos()) - if result: - to_run = actions_mapping[result] - if to_run: - to_run() - return - super(self.__class__, self).mouseReleaseEvent(event) - - -class InputWidget(ConfigWidget): +class InputObject(ConfigObject): def overrides(self): if not self.is_overriden: return NOT_SET, False @@ -200,7 +169,7 @@ class InputWidget(ConfigWidget): return -class BooleanWidget(QtWidgets.QWidget, InputWidget): +class BooleanWidget(ConfigWidget, InputObject): value_changed = QtCore.Signal(object) def __init__( @@ -329,7 +298,7 @@ class BooleanWidget(QtWidgets.QWidget, InputWidget): return self.checkbox.isChecked() -class IntegerWidget(QtWidgets.QWidget, InputWidget): +class IntegerWidget(ConfigWidget, InputObject): value_changed = QtCore.Signal(object) def __init__( @@ -446,7 +415,7 @@ class IntegerWidget(QtWidgets.QWidget, InputWidget): return self.int_input.value() -class FloatWidget(QtWidgets.QWidget, InputWidget): +class FloatWidget(ConfigWidget, InputObject): value_changed = QtCore.Signal(object) def __init__( @@ -571,7 +540,7 @@ class FloatWidget(QtWidgets.QWidget, InputWidget): return self.float_input.value() -class TextSingleLineWidget(QtWidgets.QWidget, InputWidget): +class TextSingleLineWidget(ConfigWidget, InputObject): value_changed = QtCore.Signal(object) def __init__( @@ -580,7 +549,6 @@ class TextSingleLineWidget(QtWidgets.QWidget, InputWidget): self._parent = parent self._as_widget = values is AS_WIDGET - any_parent_is_group = parent.is_group if not any_parent_is_group: any_parent_is_group = parent.any_parent_is_group @@ -688,7 +656,7 @@ class TextSingleLineWidget(QtWidgets.QWidget, InputWidget): return self.text_input.text() -class TextMultiLineWidget(QtWidgets.QWidget, InputWidget): +class TextMultiLineWidget(ConfigWidget, InputObject): value_changed = QtCore.Signal(object) def __init__( @@ -880,7 +848,7 @@ class RawJsonInput(QtWidgets.QPlainTextEdit): self.update_style(is_valid) -class RawJsonWidget(QtWidgets.QWidget, InputWidget): +class RawJsonWidget(ConfigWidget, InputObject): value_changed = QtCore.Signal(object) def __init__( @@ -999,7 +967,7 @@ class RawJsonWidget(QtWidgets.QWidget, InputWidget): return self.text_input.toPlainText() -class TextListItem(QtWidgets.QWidget, ConfigWidget): +class TextListItem(QtWidgets.QWidget, ConfigObject): _btn_size = 20 value_changed = QtCore.Signal(object) @@ -1049,7 +1017,7 @@ class TextListItem(QtWidgets.QWidget, ConfigWidget): return self.text_input.text() -class TextListSubWidget(QtWidgets.QWidget, ConfigWidget): +class TextListSubWidget(QtWidgets.QWidget, ConfigObject): value_changed = QtCore.Signal(object) def __init__(self, input_data, values, parent_keys, parent): @@ -1169,7 +1137,7 @@ class TextListSubWidget(QtWidgets.QWidget, ConfigWidget): return output -class TextListWidget(QtWidgets.QWidget, InputWidget): +class TextListWidget(ConfigWidget, InputObject): value_changed = QtCore.Signal(object) def __init__( @@ -1272,7 +1240,7 @@ class TextListWidget(QtWidgets.QWidget, InputWidget): return self.value_widget.config_value() -class ModifiableDictItem(QtWidgets.QWidget, ConfigWidget): +class ModifiableDictItem(QtWidgets.QWidget, ConfigObject): _btn_size = 20 value_changed = QtCore.Signal(object) @@ -1380,7 +1348,7 @@ class ModifiableDictItem(QtWidgets.QWidget, ConfigWidget): return {key: value} -class ModifiableDictSubWidget(QtWidgets.QWidget, ConfigWidget): +class ModifiableDictSubWidget(QtWidgets.QWidget, ConfigObject): value_changed = QtCore.Signal(object) def __init__(self, input_data, values, parent_keys, parent): @@ -1486,7 +1454,7 @@ class ModifiableDictSubWidget(QtWidgets.QWidget, ConfigWidget): return output -class ModifiableDict(ExpandingWidget, InputWidget): +class ModifiableDict(ExpandingWidget, InputObject): # Should be used only for dictionary with one datatype as value # TODO this is actually input field (do not care if is group or not) value_changed = QtCore.Signal(object) @@ -1584,7 +1552,7 @@ class ModifiableDict(ExpandingWidget, InputWidget): # Dictionaries -class DictExpandWidget(ExpandingWidget, ConfigWidget): +class DictExpandWidget(ExpandingWidget, ConfigObject): value_changed = QtCore.Signal(object) def __init__( @@ -1775,7 +1743,7 @@ class DictExpandWidget(ExpandingWidget, ConfigWidget): return {self.key: values}, self.is_group -class DictWidget(QtWidgets.QWidget, ConfigWidget): +class DictWidget(ConfigWidget, ConfigObject): value_changed = QtCore.Signal(object) def __init__( @@ -1981,9 +1949,10 @@ class DictWidget(QtWidgets.QWidget, ConfigWidget): return {self.key: values}, self.is_group -class DictInvisible(QtWidgets.QWidget, ConfigWidget): +class DictInvisible(ConfigWidget, ConfigObject): # TODO is not overridable by itself value_changed = QtCore.Signal(object) + allow_actions = False def __init__( self, input_data, values, parent_keys, parent, label_widget=None @@ -2130,8 +2099,9 @@ class DictInvisible(QtWidgets.QWidget, ConfigWidget): # Proxy for form layout -class DictFormWidget(QtWidgets.QWidget, ConfigWidget): +class DictFormWidget(ConfigWidget, ConfigObject): value_changed = QtCore.Signal(object) + allow_actions = False def __init__( self, input_data, values, parent_keys, parent, label_widget=None From 7887cb3d7215379e532cc7f10188c8a1707832d8 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 26 Aug 2020 19:55:28 +0200 Subject: [PATCH 236/813] FormWIdget can handle mouse right clicks for its items --- .../config_setting/widgets/inputs.py | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index e30f437cd9..64a5a90b45 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -2098,6 +2098,12 @@ class DictInvisible(ConfigWidget, ConfigObject): return {self.key: values}, self.is_group +class FormLabel(QtWidgets.QLabel): + def __init__(self, *args, **kwargs): + super(FormLabel, self).__init__(*args, **kwargs) + self.item = None + + # Proxy for form layout class DictFormWidget(ConfigWidget, ConfigObject): value_changed = QtCore.Signal(object) @@ -2126,6 +2132,16 @@ class DictFormWidget(ConfigWidget, ConfigObject): for child_data in input_data.get("children", []): self.add_children_gui(child_data, values) + def mouseReleaseEvent(self, event): + if event.button() == QtCore.Qt.RightButton: + position = self.mapFromGlobal(QtGui.QCursor().pos()) + widget = self.childAt(position) + if widget and isinstance(widget, FormLabel): + widget.item.mouseReleaseEvent(event) + event.accept() + return + super(DictFormWidget, self).mouseReleaseEvent(event) + def _on_value_change(self, item=None): if self.ignore_value_changes: return @@ -2153,18 +2169,20 @@ class DictFormWidget(ConfigWidget, ConfigObject): klass = TypeToKlass.types.get(item_type) - label_widget = QtWidgets.QLabel(label) + label_widget = FormLabel(label, self) item = klass( child_configuration, values, self.keys, self, label_widget ) + label_widget.item = item + item.value_changed.connect(self._on_value_change) self.content_layout.addRow(label_widget, item) self.input_fields[key] = item return item def hierarchical_style_update(self): - for input_field in self.input_fields.items(): + for input_field in self.input_fields.values(): input_field.hierarchical_style_update() def item_value(self): From 1c40104d512837360ea6ea4a20703cc369195a9a Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 26 Aug 2020 20:01:56 +0200 Subject: [PATCH 237/813] removed SchemeGroupHierarchyBug as is already validated when loading schemas --- .../config_setting/widgets/inputs.py | 127 ++---------------- 1 file changed, 12 insertions(+), 115 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 64a5a90b45..09174b7c0e 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -9,14 +9,6 @@ from .widgets import ( from .lib import NOT_SET, AS_WIDGET, METADATA_KEY, TypeToKlass -class SchemeGroupHierarchyBug(Exception): - def __init__(self, msg=None): - if not msg: - # TODO better message - msg = "SCHEME BUG: Attribute `is_group` is mixed in the hierarchy" - super(SchemeGroupHierarchyBug, self).__init__(msg) - - class ConfigObject: default_state = "" _is_overriden = False @@ -175,21 +167,10 @@ class BooleanWidget(ConfigWidget, InputObject): def __init__( self, input_data, values, parent_keys, parent, label_widget=None ): - self._as_widget = values is AS_WIDGET self._parent = parent + self._as_widget = values is AS_WIDGET - any_parent_is_group = parent.is_group - if not any_parent_is_group: - any_parent_is_group = parent.any_parent_is_group - - is_group = input_data.get("is_group", False) - if is_group and any_parent_is_group: - raise SchemeGroupHierarchyBug() - - if not any_parent_is_group and not is_group: - is_group = True - - self.is_group = is_group + self.is_group = input_data.get("is_group", False) self._state = None @@ -307,18 +288,7 @@ class IntegerWidget(ConfigWidget, InputObject): self._parent = parent self._as_widget = values is AS_WIDGET - any_parent_is_group = parent.is_group - if not any_parent_is_group: - any_parent_is_group = parent.any_parent_is_group - - is_group = input_data.get("is_group", False) - if is_group and any_parent_is_group: - raise SchemeGroupHierarchyBug() - - if not any_parent_is_group and not is_group: - is_group = True - - self.is_group = is_group + self.is_group = input_data.get("is_group", False) self._state = None @@ -424,18 +394,7 @@ class FloatWidget(ConfigWidget, InputObject): self._parent = parent self._as_widget = values is AS_WIDGET - any_parent_is_group = parent.is_group - if not any_parent_is_group: - any_parent_is_group = parent.any_parent_is_group - - is_group = input_data.get("is_group", False) - if is_group and any_parent_is_group: - raise SchemeGroupHierarchyBug() - - if not any_parent_is_group and not is_group: - is_group = True - - self.is_group = is_group + self.is_group = input_data.get("is_group", False) self._state = None @@ -549,17 +508,7 @@ class TextSingleLineWidget(ConfigWidget, InputObject): self._parent = parent self._as_widget = values is AS_WIDGET - if not any_parent_is_group: - any_parent_is_group = parent.any_parent_is_group - - is_group = input_data.get("is_group", False) - if is_group and any_parent_is_group: - raise SchemeGroupHierarchyBug() - - if not any_parent_is_group and not is_group: - is_group = True - - self.is_group = is_group + self.is_group = input_data.get("is_group", False) self._state = None @@ -665,18 +614,7 @@ class TextMultiLineWidget(ConfigWidget, InputObject): self._parent = parent self._as_widget = values is AS_WIDGET - any_parent_is_group = parent.is_group - if not any_parent_is_group: - any_parent_is_group = parent.any_parent_is_group - - is_group = input_data.get("is_group", False) - if is_group and any_parent_is_group: - raise SchemeGroupHierarchyBug() - - if not any_parent_is_group and not is_group: - is_group = True - - self.is_group = is_group + self.is_group = input_data.get("is_group", False) self._state = None @@ -857,18 +795,7 @@ class RawJsonWidget(ConfigWidget, InputObject): self._parent = parent self._as_widget = values is AS_WIDGET - any_parent_is_group = parent.is_group - if not any_parent_is_group: - any_parent_is_group = parent.any_parent_is_group - - is_group = input_data.get("is_group", False) - if is_group and any_parent_is_group: - raise SchemeGroupHierarchyBug() - - if not any_parent_is_group and not is_group: - is_group = True - - self.is_group = is_group + self.is_group = input_data.get("is_group", False) self._state = None @@ -1145,18 +1072,7 @@ class TextListWidget(ConfigWidget, InputObject): ): self._parent = parent - any_parent_is_group = parent.is_group - if not any_parent_is_group: - any_parent_is_group = parent.any_parent_is_group - - is_group = input_data.get("is_group", False) - if is_group and any_parent_is_group: - raise SchemeGroupHierarchyBug() - - if not any_parent_is_group and not is_group: - is_group = True - - self.is_group = is_group + self.is_group = input_data.get("is_group", False) self._state = None @@ -1469,16 +1385,9 @@ class ModifiableDict(ExpandingWidget, InputObject): if not any_parent_is_group: any_parent_is_group = parent.any_parent_is_group - is_group = input_data.get("is_group", False) - if is_group and any_parent_is_group: - raise SchemeGroupHierarchyBug() - - if not any_parent_is_group and not is_group: - is_group = True - self.any_parent_is_group = any_parent_is_group - self.is_group = is_group + self.is_group = input_data.get("is_group", False) self._state = None super(ModifiableDict, self).__init__(input_data["label"], parent) @@ -1568,13 +1477,9 @@ class DictExpandWidget(ExpandingWidget, ConfigObject): if not any_parent_is_group: any_parent_is_group = parent.any_parent_is_group - is_group = input_data.get("is_group", False) - if is_group and any_parent_is_group: - raise SchemeGroupHierarchyBug() - self.any_parent_is_group = any_parent_is_group - self.is_group = is_group + self.is_group = input_data.get("is_group", False) self._state = None self._child_state = None @@ -1759,13 +1664,9 @@ class DictWidget(ConfigWidget, ConfigObject): if not any_parent_is_group: any_parent_is_group = parent.any_parent_is_group - is_group = input_data.get("is_group", False) - if is_group and any_parent_is_group: - raise SchemeGroupHierarchyBug() - self.any_parent_is_group = any_parent_is_group - self.is_group = is_group + self.is_group = input_data.get("is_group", False) self._state = None self._child_state = None @@ -1963,12 +1864,8 @@ class DictInvisible(ConfigWidget, ConfigObject): if not any_parent_is_group: any_parent_is_group = parent.any_parent_is_group - is_group = input_data.get("is_group", False) - if is_group and any_parent_is_group: - raise SchemeGroupHierarchyBug() - self.any_parent_is_group = any_parent_is_group - self.is_group = is_group + self.is_group = input_data.get("is_group", False) super(DictInvisible, self).__init__(parent) self.setObjectName("DictInvisible") From bb78d47d482afcd494bf27b8822cd05d53c235f2 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 26 Aug 2020 20:02:09 +0200 Subject: [PATCH 238/813] implemented discard changes for form layout --- pype/tools/config_setting/config_setting/widgets/inputs.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 09174b7c0e..d4806b4a5d 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -2039,6 +2039,13 @@ class DictFormWidget(ConfigWidget, ConfigObject): return super(DictFormWidget, self).mouseReleaseEvent(event) + def discard_changes(self): + for item in self.input_fields.values(): + item.discard_changes() + + self._is_modified = self.child_modified + self._is_overriden = self._was_overriden + def _on_value_change(self, item=None): if self.ignore_value_changes: return From a295c6420f9756e79e8b9be2005b849049e8f0c5 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 26 Aug 2020 20:24:44 +0200 Subject: [PATCH 239/813] removed duplicated keys --- .../1_applications_gui_schema.json | 20 ++++--------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_applications_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_applications_gui_schema.json index bbf74a8f3f..2e60ed360d 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_applications_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_applications_gui_schema.json @@ -36,18 +36,6 @@ "type": "boolean", "key": "harmony_17", "label": "Harmony 17" - }, { - "type": "boolean", - "key": "houdini_16", - "label": "Houdini 16" - }, { - "type": "boolean", - "key": "houdini_17", - "label": "Houdini 17" - }, { - "type": "boolean", - "key": "houdini_18", - "label": "Houdini 18" }, { "type": "boolean", "key": "maya_2017", @@ -112,6 +100,10 @@ "type": "boolean", "key": "nukestudio_12.0", "label": "NukeStudio 12.0" + }, { + "type": "boolean", + "key": "houdini_16", + "label": "Houdini 16" }, { "type": "boolean", "key": "houdini_16.5", @@ -132,10 +124,6 @@ "type": "boolean", "key": "premiere_2020", "label": "Premiere 2020" - }, { - "type": "boolean", - "key": "premiere_2020", - "label": "Premiere 2020" }, { "type": "boolean", "key": "resolve_16", From 25404fbadceb0e3afe0e7167a3e623ff9de90733 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 26 Aug 2020 20:28:52 +0200 Subject: [PATCH 240/813] implemented validation for duplicated keys --- .../config_setting/widgets/lib.py | 70 ++++++++++++++++++- 1 file changed, 68 insertions(+), 2 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/lib.py b/pype/tools/config_setting/config_setting/widgets/lib.py index e7173f1c56..fe4e514aaf 100644 --- a/pype/tools/config_setting/config_setting/widgets/lib.py +++ b/pype/tools/config_setting/config_setting/widgets/lib.py @@ -2,7 +2,7 @@ import os import json import copy from pype.api import config - +from queue import Queue OVERRIDEN_KEY = config.OVERRIDEN_KEY @@ -107,6 +107,21 @@ class SchemeGroupHierarchyBug(Exception): super(SchemeGroupHierarchyBug, self).__init__(msg) +class SchemaDuplicatedKeys(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 keys in one hierarchy level. {}" + ).format(" || ".join(items)) + super(SchemaDuplicatedKeys, self).__init__(msg) + + def file_keys_from_schema(schema_data): output = [] keys = [] @@ -203,12 +218,63 @@ def validate_is_group_is_unique_in_hierarchy( raise SchemeGroupHierarchyBug(invalid) +def validate_keys_are_unique(schema_data, keys=None): + is_top = keys is None + if keys is None: + keys = [schema_data["key"]] + else: + keys.append(schema_data["key"]) + + children = schema_data.get("children") + if not children: + return + + child_queue = Queue() + for child in children: + child_queue.put(child) + + child_inputs = [] + while not child_queue.empty(): + child = child_queue.get() + if "key" not in child: + _children = child.get("children") or [] + for _child in _children: + child_queue.put(_child) + else: + child_inputs.append(child) + + duplicated_keys = set() + child_keys = set() + for child in child_inputs: + key = child["key"] + if key in child_keys: + duplicated_keys.add(key) + else: + child_keys.add(key) + + invalid = {} + if duplicated_keys: + joined_keys = "/".join(keys) + invalid[joined_keys] = duplicated_keys + + for child in child_inputs: + result = validate_keys_are_unique(child, copy.deepcopy(keys)) + if result: + invalid.update(result) + + if not is_top: + return invalid + + if invalid: + raise SchemaDuplicatedKeys(invalid) + + def validate_schema(schema_data): - # TODO validator for key uniquenes # TODO validator that is_group key is not before is_file child # TODO validator that is_group or is_file is not on child without key validate_all_has_ending_file(schema_data) validate_is_group_is_unique_in_hierarchy(schema_data) + validate_keys_are_unique(schema_data) def gui_schema(subfolder, main_schema_name): From 850ab0a820698d21af891f7a6fc1f9e4a10ca5f6 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 27 Aug 2020 11:01:46 +0100 Subject: [PATCH 241/813] Fix collect reviews - The code logic resulted in the last track review in the context being the review file for all shots. Comparing shot naming as well to isolate to correct clip instance. - Remove "- review" from label cause its already in subset. --- pype/plugins/nukestudio/publish/collect_reviews.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/pype/plugins/nukestudio/publish/collect_reviews.py b/pype/plugins/nukestudio/publish/collect_reviews.py index c158dee876..3167c66170 100644 --- a/pype/plugins/nukestudio/publish/collect_reviews.py +++ b/pype/plugins/nukestudio/publish/collect_reviews.py @@ -63,10 +63,14 @@ class CollectReviews(api.InstancePlugin): self.log.debug("Track item on plateMain") rev_inst = None for inst in instance.context[:]: - if inst.data["track"] in track: - rev_inst = inst - self.log.debug("Instance review: {}".format( - rev_inst.data["name"])) + if inst.data["track"] != track: + continue + + if inst.data["item"].name() != instance.data["item"].name(): + continue + + rev_inst = inst + break if rev_inst is None: raise RuntimeError(( @@ -82,7 +86,7 @@ class CollectReviews(api.InstancePlugin): ext = os.path.splitext(file)[-1][1:] # change label - instance.data["label"] = "{0} - {1} - ({2}) - review".format( + instance.data["label"] = "{0} - {1} - ({2})".format( instance.data['asset'], instance.data["subset"], ext ) From 7cdacafb8368b9aee8dd24e8647f1d5f8bde983f Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 27 Aug 2020 12:54:53 +0200 Subject: [PATCH 242/813] Moved PR from 2.x/develop --- .../modules/ftrack/actions/action_delivery.py | 364 +++++++++++++----- 1 file changed, 257 insertions(+), 107 deletions(-) diff --git a/pype/modules/ftrack/actions/action_delivery.py b/pype/modules/ftrack/actions/action_delivery.py index 7dbb7c65e8..86c3d604ee 100644 --- a/pype/modules/ftrack/actions/action_delivery.py +++ b/pype/modules/ftrack/actions/action_delivery.py @@ -1,5 +1,6 @@ import os import copy +import json import shutil import collections @@ -9,7 +10,7 @@ from bson.objectid import ObjectId from avalon import pipeline from avalon.vendor import filelink -from pype.api import Anatomy +from pype.api import Anatomy, config from pype.modules.ftrack.lib import BaseAction, statics_icon from pype.modules.ftrack.lib.avalon_sync import CustAttrIdKey from pype.modules.ftrack.lib.io_nonsingleton import DbConnector @@ -41,36 +42,22 @@ class Delivery(BaseAction): items = [] item_splitter = {"type": "label", "value": "---"} - # Prepare component names for processing - components = None - project = None - for entity in entities: - if project is None: - project_id = None - for ent_info in entity["link"]: - if ent_info["type"].lower() == "project": - project_id = ent_info["id"] - break + project_entity = self.get_project_from_entity(entities[0]) + project_name = project_entity["full_name"] + self.db_con.install() + self.db_con.Session["AVALON_PROJECT"] = project_name + project_doc = self.db_con.find_one({"type": "project"}) + if not project_doc: + return { + "success": False, + "message": ( + "Didn't found project \"{}\" in avalon." + ).format(project_name) + } - if project_id is None: - project = entity["asset"]["parent"]["project"] - else: - project = session.query(( - "select id, full_name from Project where id is \"{}\"" - ).format(project_id)).one() + repre_names = self._get_repre_names(entities) + self.db_con.uninstall() - _components = set( - [component["name"] for component in entity["components"]] - ) - if components is None: - components = _components - continue - - components = components.intersection(_components) - if not components: - break - - project_name = project["full_name"] items.append({ "type": "hidden", "name": "__project_name__", @@ -93,7 +80,7 @@ class Delivery(BaseAction): skipped = False # Add message if there are any common components - if not components or not new_anatomies: + if not repre_names or not new_anatomies: skipped = True items.append({ "type": "label", @@ -106,7 +93,7 @@ class Delivery(BaseAction): "value": skipped }) - if not components: + if not repre_names: if len(entities) == 1: items.append({ "type": "label", @@ -143,12 +130,12 @@ class Delivery(BaseAction): "type": "label" }) - for component in components: + for repre_name in repre_names: items.append({ "type": "boolean", "value": False, - "label": component, - "name": component + "label": repre_name, + "name": repre_name }) items.append(item_splitter) @@ -198,27 +185,231 @@ class Delivery(BaseAction): "title": title } + def _get_repre_names(self, entities): + version_ids = self._get_interest_version_ids(entities) + repre_docs = self.db_con.find({ + "type": "representation", + "parent": {"$in": version_ids} + }) + return list(sorted(repre_docs.distinct("name"))) + + def _get_interest_version_ids(self, entities): + parent_ent_by_id = {} + subset_names = set() + version_nums = set() + for entity in entities: + asset = entity["asset"] + parent = asset["parent"] + parent_ent_by_id[parent["id"]] = parent + + subset_name = asset["name"] + subset_names.add(subset_name) + + version = entity["version"] + version_nums.add(version) + + asset_docs_by_ftrack_id = self._get_asset_docs(parent_ent_by_id) + subset_docs = self._get_subset_docs( + asset_docs_by_ftrack_id, subset_names, entities + ) + version_docs = self._get_version_docs( + asset_docs_by_ftrack_id, subset_docs, version_nums, entities + ) + + return [version_doc["_id"] for version_doc in version_docs] + + def _get_version_docs( + self, asset_docs_by_ftrack_id, subset_docs, version_nums, entities + ): + subset_docs_by_id = { + subset_doc["_id"]: subset_doc + for subset_doc in subset_docs + } + version_docs = list(self.db_con.find({ + "type": "version", + "parent": {"$in": list(subset_docs_by_id.keys())}, + "name": {"$in": list(version_nums)} + })) + version_docs_by_parent_id = collections.defaultdict(dict) + for version_doc in version_docs: + subset_doc = subset_docs_by_id[version_doc["parent"]] + + asset_id = subset_doc["parent"] + subset_name = subset_doc["name"] + version = version_doc["name"] + if version_docs_by_parent_id[asset_id].get(subset_name) is None: + version_docs_by_parent_id[asset_id][subset_name] = {} + + version_docs_by_parent_id[asset_id][subset_name][version] = ( + version_doc + ) + + filtered_versions = [] + for entity in entities: + asset = entity["asset"] + + parent = asset["parent"] + asset_doc = asset_docs_by_ftrack_id[parent["id"]] + + subsets_by_name = version_docs_by_parent_id.get(asset_doc["_id"]) + if not subsets_by_name: + continue + + subset_name = asset["name"] + version_docs_by_version = subsets_by_name.get(subset_name) + if not version_docs_by_version: + continue + + version = entity["version"] + version_doc = version_docs_by_version.get(version) + if version_doc: + filtered_versions.append(version_doc) + return filtered_versions + + def _get_subset_docs( + self, asset_docs_by_ftrack_id, subset_names, entities + ): + asset_doc_ids = list() + for asset_doc in asset_docs_by_ftrack_id.values(): + asset_doc_ids.append(asset_doc["_id"]) + + subset_docs = list(self.db_con.find({ + "type": "subset", + "parent": {"$in": asset_doc_ids}, + "name": {"$in": list(subset_names)} + })) + subset_docs_by_parent_id = collections.defaultdict(dict) + for subset_doc in subset_docs: + asset_id = subset_doc["parent"] + subset_name = subset_doc["name"] + subset_docs_by_parent_id[asset_id][subset_name] = subset_doc + + filtered_subsets = [] + for entity in entities: + asset = entity["asset"] + + parent = asset["parent"] + asset_doc = asset_docs_by_ftrack_id[parent["id"]] + + subsets_by_name = subset_docs_by_parent_id.get(asset_doc["_id"]) + if not subsets_by_name: + continue + + subset_name = asset["name"] + subset_doc = subsets_by_name.get(subset_name) + if subset_doc: + filtered_subsets.append(subset_doc) + return filtered_subsets + + def _get_asset_docs(self, parent_ent_by_id): + asset_docs = list(self.db_con.find({ + "type": "asset", + "data.ftrackId": {"$in": list(parent_ent_by_id.keys())} + })) + asset_docs_by_ftrack_id = { + asset_doc["data"]["ftrackId"]: asset_doc + for asset_doc in asset_docs + } + + entities_by_mongo_id = {} + entities_by_names = {} + for ftrack_id, entity in parent_ent_by_id.items(): + if ftrack_id not in asset_docs_by_ftrack_id: + parent_mongo_id = entity["custom_attributes"].get( + CustAttrIdKey + ) + if parent_mongo_id: + entities_by_mongo_id[ObjectId(parent_mongo_id)] = entity + else: + entities_by_names[entity["name"]] = entity + + expressions = [] + if entities_by_mongo_id: + expression = { + "type": "asset", + "_id": {"$in": list(entities_by_mongo_id.keys())} + } + expressions.append(expression) + + if entities_by_names: + expression = { + "type": "asset", + "name": {"$in": list(entities_by_names.keys())} + } + expressions.append(expression) + + if expressions: + if len(expressions) == 1: + filter = expressions[0] + else: + filter = {"$or": expressions} + + asset_docs = self.db_con.find(filter) + for asset_doc in asset_docs: + if asset_doc["_id"] in entities_by_mongo_id: + entity = entities_by_mongo_id[asset_doc["_id"]] + asset_docs_by_ftrack_id[entity["id"]] = asset_doc + + elif asset_doc["name"] in entities_by_names: + entity = entities_by_names[asset_doc["name"]] + asset_docs_by_ftrack_id[entity["id"]] = asset_doc + + return asset_docs_by_ftrack_id + def launch(self, session, entities, event): if "values" not in event["data"]: return - self.report_items = collections.defaultdict(list) - values = event["data"]["values"] skipped = values.pop("__skipped__") if skipped: return None - component_names = [] + user_id = event["source"]["user"]["id"] + user_entity = session.query("User where id is ".format(user_id)).one() + + job = session.create("Job", { + "user": user_entity, + "status": "running", + "data": json.dumps({ + "description": "Delivery processing." + }) + }) + session.commit() + + try: + self.db_con.install() + self.real_launch(session, entities, event) + job["status"] = "done" + + except Exception: + self.log.warning( + "Failed during processing delivery action.", + exc_info=True + ) + + finally: + if job["status"] != "done": + job["status"] = "failed" + session.commit() + self.db_con.uninstall() + + def real_launch(self, session, entities, event): + self.log.info("Delivery action just started.") + report_items = collections.defaultdict(list) + + values = event["data"]["values"] + location_path = values.pop("__location_path__") anatomy_name = values.pop("__new_anatomies__") project_name = values.pop("__project_name__") + repre_names = [] for key, value in values.items(): if value is True: - component_names.append(key) + repre_names.append(key) - if not component_names: + if not repre_names: return { "success": True, "message": "Not selected components to deliver." @@ -230,64 +421,15 @@ class Delivery(BaseAction): if not os.path.exists(location_path): os.makedirs(location_path) - self.db_con.install() self.db_con.Session["AVALON_PROJECT"] = project_name - repres_to_deliver = [] - for entity in entities: - asset = entity["asset"] - subset_name = asset["name"] - version = entity["version"] - - parent = asset["parent"] - parent_mongo_id = parent["custom_attributes"].get(CustAttrIdKey) - if parent_mongo_id: - parent_mongo_id = ObjectId(parent_mongo_id) - else: - asset_ent = self.db_con.find_one({ - "type": "asset", - "data.ftrackId": parent["id"] - }) - if not asset_ent: - ent_path = "/".join( - [ent["name"] for ent in parent["link"]] - ) - msg = "Not synchronized entities to avalon" - self.report_items[msg].append(ent_path) - self.log.warning("{} <{}>".format(msg, ent_path)) - continue - - parent_mongo_id = asset_ent["_id"] - - subset_ent = self.db_con.find_one({ - "type": "subset", - "parent": parent_mongo_id, - "name": subset_name - }) - - version_ent = self.db_con.find_one({ - "type": "version", - "name": version, - "parent": subset_ent["_id"] - }) - - repre_ents = self.db_con.find({ - "type": "representation", - "parent": version_ent["_id"] - }) - - repres_by_name = {} - for repre in repre_ents: - repre_name = repre["name"] - repres_by_name[repre_name] = repre - - for component in entity["components"]: - comp_name = component["name"] - if comp_name not in component_names: - continue - - repre = repres_by_name.get(comp_name) - repres_to_deliver.append(repre) + self.log.debug("Collecting representations to process.") + version_ids = self._get_interest_version_ids(entities) + repres_to_deliver = list(self.db_con.find({ + "type": "representation", + "parent": {"$in": version_ids}, + "name": {"$in": repre_names} + })) anatomy = Anatomy(project_name) @@ -304,9 +446,17 @@ class Delivery(BaseAction): for name in root_names: format_dict["root"][name] = location_path + datetime_data = config.get_datetime_data() for repre in repres_to_deliver: + source_path = repre.get("data", {}).get("path") + debug_msg = "Processing representation {}".format(repre["_id"]) + if source_path: + debug_msg += " with published path {}.".format(source_path) + self.log.debug(debug_msg) + # Get destination repre path anatomy_data = copy.deepcopy(repre["context"]) + anatomy_data.update(datetime_data) anatomy_filled = anatomy.format_all(anatomy_data) test_path = anatomy_filled["delivery"][anatomy_name] @@ -333,7 +483,7 @@ class Delivery(BaseAction): "- Invalid value DataType: \"{}\"
" ).format(str(repre["_id"]), keys) - self.report_items[msg].append(sub_msg) + report_items[msg].append(sub_msg) self.log.warning( "{} Representation: \"{}\" Filled: <{}>".format( msg, str(repre["_id"]), str(test_path) @@ -355,20 +505,19 @@ class Delivery(BaseAction): anatomy, anatomy_name, anatomy_data, - format_dict + format_dict, + report_items ) - if not frame: self.process_single_file(*args) else: self.process_sequence(*args) - self.db_con.uninstall() - - return self.report() + return self.report(report_items) def process_single_file( - self, repre_path, anatomy, anatomy_name, anatomy_data, format_dict + self, repre_path, anatomy, anatomy_name, anatomy_data, format_dict, + report_items ): anatomy_filled = anatomy.format(anatomy_data) if format_dict: @@ -384,7 +533,8 @@ class Delivery(BaseAction): self.copy_file(repre_path, delivery_path) def process_sequence( - self, repre_path, anatomy, anatomy_name, anatomy_data, format_dict + self, repre_path, anatomy, anatomy_name, anatomy_data, format_dict, + report_items ): dir_path, file_name = os.path.split(str(repre_path)) @@ -398,7 +548,7 @@ class Delivery(BaseAction): if not file_name_items: msg = "Source file was not found" - self.report_items[msg].append(repre_path) + report_items[msg].append(repre_path) self.log.warning("{} <{}>".format(msg, repre_path)) return @@ -418,7 +568,7 @@ class Delivery(BaseAction): if src_collection is None: # TODO log error! msg = "Source collection of files was not found" - self.report_items[msg].append(repre_path) + report_items[msg].append(repre_path) self.log.warning("{} <{}>".format(msg, repre_path)) return @@ -491,10 +641,10 @@ class Delivery(BaseAction): except OSError: shutil.copyfile(src_path, dst_path) - def report(self): + def report(self, report_items): items = [] title = "Delivery report" - for msg, _items in self.report_items.items(): + for msg, _items in report_items.items(): if not _items: continue From 4466db6e1242091946f37aa58acf3dc64abb78e5 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 27 Aug 2020 14:02:38 +0200 Subject: [PATCH 243/813] fix user id missing formatting brackets --- pype/modules/ftrack/actions/action_delivery.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pype/modules/ftrack/actions/action_delivery.py b/pype/modules/ftrack/actions/action_delivery.py index 86c3d604ee..663a81aad4 100644 --- a/pype/modules/ftrack/actions/action_delivery.py +++ b/pype/modules/ftrack/actions/action_delivery.py @@ -366,7 +366,9 @@ class Delivery(BaseAction): return None user_id = event["source"]["user"]["id"] - user_entity = session.query("User where id is ".format(user_id)).one() + user_entity = session.query( + "User where id is {}".format(user_id) + ).one() job = session.create("Job", { "user": user_entity, From 1c3e8c38fed53420866d24354d13fc4dfab73440 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 27 Aug 2020 14:39:44 +0200 Subject: [PATCH 244/813] update version.py --- pype/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/version.py b/pype/version.py index 0d4f03098b..9e1a271244 100644 --- a/pype/version.py +++ b/pype/version.py @@ -1 +1 @@ -__version__ = "2.11.5" +__version__ = "2.11.8" From e074bc7733fa9464bbb0abef8f4011f56dcba6b3 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 27 Aug 2020 16:01:19 +0200 Subject: [PATCH 245/813] added hovering actions --- .../config_setting/config_setting/style/style.css | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pype/tools/config_setting/config_setting/style/style.css b/pype/tools/config_setting/config_setting/style/style.css index c5f1f33500..9478892b60 100644 --- a/pype/tools/config_setting/config_setting/style/style.css +++ b/pype/tools/config_setting/config_setting/style/style.css @@ -119,16 +119,29 @@ QPushButton[btn-type="expand-toggle"] { border-right-width: 0px; border-top-width: 0px; } +#ExpandingWidget:hover, #ModifiableDict:hover, #DictWidget:hover { + border-color: #62839d; +} + #ExpandingWidget[state="child-modified"], #ModifiableDict[state="child-modified"], #DictWidget[state="child-modified"] { + border-color: #106aa2; +} +#ExpandingWidget[state="child-modified"]:hover, #ModifiableDict[state="child-modified"]:hover, #DictWidget[state="child-modified"]:hover { border-color: #137cbd; } #ExpandingWidget[state="child-overriden"], #ModifiableDict[state="child-overriden"], #DictWidget[state="child-overriden"] { + border-color: #e67300; +} +#ExpandingWidget[state="child-overriden"]:hover, #ModifiableDict[state="child-overriden"]:hover, #DictWidget[state="child-overriden"]:hover { border-color: #ff8c1a; } #ExpandingWidget[state="child-overriden-modified"], #ModifiableDict[state="child-overriden-modified"], #DictWidget[state="child-overriden-modified"] { + border-color: #106aa2; +} +#ExpandingWidget[state="child-overriden-modified"]:hover, #ModifiableDict[state="child-overriden-modified"]:hover, #DictWidget[state="child-overriden-modified"]:hover { border-color: #137cbd; } From ae30e3c3befb9fecc32bc823ad52c8229d48f814 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 27 Aug 2020 16:07:06 +0200 Subject: [PATCH 246/813] fixed raw json item value --- pype/tools/config_setting/config_setting/widgets/inputs.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index d4806b4a5d..8036eb5e1b 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -777,6 +777,10 @@ class RawJsonInput(QtWidgets.QPlainTextEdit): self.updateGeometry() super(RawJsonInput, self).resizeEvent(event) + def item_value(self): + value = self.value() + return json.loads(value) + def value(self): return self.toPlainText() @@ -891,7 +895,7 @@ class RawJsonWidget(ConfigWidget, InputObject): widget.style().polish(widget) def item_value(self): - return self.text_input.toPlainText() + return self.text_input.item_value() class TextListItem(QtWidgets.QWidget, ConfigObject): From b8ea2eea3a153f5abe0624735b445be50a14941c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 27 Aug 2020 16:12:54 +0200 Subject: [PATCH 247/813] fixed text list items --- pype/tools/config_setting/config_setting/widgets/inputs.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 8036eb5e1b..67f1ac81ec 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -977,12 +977,13 @@ class TextListSubWidget(QtWidgets.QWidget, ConfigObject): self.override_value = NOT_SET def set_value(self, value, *, global_value=False): - for input_field in self.input_fields: - self.remove_row(input_field) - + previous_inputs = tuple(self.input_fields) for item_text in value: self.add_row(text=item_text) + for input_field in previous_inputs: + self.remove_row(input_field) + if global_value: self.global_value = value self.start_value = self.item_value() From 3c71ae5d2a28546c7e523cf9f3042cc2429ae589 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 27 Aug 2020 16:21:36 +0200 Subject: [PATCH 248/813] raw json works better --- .../config_setting/widgets/inputs.py | 49 ++++++++++--------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 67f1ac81ec..d800f8c635 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -709,6 +709,7 @@ class TextMultiLineWidget(ConfigWidget, InputObject): class RawJsonInput(QtWidgets.QPlainTextEdit): + value_changed = QtCore.Signal(object) tab_length = 4 def __init__(self, *args, **kwargs): @@ -720,7 +721,9 @@ class RawJsonInput(QtWidgets.QPlainTextEdit): ).horizontalAdvance(" ") * self.tab_length ) + self._state = None self.is_valid = None + self.textChanged.connect(self._on_value_change) def sizeHint(self): document = self.document() @@ -742,13 +745,9 @@ class RawJsonInput(QtWidgets.QPlainTextEdit): value = json.dumps(value, indent=4) self.setPlainText(value) - def setPlainText(self, *args, **kwargs): - super(RawJsonInput, self).setPlainText(*args, **kwargs) - self.validate() - - def focusOutEvent(self, event): - super(RawJsonInput, self).focusOutEvent(event) + def _on_value_change(self): self.validate() + self.value_changed.emit(self) def validate_value(self, value): if isinstance(value, str) and not value: @@ -760,16 +759,18 @@ class RawJsonInput(QtWidgets.QPlainTextEdit): except Exception: return False - def update_style(self, is_valid=None): - if is_valid is None: + def update_style(self): + if self.is_valid is None: return self.validate() - if is_valid != self.is_valid: - self.is_valid = is_valid - if is_valid: - state = "" - else: - state = "invalid" + if self.is_valid: + state = "" + else: + state = "invalid" + + if self._state is None or self._state != state: + self._state = state + self.setProperty("state", state) self.style().polish(self) @@ -778,16 +779,12 @@ class RawJsonInput(QtWidgets.QPlainTextEdit): super(RawJsonInput, self).resizeEvent(event) def item_value(self): - value = self.value() - return json.loads(value) - - def value(self): - return self.toPlainText() + return json.loads(self.toPlainText()) def validate(self): - value = self.value() - is_valid = self.validate_value(value) - self.update_style(is_valid) + value = self.toPlainText() + self.is_valid = self.validate_value(value) + self.update_style() class RawJsonWidget(ConfigWidget, InputObject): @@ -838,7 +835,7 @@ class RawJsonWidget(ConfigWidget, InputObject): self.start_value = self.item_value() self.override_value = NOT_SET - self.text_input.textChanged.connect(self._on_value_change) + self.text_input.value_changed.connect(self._on_value_change) def set_value(self, value, *, global_value=False): self.text_input.set_value(value) @@ -871,7 +868,11 @@ class RawJsonWidget(ConfigWidget, InputObject): if self.ignore_value_changes: return - self._is_modified = self.item_value() != self.global_value + if self.text_input.is_valid: + self._is_modified = self.item_value() != self.global_value + else: + self._is_modified = True + if self.is_overidable: self._is_overriden = True From 9af852b0cfd21eb03e9812dffe2fa83fd32e0737 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 27 Aug 2020 16:24:34 +0200 Subject: [PATCH 249/813] invalid looks better --- pype/tools/config_setting/config_setting/style/style.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pype/tools/config_setting/config_setting/style/style.css b/pype/tools/config_setting/config_setting/style/style.css index 9478892b60..ce014f0768 100644 --- a/pype/tools/config_setting/config_setting/style/style.css +++ b/pype/tools/config_setting/config_setting/style/style.css @@ -93,7 +93,8 @@ QPushButton[btn-type="expand-toggle"] { } #RawJsonInput[state="invalid"] { - border-left-color: #ff5511; + border-color: #ab2e46; + border-width: 2px; } #DictKey[state="modified"] { From 3fdcff6185771979966f47348e27e25d65f04f78 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 27 Aug 2020 16:28:11 +0200 Subject: [PATCH 250/813] reset state on set value in json raw input --- pype/tools/config_setting/config_setting/widgets/inputs.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index d800f8c635..6568543799 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -741,6 +741,7 @@ class RawJsonInput(QtWidgets.QPlainTextEdit): return value def set_value(self, value, *, global_value=False): + self._state = None if not isinstance(value, str): value = json.dumps(value, indent=4) self.setPlainText(value) From 39a1a560aa485a456eb7d4435a6686fdee780f7e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 27 Aug 2020 16:41:45 +0200 Subject: [PATCH 251/813] fixed json files with list inside --- pype/tools/config_setting/config_setting/widgets/base.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pype/tools/config_setting/config_setting/widgets/base.py b/pype/tools/config_setting/config_setting/widgets/base.py index c0246dd8a2..d5f9a05aea 100644 --- a/pype/tools/config_setting/config_setting/widgets/base.py +++ b/pype/tools/config_setting/config_setting/widgets/base.py @@ -469,7 +469,10 @@ class ProjectWidget(QtWidgets.QWidget): new_values = all_values for key in key_sequence: new_values = new_values[key] - origin_values.update(new_values) + if isinstance(new_values, dict): + origin_values.update(new_values) + else: + origin_values = new_values output_path = os.path.join( config.PROJECT_PRESETS_PATH, subpath From 5fe6a1e25d5e624980dd4d8a7577e04e586f5e5a Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 27 Aug 2020 17:08:53 +0200 Subject: [PATCH 252/813] fixed list input --- pype/tools/config_setting/config_setting/widgets/inputs.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 6568543799..c7d02471a7 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1096,8 +1096,9 @@ class TextListWidget(ConfigWidget, InputObject): layout.addWidget(label_widget) self.label_widget = label_widget + self.key = input_data["key"] keys = list(parent_keys) - keys.append(input_data["key"]) + keys.append(self.key) self.keys = keys self.value_widget = TextListSubWidget( @@ -1107,7 +1108,7 @@ class TextListWidget(ConfigWidget, InputObject): self.value_widget.value_changed.connect(self._on_value_change) # self.value_widget.se - self.key = input_data["key"] + layout.addWidget(self.value_widget) self.setLayout(layout) @@ -1160,7 +1161,7 @@ class TextListWidget(ConfigWidget, InputObject): self.label_widget.style().polish(self.label_widget) def item_value(self): - return self.value_widget.config_value() + return self.value_widget.item_value() class ModifiableDictItem(QtWidgets.QWidget, ConfigObject): From 66f99b321194493a5933be3fa5ac18adc745daa7 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 27 Aug 2020 17:46:41 +0200 Subject: [PATCH 253/813] list item does not have focus on btns --- pype/tools/config_setting/config_setting/widgets/inputs.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index c7d02471a7..4cba89f251 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -914,6 +914,8 @@ class TextListItem(QtWidgets.QWidget, ConfigObject): self.text_input = QtWidgets.QLineEdit() self.add_btn = QtWidgets.QPushButton("+") self.remove_btn = QtWidgets.QPushButton("-") + self.add_btn.setFocusPolicy(QtCore.Qt.ClickFocus) + self.remove_btn.setFocusPolicy(QtCore.Qt.ClickFocus) self.add_btn.setProperty("btn-type", "text-list") self.remove_btn.setProperty("btn-type", "text-list") From 893173e1b8511d4f89754c5b53897eade89687e1 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 27 Aug 2020 17:46:57 +0200 Subject: [PATCH 254/813] list items has right order --- pype/tools/config_setting/config_setting/widgets/inputs.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 4cba89f251..d353b6e53e 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1037,6 +1037,12 @@ class TextListSubWidget(QtWidgets.QWidget, ConfigObject): self.layout().insertWidget(row, item_widget) self.input_fields.insert(row, item_widget) + previous_input = None + for input_field in self.input_fields: + if previous_input is not None: + self.setTabOrder(previous_input, input_field.text_input) + previous_input = input_field.text_input + # Set text if entered text is not None # else (when add button clicked) trigger `_on_value_change` if text is not None: From 9fbdfcc1cf4a87e2a84ff85c18ff027b3ed7ad60 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 27 Aug 2020 17:50:16 +0200 Subject: [PATCH 255/813] fixed tab order of key->value for modifieble dict item --- pype/tools/config_setting/config_setting/widgets/inputs.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index d353b6e53e..74505bebca 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1187,7 +1187,7 @@ class ModifiableDictItem(QtWidgets.QWidget, ConfigObject): ItemKlass = TypeToKlass.types[object_type] - self.key_input = QtWidgets.QLineEdit() + self.key_input = QtWidgets.QLineEdit(self) self.key_input.setObjectName("DictKey") self.value_input = ItemKlass( @@ -1200,6 +1200,9 @@ class ModifiableDictItem(QtWidgets.QWidget, ConfigObject): self.add_btn = QtWidgets.QPushButton("+") self.remove_btn = QtWidgets.QPushButton("-") + self.add_btn.setFocusPolicy(QtCore.Qt.ClickFocus) + self.remove_btn.setFocusPolicy(QtCore.Qt.ClickFocus) + self.add_btn.setProperty("btn-type", "text-list") self.remove_btn.setProperty("btn-type", "text-list") From a3b6d64004bb249ab6524e4c3dd4bdfdc68bae07 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 27 Aug 2020 18:22:05 +0200 Subject: [PATCH 256/813] fixed most issues with tab order in modifiable dict --- .../config_setting/widgets/inputs.py | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 74505bebca..172b93e4ba 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -181,6 +181,9 @@ class BooleanWidget(ConfigWidget, InputObject): layout.setSpacing(5) self.checkbox = QtWidgets.QCheckBox() + + self.setFocusProxy(self.checkbox) + self.checkbox.setAttribute(QtCore.Qt.WA_StyledBackground) if not self._as_widget and not label_widget: label = input_data["label"] @@ -300,6 +303,8 @@ class IntegerWidget(ConfigWidget, InputObject): self.int_input = ModifiedIntSpinBox() + self.setFocusProxy(self.int_input) + if not self._as_widget and not label_widget: label = input_data["label"] label_widget = QtWidgets.QLabel(label) @@ -406,6 +411,8 @@ class FloatWidget(ConfigWidget, InputObject): self.float_input = ModifiedFloatSpinBox() + self.setFocusProxy(self.float_input) + decimals = input_data.get("decimals", 5) maximum = input_data.get("maximum") minimum = input_data.get("minimum") @@ -520,6 +527,8 @@ class TextSingleLineWidget(ConfigWidget, InputObject): self.text_input = QtWidgets.QLineEdit() + self.setFocusProxy(self.text_input) + if not self._as_widget and not label_widget: label = input_data["label"] label_widget = QtWidgets.QLabel(label) @@ -625,6 +634,9 @@ class TextMultiLineWidget(ConfigWidget, InputObject): layout.setSpacing(5) self.text_input = QtWidgets.QPlainTextEdit() + + self.setFocusProxy(self.text_input) + if not self._as_widget and not label_widget: label = input_data["label"] label_widget = QtWidgets.QLabel(label) @@ -813,6 +825,8 @@ class RawJsonWidget(ConfigWidget, InputObject): QtWidgets.QSizePolicy.MinimumExpanding ) + self.setFocusProxy(self.text_input) + if not self._as_widget and not label_widget: label = input_data["label"] label_widget = QtWidgets.QLabel(label) @@ -1211,6 +1225,8 @@ class ModifiableDictItem(QtWidgets.QWidget, ConfigObject): layout.addWidget(self.add_btn, 0) layout.addWidget(self.remove_btn, 0) + self.setFocusProxy(self.value_input) + self.add_btn.setFixedSize(self._btn_size, self._btn_size) self.remove_btn.setFixedSize(self._btn_size, self._btn_size) self.add_btn.clicked.connect(self.on_add_clicked) @@ -1352,6 +1368,17 @@ class ModifiableDictSubWidget(QtWidgets.QWidget, ConfigObject): self.layout().insertWidget(row, item_widget) self.input_fields.insert(row, item_widget) + previous_input = None + for input_field in self.input_fields: + if previous_input is not None: + self.setTabOrder( + previous_input, input_field.key_input + ) + previous_input = input_field.value_input.focusProxy() + self.setTabOrder( + input_field.key_input, previous_input + ) + # Set value if entered value is not None # else (when add button clicked) trigger `_on_value_change` if value is not None and key is not None: From 06359c6dc335779f251dc426af8d6bda5be76422 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 27 Aug 2020 19:22:15 +0200 Subject: [PATCH 257/813] Hound --- pype/modules/websocket_server/hosts/photoshop.py | 2 +- .../modules/websocket_server/stubs/photoshop_server_stub.py | 3 +-- pype/modules/websocket_server/websocket_server.py | 6 +++--- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/pype/modules/websocket_server/hosts/photoshop.py b/pype/modules/websocket_server/hosts/photoshop.py index ae72963b1b..cdfb9413a0 100644 --- a/pype/modules/websocket_server/hosts/photoshop.py +++ b/pype/modules/websocket_server/hosts/photoshop.py @@ -61,4 +61,4 @@ class Photoshop(WebSocketRoute): photoshop.execute_in_main_thread(partial_method) # Required return statement. - return "nothing" \ No newline at end of file + return "nothing" diff --git a/pype/modules/websocket_server/stubs/photoshop_server_stub.py b/pype/modules/websocket_server/stubs/photoshop_server_stub.py index f798a09b92..da69127799 100644 --- a/pype/modules/websocket_server/stubs/photoshop_server_stub.py +++ b/pype/modules/websocket_server/stubs/photoshop_server_stub.py @@ -164,8 +164,7 @@ class PhotoshopServerStub(): :return: full path with name """ res = self.websocketserver.call( - self.client.call - ('Photoshop.get_active_document_full_name')) + self.client.call('Photoshop.get_active_document_full_name')) return res diff --git a/pype/modules/websocket_server/websocket_server.py b/pype/modules/websocket_server/websocket_server.py index 777bcf1f61..4556dd0491 100644 --- a/pype/modules/websocket_server/websocket_server.py +++ b/pype/modules/websocket_server/websocket_server.py @@ -1,4 +1,4 @@ -from pype.api import config, Logger +from pype.api import Logger import threading from aiohttp import web @@ -37,8 +37,8 @@ class WebSocketServer(): if websocket_url: parsed = urllib.parse.urlparse(websocket_url) port = parsed.port - if not port: - port = 8099 # try default port + if not port: + port = 8099 # fallback self.app = web.Application() From 401d1ad6132d7fd4dcc781150e41ca7785f9a73a Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 28 Aug 2020 10:04:04 +0200 Subject: [PATCH 258/813] make sure ignore_value_changes will raise proper error --- pype/tools/config_setting/config_setting/widgets/inputs.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 172b93e4ba..050966dd63 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -38,6 +38,10 @@ class ConfigObject: @property def ignore_value_changes(self): + if not hasattr(self, "_parent"): + raise NotImplementedError( + "Object {} does not have `_parent` attribute".format(self) + ) return self._parent.ignore_value_changes @ignore_value_changes.setter From e2061c230f8023e0e150ec65afbb793bb6294248 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 28 Aug 2020 10:04:24 +0200 Subject: [PATCH 259/813] set parenting to real input widgets --- .../config_setting/config_setting/widgets/inputs.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 050966dd63..8f4d15912d 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -184,7 +184,7 @@ class BooleanWidget(ConfigWidget, InputObject): layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(5) - self.checkbox = QtWidgets.QCheckBox() + self.checkbox = QtWidgets.QCheckBox(self) self.setFocusProxy(self.checkbox) @@ -305,7 +305,7 @@ class IntegerWidget(ConfigWidget, InputObject): layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(5) - self.int_input = ModifiedIntSpinBox() + self.int_input = ModifiedIntSpinBox(self) self.setFocusProxy(self.int_input) @@ -413,7 +413,7 @@ class FloatWidget(ConfigWidget, InputObject): layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(5) - self.float_input = ModifiedFloatSpinBox() + self.float_input = ModifiedFloatSpinBox(self) self.setFocusProxy(self.float_input) @@ -529,7 +529,7 @@ class TextSingleLineWidget(ConfigWidget, InputObject): layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(5) - self.text_input = QtWidgets.QLineEdit() + self.text_input = QtWidgets.QLineEdit(self) self.setFocusProxy(self.text_input) @@ -637,7 +637,7 @@ class TextMultiLineWidget(ConfigWidget, InputObject): layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(5) - self.text_input = QtWidgets.QPlainTextEdit() + self.text_input = QtWidgets.QPlainTextEdit(self) self.setFocusProxy(self.text_input) From 3edb2f4080f0bfadbd41732b8f32eec673afe458 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 28 Aug 2020 10:05:30 +0200 Subject: [PATCH 260/813] TextListWidget changed to ListWidget --- .../config_setting/widgets/inputs.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 8f4d15912d..67e44023e7 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1097,7 +1097,7 @@ class TextListSubWidget(QtWidgets.QWidget, ConfigObject): return output -class TextListWidget(ConfigWidget, InputObject): +class ListWidget(ConfigWidget, InputObject): value_changed = QtCore.Signal(object) def __init__( @@ -1109,8 +1109,8 @@ class TextListWidget(ConfigWidget, InputObject): self._state = None - super(TextListWidget, self).__init__(parent) - self.setObjectName("TextListWidget") + super(ListWidget, self).__init__(parent) + self.setObjectName("ListWidget") layout = QtWidgets.QVBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) @@ -1127,17 +1127,16 @@ class TextListWidget(ConfigWidget, InputObject): keys.append(self.key) self.keys = keys - self.value_widget = TextListSubWidget( + self.value_widget = ListSubWidget( input_data, values, parent_keys, self ) self.value_widget.setAttribute(QtCore.Qt.WA_StyledBackground) - self.value_widget.value_changed.connect(self._on_value_change) - - # self.value_widget.se layout.addWidget(self.value_widget) self.setLayout(layout) + self.value_widget.value_changed.connect(self._on_value_change) + @property def start_value(self): return self.value_widget.start_value @@ -2178,4 +2177,4 @@ TypeToKlass.types["dict"] = DictWidget TypeToKlass.types["dict-expanding"] = DictExpandWidget TypeToKlass.types["dict-form"] = DictFormWidget TypeToKlass.types["dict-invisible"] = DictInvisible -TypeToKlass.types["list-text"] = TextListWidget +TypeToKlass.types["list"] = ListWidget From 4cfc0a7c1210024c709fe56c3b9cd45548d87731 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 28 Aug 2020 10:06:46 +0200 Subject: [PATCH 261/813] TextListItem changed to ListItem --- .../config_setting/widgets/inputs.py | 33 +++++++++++++------ 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 67e44023e7..e10a4a841d 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -918,37 +918,50 @@ class RawJsonWidget(ConfigWidget, InputObject): return self.text_input.item_value() -class TextListItem(QtWidgets.QWidget, ConfigObject): +class ListItem(QtWidgets.QWidget, ConfigObject): _btn_size = 20 value_changed = QtCore.Signal(object) - def __init__(self, parent): - super(TextListItem, self).__init__(parent) + def __init__(self, object_type, parent): + self._parent = parent + + super(ListItem, self).__init__(parent) layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(3) - self.text_input = QtWidgets.QLineEdit() + ItemKlass = TypeToKlass.types[object_type] + self.value_input = ItemKlass( + {}, + AS_WIDGET, + [], + self, + None + ) + self.add_btn = QtWidgets.QPushButton("+") self.remove_btn = QtWidgets.QPushButton("-") + self.add_btn.setFocusPolicy(QtCore.Qt.ClickFocus) self.remove_btn.setFocusPolicy(QtCore.Qt.ClickFocus) + self.add_btn.setFixedSize(self._btn_size, self._btn_size) + self.remove_btn.setFixedSize(self._btn_size, self._btn_size) + self.add_btn.setProperty("btn-type", "text-list") self.remove_btn.setProperty("btn-type", "text-list") - layout.addWidget(self.text_input, 1) + layout.addWidget(self.value_input, 1) layout.addWidget(self.add_btn, 0) layout.addWidget(self.remove_btn, 0) - self.add_btn.setFixedSize(self._btn_size, self._btn_size) - self.remove_btn.setFixedSize(self._btn_size, self._btn_size) self.add_btn.clicked.connect(self.on_add_clicked) self.remove_btn.clicked.connect(self.on_remove_clicked) - self.text_input.textChanged.connect(self._on_value_change) + self.value_input.value_changed.connect(self._on_value_change) + self.value_input.item_value() self.is_single = False def _on_value_change(self, item=None): @@ -962,12 +975,12 @@ class TextListItem(QtWidgets.QWidget, ConfigObject): def on_remove_clicked(self): if self.is_single: - self.text_input.setText("") + self.value_input.clear() else: self.parent().remove_row(self) def config_value(self): - return self.text_input.text() + return self.value_input.item_value() class TextListSubWidget(QtWidgets.QWidget, ConfigObject): From d82118cd8e8cc869ca04481d15a88e71fecb0373 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 28 Aug 2020 10:07:06 +0200 Subject: [PATCH 262/813] TextListSubWidget changed to ListSubWidget --- .../config_setting/widgets/inputs.py | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index e10a4a841d..2f0d85fd4e 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -983,12 +983,14 @@ class ListItem(QtWidgets.QWidget, ConfigObject): return self.value_input.item_value() -class TextListSubWidget(QtWidgets.QWidget, ConfigObject): +class ListSubWidget(QtWidgets.QWidget, ConfigObject): value_changed = QtCore.Signal(object) def __init__(self, input_data, values, parent_keys, parent): - super(TextListSubWidget, self).__init__(parent) - self.setObjectName("TextListSubWidget") + self._parent = parent + + super(ListSubWidget, self).__init__(parent) + self.setObjectName("ListSubWidget") layout = QtWidgets.QVBoxLayout(self) layout.setContentsMargins(0, 5, 0, 5) @@ -996,7 +998,7 @@ class TextListSubWidget(QtWidgets.QWidget, ConfigObject): self.setLayout(layout) self.input_fields = [] - self.add_row() + self.object_type = input_data["object_type"] self.key = input_data["key"] keys = list(parent_keys) @@ -1005,7 +1007,11 @@ class TextListSubWidget(QtWidgets.QWidget, ConfigObject): value = self.value_from_values(values) if value is not NOT_SET: - self.set_value(value) + for item_value in value: + self.add_row(value=item_value) + + if self.count() == 0: + self.add_row() self.global_value = value self.start_value = self.item_value() @@ -1013,8 +1019,8 @@ class TextListSubWidget(QtWidgets.QWidget, ConfigObject): def set_value(self, value, *, global_value=False): previous_inputs = tuple(self.input_fields) - for item_text in value: - self.add_row(text=item_text) + for item_value in value: + self.add_row(value=item_value) for input_field in previous_inputs: self.remove_row(input_field) @@ -1047,9 +1053,9 @@ class TextListSubWidget(QtWidgets.QWidget, ConfigObject): def count(self): return len(self.input_fields) - def add_row(self, row=None, text=None): + def add_row(self, row=None, value=None): # Create new item - item_widget = TextListItem(self) + item_widget = ListItem(self.object_type, self) # Set/unset if new item is single item current_count = self.count() @@ -1071,13 +1077,15 @@ class TextListSubWidget(QtWidgets.QWidget, ConfigObject): previous_input = None for input_field in self.input_fields: if previous_input is not None: - self.setTabOrder(previous_input, input_field.text_input) - previous_input = input_field.text_input + self.setTabOrder( + previous_input, input_field.value_input.focusProxy() + ) + previous_input = input_field.value_input.focusProxy() # Set text if entered text is not None # else (when add button clicked) trigger `_on_value_change` - if text is not None: - item_widget.text_input.setText(text) + if value is not None: + item_widget.value_input.set_value(value, global_value=True) else: self._on_value_change() self.parent().updateGeometry() From 710c97f9a1ee0af00a66c5f0e9cd275b6db21df1 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 28 Aug 2020 10:08:25 +0200 Subject: [PATCH 263/813] use "list" instead of "list-text" --- .../projects_schema/1_plugins_gui_schema.json | 12 ++++++++---- .../studio_schema/0_studio_gui_schema.json | 10 +++++----- .../studio_schema/1_intents_gui_schema.json | 4 ++-- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json index e6582d82b1..9f40c7871b 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json @@ -88,7 +88,8 @@ "label": "Note with intent template", "default": "{intent}: {comment}" }, { - "type": "list-text", + "type": "list", + "object_type": "text-singleline", "key": "note_labels", "label": "Note labels", "default": [] @@ -132,12 +133,14 @@ "key": "ffmpeg_args", "children": [ { - "type": "list-text", + "type": "list", + "object_type": "text-singleline", "key": "input", "label": "FFmpeg input arguments", "default": [] }, { - "type": "list-text", + "type": "list", + "object_type": "text-singleline", "key": "output", "label": "FFmpeg output arguments", "default": [] @@ -532,7 +535,8 @@ "label": "Enabled", "default": true }, { - "type": "list-text", + "type": "list", + "object_type": "text-singleline", "key": "tags_addition", "label": "Tags addition", "default": [] diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/0_studio_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/0_studio_gui_schema.json index 2fd63c7cdc..921122166b 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/0_studio_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/0_studio_gui_schema.json @@ -4,8 +4,8 @@ "label": "Studio", "children": [ { - "key": "global", "type": "dict-invisible", + "key": "global", "label": "Global", "children": [{ "type": "schema", @@ -17,15 +17,15 @@ ] }] }, { - "key": "muster", "type": "dict-invisible", + "key": "muster", "children": [{ + "type": "dict-modifiable", + "object_type": "int", "is_group": true, "is_file": true, "key": "templates_mapping", - "label": "Muster - Templates mapping", - "type": "dict-modifiable", - "object_type": "int" + "label": "Muster - Templates mapping" }] } ] diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_intents_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_intents_gui_schema.json index 1469ffc5fc..37d525cfb6 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_intents_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_intents_gui_schema.json @@ -7,9 +7,9 @@ "children": [ { "type": "dict-modifiable", + "object_type": "text-singleline", "key": "items", - "label": "Intent Key/Label", - "object_type": "text-singleline" + "label": "Intent Key/Label" }, { "type": "text-singleline", "key": "default", From b7a847563e0cdddad0c509a3ddb21193297e5009 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 28 Aug 2020 10:53:18 +0100 Subject: [PATCH 264/813] Revamp Build Workfile in Nuke. --- pype/hosts/nuke/lib.py | 305 ---------------------------------------- pype/hosts/nuke/menu.py | 31 ++-- 2 files changed, 21 insertions(+), 315 deletions(-) diff --git a/pype/hosts/nuke/lib.py b/pype/hosts/nuke/lib.py index 8c0e37b15d..19a0784327 100644 --- a/pype/hosts/nuke/lib.py +++ b/pype/hosts/nuke/lib.py @@ -1,7 +1,6 @@ import os import re import sys -import getpass from collections import OrderedDict from avalon import api, io, lib @@ -1060,310 +1059,6 @@ def get_write_node_template_attr(node): return avalon.nuke.lib.fix_data_for_node_create(correct_data) -class BuildWorkfile(WorkfileSettings): - """ - Building first version of workfile. - - Settings are taken from presets and db. It will add all subsets - in last version for defined representaions - - Arguments: - variable (type): description - - """ - xpos = 0 - ypos = 0 - xpos_size = 80 - ypos_size = 90 - xpos_gap = 50 - ypos_gap = 50 - pos_layer = 10 - - def __init__(self, - root_path=None, - root_node=None, - nodes=None, - to_script=None, - **kwargs): - """ - A short description. - - A bit longer description. - - Argumetns: - root_path (str): description - root_node (nuke.Node): description - nodes (list): list of nuke.Node - nodes_effects (dict): dictionary with subsets - - Example: - nodes_effects = { - "plateMain": { - "nodes": [ - [("Class", "Reformat"), - ("resize", "distort"), - ("flip", True)], - - [("Class", "Grade"), - ("blackpoint", 0.5), - ("multiply", 0.4)] - ] - }, - } - - """ - - WorkfileSettings.__init__(self, - root_node=root_node, - nodes=nodes, - **kwargs) - self.to_script = to_script - # collect data for formating - self.data_tmp = { - "project": {"name": self._project["name"], - "code": self._project["data"].get("code", "")}, - "asset": self._asset or os.environ["AVALON_ASSET"], - "task": kwargs.get("task") or api.Session["AVALON_TASK"], - "hierarchy": kwargs.get("hierarchy") or pype.get_hierarchy(), - "version": kwargs.get("version", {}).get("name", 1), - "user": getpass.getuser(), - "comment": "firstBuild", - "ext": "nk" - } - - # get presets from anatomy - anatomy = get_anatomy() - # format anatomy - anatomy_filled = anatomy.format(self.data_tmp) - - # get dir and file for workfile - self.work_dir = anatomy_filled["work"]["folder"] - self.work_file = anatomy_filled["work"]["file"] - - def save_script_as(self, path=None): - # first clear anything in open window - nuke.scriptClear() - - if not path: - dir = self.work_dir - path = os.path.join( - self.work_dir, - self.work_file).replace("\\", "/") - else: - dir = os.path.dirname(path) - - # check if folder is created - if not os.path.exists(dir): - os.makedirs(dir) - - # save script to path - nuke.scriptSaveAs(path) - - def process(self, - regex_filter=None, - version=None, - representations=["exr", "dpx", "lutJson", "mov", - "preview", "png", "jpeg", "jpg"]): - """ - A short description. - - A bit longer description. - - Args: - regex_filter (raw string): regex pattern to filter out subsets - version (int): define a particular version, None gets last - representations (list): - - Returns: - type: description - - Raises: - Exception: description - - """ - - if not self.to_script: - # save the script - self.save_script_as() - - # create viewer and reset frame range - viewer = self.get_nodes(nodes_filter=["Viewer"]) - if not viewer: - vn = nuke.createNode("Viewer") - vn["xpos"].setValue(self.xpos) - vn["ypos"].setValue(self.ypos) - else: - vn = viewer[-1] - - # move position - self.position_up() - - wn = self.write_create() - wn["xpos"].setValue(self.xpos) - wn["ypos"].setValue(self.ypos) - wn["render"].setValue(True) - vn.setInput(0, wn) - - # adding backdrop under write - self.create_backdrop(label="Render write \n\n\n\nOUTPUT", - color='0xcc1102ff', layer=-1, - nodes=[wn]) - - # move position - self.position_up(4) - - # set frame range for new viewer - self.reset_frame_range_handles() - - # get all available representations - subsets = pype.get_subsets(self._asset, - regex_filter=regex_filter, - version=version, - representations=representations) - - for name, subset in subsets.items(): - log.debug("___________________") - log.debug(name) - log.debug(subset["version"]) - - nodes_backdrop = list() - for name, subset in subsets.items(): - if "lut" in name: - continue - log.info("Building Loader to: `{}`".format(name)) - version = subset["version"] - log.info("Version to: `{}`".format(version["name"])) - representations = subset["representaions"] - for repr in representations: - rn = self.read_loader(repr) - rn["xpos"].setValue(self.xpos) - rn["ypos"].setValue(self.ypos) - wn.setInput(0, rn) - - # get editional nodes - lut_subset = [s for n, s in subsets.items() - if "lut{}".format(name.lower()) in n.lower()] - log.debug(">> lut_subset: `{}`".format(lut_subset)) - - if len(lut_subset) > 0: - lsub = lut_subset[0] - fxn = self.effect_loader(lsub["representaions"][-1]) - fxn_ypos = fxn["ypos"].value() - fxn["ypos"].setValue(fxn_ypos - 100) - nodes_backdrop.append(fxn) - - nodes_backdrop.append(rn) - # move position - self.position_right() - - # adding backdrop under all read nodes - self.create_backdrop(label="Loaded Reads", - color='0x2d7702ff', layer=-1, - nodes=nodes_backdrop) - - def read_loader(self, representation): - """ - Gets Loader plugin for image sequence or mov - - Arguments: - representation (dict): avalon db entity - - """ - context = representation["context"] - - loader_name = "LoadSequence" - if "mov" in context["representation"]: - loader_name = "LoadMov" - - loader_plugin = None - for Loader in api.discover(api.Loader): - if Loader.__name__ != loader_name: - continue - - loader_plugin = Loader - - return api.load(Loader=loader_plugin, - representation=representation["_id"]) - - def effect_loader(self, representation): - """ - Gets Loader plugin for effects - - Arguments: - representation (dict): avalon db entity - - """ - loader_name = "LoadLuts" - - loader_plugin = None - for Loader in api.discover(api.Loader): - if Loader.__name__ != loader_name: - continue - - loader_plugin = Loader - - return api.load(Loader=loader_plugin, - representation=representation["_id"]) - - def write_create(self): - """ - Create render write - - Arguments: - representation (dict): avalon db entity - - """ - task = self.data_tmp["task"] - sanitized_task = re.sub('[^0-9a-zA-Z]+', '', task) - subset_name = "render{}Main".format( - sanitized_task.capitalize()) - - Create_name = "CreateWriteRender" - - creator_plugin = None - for Creator in api.discover(api.Creator): - if Creator.__name__ != Create_name: - continue - - creator_plugin = Creator - - # return api.create() - return creator_plugin(subset_name, self._asset).process() - - def create_backdrop(self, label="", color=None, layer=0, - nodes=None): - """ - Create Backdrop node - - Arguments: - color (str): nuke compatible string with color code - layer (int): layer of node usually used (self.pos_layer - 1) - label (str): the message - nodes (list): list of nodes to be wrapped into backdrop - - """ - assert isinstance(nodes, list), "`nodes` should be a list of nodes" - layer = self.pos_layer + layer - - create_backdrop(label=label, color=color, layer=layer, nodes=nodes) - - def position_reset(self, xpos=0, ypos=0): - self.xpos = xpos - self.ypos = ypos - - def position_right(self, multiply=1): - self.xpos += (self.xpos_size * multiply) + self.xpos_gap - - def position_left(self, multiply=1): - self.xpos -= (self.xpos_size * multiply) + self.xpos_gap - - def position_down(self, multiply=1): - self.ypos -= (self.ypos_size * multiply) + self.ypos_gap - - def position_up(self, multiply=1): - self.ypos -= (self.ypos_size * multiply) + self.ypos_gap - - class ExporterReview: """ Base class object for generating review data from Nuke diff --git a/pype/hosts/nuke/menu.py b/pype/hosts/nuke/menu.py index 7306add9fe..b1ef7f47c4 100644 --- a/pype/hosts/nuke/menu.py +++ b/pype/hosts/nuke/menu.py @@ -2,10 +2,12 @@ import nuke from avalon.api import Session from pype.hosts.nuke import lib +from ...lib import BuildWorkfile from pype.api import Logger log = Logger().get_logger(__name__, "nuke") + def install(): menubar = nuke.menu("Nuke") menu = menubar.findItem(Session["AVALON_LABEL"]) @@ -20,7 +22,11 @@ def install(): log.debug("Changing Item: {}".format(rm_item)) # rm_item[1].setEnabled(False) menu.removeItem(rm_item[1].name()) - menu.addCommand(new_name, lambda: workfile_settings().reset_resolution(), index=(rm_item[0])) + menu.addCommand( + new_name, + lambda: workfile_settings().reset_resolution(), + index=(rm_item[0]) + ) # replace reset frame range from avalon core to pype's name = "Reset Frame Range" @@ -31,33 +37,38 @@ def install(): log.debug("Changing Item: {}".format(rm_item)) # rm_item[1].setEnabled(False) menu.removeItem(rm_item[1].name()) - menu.addCommand(new_name, lambda: workfile_settings().reset_frame_range_handles(), index=(rm_item[0])) + menu.addCommand( + new_name, + lambda: workfile_settings().reset_frame_range_handles(), + index=(rm_item[0]) + ) # add colorspace menu item - name = "Set colorspace" + name = "Set Colorspace" menu.addCommand( name, lambda: workfile_settings().set_colorspace(), - index=(rm_item[0]+2) + index=(rm_item[0] + 2) ) log.debug("Adding menu item: {}".format(name)) # add workfile builder menu item - name = "Build First Workfile.." + name = "Build Workfile" menu.addCommand( - name, lambda: lib.BuildWorkfile().process(), - index=(rm_item[0]+7) + name, lambda: BuildWorkfile().process(), + index=(rm_item[0] + 7) ) log.debug("Adding menu item: {}".format(name)) # add item that applies all setting above - name = "Apply all settings" + name = "Apply All Settings" menu.addCommand( - name, lambda: workfile_settings().set_context_settings(), index=(rm_item[0]+3) + name, + lambda: workfile_settings().set_context_settings(), + index=(rm_item[0] + 3) ) log.debug("Adding menu item: {}".format(name)) - def uninstall(): menubar = nuke.menu("Nuke") From 98f7be5f9047f25a87b4401ca90573549e1fd667 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 28 Aug 2020 12:26:16 +0200 Subject: [PATCH 265/813] added log to objects --- .../tools/config_setting/config_setting/widgets/inputs.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 2f0d85fd4e..573d66556e 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1,4 +1,5 @@ import json +import logging from Qt import QtWidgets, QtCore, QtGui from .widgets import ( ConfigWidget, @@ -14,6 +15,13 @@ class ConfigObject: _is_overriden = False _is_modified = False _was_overriden = False + _log = None + + @property + def log(self): + if self._log is None: + self._log = logging.getLogger(self.__class__.__name__) + return self._log @property def is_modified(self): From 95a68cca5755b56cd00822a21d1e7c353ce6173e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 28 Aug 2020 12:28:00 +0200 Subject: [PATCH 266/813] child_modified and child_overriden are abstract --- .../config_setting/config_setting/widgets/inputs.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 573d66556e..405ad3502d 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -56,6 +56,19 @@ class ConfigObject: def ignore_value_changes(self, value): self._parent.ignore_value_changes = value + @property + def child_modified(self): + """Any children item is modified.""" + raise NotImplementedError( + "{} does not have implemented `child_modified`".format(self) + ) + + @property + def child_overriden(self): + """Any children item is overriden.""" + raise NotImplementedError( + "{} does not have implemented `child_overriden`".format(self) + ) def item_value(self): raise NotImplementedError( "Method `item_value` not implemented!" From 8032a38a1a80cbd52c6f8c008520e5f5cb5eb975 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 28 Aug 2020 12:28:35 +0200 Subject: [PATCH 267/813] added few docstrings --- .../config_setting/config_setting/widgets/inputs.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 405ad3502d..eb713d5fd9 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -25,27 +25,33 @@ class ConfigObject: @property def is_modified(self): + """Has object any changes that require saving.""" return self._is_modified or (self.was_overriden != self.is_overriden) @property def is_overriden(self): + """Is object overriden so should be saved to overrides.""" return self._is_overriden or self._parent.is_overriden @property def was_overriden(self): + """Initial state after applying overrides.""" return self._was_overriden @property def is_overidable(self): + """Should care about overrides.""" return self._parent.is_overidable def any_parent_overriden(self): + """Any of parent object up to top hiearchy is overriden.""" if self._parent._is_overriden: return True return self._parent.any_parent_overriden() @property def ignore_value_changes(self): + """Most of attribute changes are ignored on value change when True.""" if not hasattr(self, "_parent"): raise NotImplementedError( "Object {} does not have `_parent` attribute".format(self) @@ -54,6 +60,7 @@ class ConfigObject: @ignore_value_changes.setter def ignore_value_changes(self, value): + """Setter for global parent item to apply changes for all inputs.""" self._parent.ignore_value_changes = value @property @@ -70,14 +77,17 @@ class ConfigObject: "{} does not have implemented `child_overriden`".format(self) ) def item_value(self): + """Value of an item without key.""" raise NotImplementedError( "Method `item_value` not implemented!" ) def config_value(self): + """Output for saving changes or overrides.""" return {self.key: self.item_value()} def value_from_values(self, values, keys=None): + """Global getter of value based on loaded values.""" if not values or values is AS_WIDGET: return NOT_SET From 1d82c562d0a0717abe3815ec16c6919edfcaf84e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 28 Aug 2020 12:28:55 +0200 Subject: [PATCH 268/813] added is invalid attribute --- pype/tools/config_setting/config_setting/widgets/inputs.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index eb713d5fd9..310717e47a 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -15,6 +15,7 @@ class ConfigObject: _is_overriden = False _is_modified = False _was_overriden = False + _is_invalid = False _log = None @property @@ -38,6 +39,11 @@ class ConfigObject: """Initial state after applying overrides.""" return self._was_overriden + @property + def is_invalid(self): + """Value set in is not valid.""" + return self._is_invalid + @property def is_overidable(self): """Should care about overrides.""" From 51ad1d39372fbd2a102d1049c2520e1e313647f0 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 28 Aug 2020 12:36:57 +0200 Subject: [PATCH 269/813] remove line that is not needed --- pype/tools/config_setting/config_setting/widgets/inputs.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 310717e47a..b97bde5a2e 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -998,7 +998,6 @@ class ListItem(QtWidgets.QWidget, ConfigObject): self.value_input.value_changed.connect(self._on_value_change) - self.value_input.item_value() self.is_single = False def _on_value_change(self, item=None): From 03c1aeac2e31df13c6232a6dc0791c95da63b2b9 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 28 Aug 2020 12:48:20 +0200 Subject: [PATCH 270/813] RawJsonInput is much simpler --- .../config_setting/widgets/inputs.py | 58 +++++-------------- 1 file changed, 15 insertions(+), 43 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index b97bde5a2e..aacef27f66 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -762,7 +762,6 @@ class TextMultiLineWidget(ConfigWidget, InputObject): class RawJsonInput(QtWidgets.QPlainTextEdit): - value_changed = QtCore.Signal(object) tab_length = 4 def __init__(self, *args, **kwargs): @@ -774,10 +773,6 @@ class RawJsonInput(QtWidgets.QPlainTextEdit): ).horizontalAdvance(" ") * self.tab_length ) - self._state = None - self.is_valid = None - self.textChanged.connect(self._on_value_change) - def sizeHint(self): document = self.document() layout = document.documentLayout() @@ -788,58 +783,35 @@ class RawJsonInput(QtWidgets.QPlainTextEdit): height += layout.blockBoundingRect(block).height() block = block.next() - value = super(RawJsonInput, self).sizeHint() - value.setHeight(height) + hint = super(RawJsonInput, self).sizeHint() + hint.setHeight(height) - return value + return hint def set_value(self, value, *, global_value=False): - self._state = None + if not value or value is NOT_SET: + value = "" if not isinstance(value, str): - value = json.dumps(value, indent=4) + try: + value = json.dumps(value, indent=4) + except Exception: + value = "" self.setPlainText(value) - def _on_value_change(self): - self.validate() - self.value_changed.emit(self) - - def validate_value(self, value): - if isinstance(value, str) and not value: - return True + def json_value(self): + return json.loads(self.toPlainText()) + def has_invalid_value(self): try: - json.loads(value) - return True - except Exception: + self.json_value() return False - - def update_style(self): - if self.is_valid is None: - return self.validate() - - if self.is_valid: - state = "" - else: - state = "invalid" - - if self._state is None or self._state != state: - self._state = state - - self.setProperty("state", state) - self.style().polish(self) + except Exception: + return True def resizeEvent(self, event): self.updateGeometry() super(RawJsonInput, self).resizeEvent(event) - def item_value(self): - return json.loads(self.toPlainText()) - - def validate(self): - value = self.toPlainText() - self.is_valid = self.validate_value(value) - self.update_style() - class RawJsonWidget(ConfigWidget, InputObject): value_changed = QtCore.Signal(object) From 111912cf0fd3097158e71490ae5821e7516b8609 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 28 Aug 2020 12:49:34 +0200 Subject: [PATCH 271/813] RawJsonWidget modified to be able to handle simpler input --- .../config_setting/widgets/inputs.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index aacef27f66..0a1dd5551b 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -858,12 +858,13 @@ class RawJsonWidget(ConfigWidget, InputObject): value = self.value_from_values(values) if value is not NOT_SET: self.text_input.set_value(value) + self._is_invalid = self.text_input.has_invalid_value() self.global_value = value self.start_value = self.item_value() self.override_value = NOT_SET - self.text_input.value_changed.connect(self._on_value_change) + self.text_input.textChanged.connect(self._on_value_change) def set_value(self, value, *, global_value=False): self.text_input.set_value(value) @@ -896,10 +897,11 @@ class RawJsonWidget(ConfigWidget, InputObject): if self.ignore_value_changes: return - if self.text_input.is_valid: - self._is_modified = self.item_value() != self.global_value - else: + self._is_invalid = self.text_input.has_invalid_value() + if self._is_invalid: self._is_modified = True + else: + self._is_modified = self.item_value() != self.global_value if self.is_overidable: self._is_overriden = True @@ -924,7 +926,9 @@ class RawJsonWidget(ConfigWidget, InputObject): widget.style().polish(widget) def item_value(self): - return self.text_input.item_value() + if self.is_invalid: + return NOT_SET + return self.text_input.json_value() class ListItem(QtWidgets.QWidget, ConfigObject): From 358362ed6c30e4780a29456bd56504d0d9a9acc8 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 28 Aug 2020 12:50:15 +0200 Subject: [PATCH 272/813] state also works with invalid --- .../config_setting/widgets/inputs.py | 61 +++++++++++++------ 1 file changed, 44 insertions(+), 17 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 0a1dd5551b..878ae8f05e 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -112,12 +112,15 @@ class ConfigObject: value = value[key] return value - def style_state(self, is_overriden, is_modified): + def style_state(self, is_invalid, is_overriden, is_modified): items = [] - if is_overriden: - items.append("overriden") - if is_modified: - items.append("modified") + if is_invalid: + items.append("invalid") + else: + if is_overriden: + items.append("overriden") + if is_modified: + items.append("modified") return "-".join(items) or self.default_state def add_children_gui(self, child_configuration, values): @@ -306,7 +309,9 @@ class BooleanWidget(ConfigWidget, InputObject): self.value_changed.emit(self) def update_style(self): - state = self.style_state(self.is_overriden, self.is_modified) + state = self.style_state( + self.is_invalid, self.is_overriden, self.is_modified + ) if self._state == state: return @@ -413,7 +418,9 @@ class IntegerWidget(ConfigWidget, InputObject): self.value_changed.emit(self) def update_style(self): - state = self.style_state(self.is_overriden, self.is_modified) + state = self.style_state( + self.is_invalid, self.is_overriden, self.is_modified + ) if self._state == state: return @@ -529,7 +536,9 @@ class FloatWidget(ConfigWidget, InputObject): self.value_changed.emit(self) def update_style(self): - state = self.style_state(self.is_overriden, self.is_modified) + state = self.style_state( + self.is_invalid, self.is_overriden, self.is_modified + ) if self._state == state: return @@ -637,7 +646,9 @@ class TextSingleLineWidget(ConfigWidget, InputObject): self.value_changed.emit(self) def update_style(self): - state = self.style_state(self.is_overriden, self.is_modified) + state = self.style_state( + self.is_invalid, self.is_overriden, self.is_modified + ) if self._state == state: return @@ -743,7 +754,9 @@ class TextMultiLineWidget(ConfigWidget, InputObject): self.value_changed.emit(self) def update_style(self): - state = self.style_state(self.is_overriden, self.is_modified) + state = self.style_state( + self.is_invalid, self.is_overriden, self.is_modified + ) if self._state == state: return @@ -911,7 +924,9 @@ class RawJsonWidget(ConfigWidget, InputObject): self.value_changed.emit(self) def update_style(self): - state = self.style_state(self.is_overriden, self.is_modified) + state = self.style_state( + self.is_invalid, self.is_overriden, self.is_modified + ) if self._state == state: return @@ -1211,7 +1226,9 @@ class ListWidget(ConfigWidget, InputObject): self.update_style() def update_style(self): - state = self.style_state(self.is_overriden, self.is_modified) + state = self.style_state( + self.is_invalid, self.is_overriden, self.is_modified + ) if self._state == state: return @@ -1521,7 +1538,9 @@ class ModifiableDict(ExpandingWidget, InputObject): self.update_style() def update_style(self): - state = self.style_state(self.is_overriden, self.is_modified) + state = self.style_state( + self.is_invalid, self.is_overriden, self.is_modified + ) if self._state == state: return @@ -1654,7 +1673,9 @@ class DictExpandWidget(ExpandingWidget, ConfigObject): def update_style(self, is_overriden=None): child_modified = self.child_modified - child_state = self.style_state(self.child_overriden, child_modified) + child_state = self.style_state( + self.child_invalid, self.child_overriden, child_modified + ) if child_state: child_state = "child-{}".format(child_state) @@ -1663,7 +1684,9 @@ class DictExpandWidget(ExpandingWidget, ConfigObject): self.style().polish(self) self._child_state = child_state - state = self.style_state(self.is_overriden, self.is_modified) + state = self.style_state( + self.is_invalid, self.is_overriden, self.is_modified + ) if self._state == state: return @@ -1856,7 +1879,9 @@ class DictWidget(ConfigWidget, ConfigObject): def update_style(self, is_overriden=None): child_modified = self.child_modified - child_state = self.style_state(self.child_overriden, child_modified) + child_state = self.style_state( + self.child_invalid, self.child_overriden, child_modified + ) if child_state: child_state = "child-{}".format(child_state) @@ -1865,7 +1890,9 @@ class DictWidget(ConfigWidget, ConfigObject): self.style().polish(self) self._child_state = child_state - state = self.style_state(self.is_overriden, self.is_modified) + state = self.style_state( + self.is_invalid, self.is_overriden, self.is_modified + ) if self._state == state: return From 55175426c48fce8cfecaf00f299eb80579360f0a Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 28 Aug 2020 12:50:38 +0200 Subject: [PATCH 273/813] implemented child invalid to be able show them --- .../config_setting/widgets/inputs.py | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 878ae8f05e..9c515ece8c 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -82,6 +82,14 @@ class ConfigObject: raise NotImplementedError( "{} does not have implemented `child_overriden`".format(self) ) + + @property + def child_invalid(self): + """Any children item does not have valid value.""" + raise NotImplementedError( + "{} does not have implemented `child_invalid`".format(self) + ) + def item_value(self): """Value of an item without key.""" raise NotImplementedError( @@ -201,6 +209,10 @@ class InputObject(ConfigObject): def child_overriden(self): return self._is_overriden + @property + def child_invalid(self): + return self.is_invalid + def reset_children_attributes(self): return @@ -1715,6 +1727,13 @@ class DictExpandWidget(ExpandingWidget, ConfigObject): return True return False + @property + def child_invalid(self): + for input_field in self.input_fields: + if input_field.child_invalid: + return True + return False + def item_value(self): output = {} for input_field in self.input_fields: @@ -1921,6 +1940,13 @@ class DictWidget(ConfigWidget, ConfigObject): return True return False + @property + def child_invalid(self): + for input_field in self.input_fields: + if input_field.child_invalid: + return True + return False + def item_value(self): output = {} for input_field in self.input_fields: @@ -2011,6 +2037,13 @@ class DictInvisible(ConfigWidget, ConfigObject): return True return False + @property + def child_invalid(self): + for input_field in self.input_fields: + if input_field.child_invalid: + return True + return False + def item_value(self): output = {} for input_field in self.input_fields: From 88dd72f76182dddc2d30b513910c5c0b5a383713 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 28 Aug 2020 13:03:20 +0200 Subject: [PATCH 274/813] added hover color to buttons and separated item-tool btn --- pype/tools/config_setting/config_setting/style/style.css | 8 ++++++-- .../tools/config_setting/config_setting/widgets/inputs.py | 8 ++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/pype/tools/config_setting/config_setting/style/style.css b/pype/tools/config_setting/config_setting/style/style.css index ce014f0768..01e6503e6e 100644 --- a/pype/tools/config_setting/config_setting/style/style.css +++ b/pype/tools/config_setting/config_setting/style/style.css @@ -65,14 +65,18 @@ QPushButton { border-radius: 3px; padding: 5px; } -QPushButton[btn-type="text-list"] { +QPushButton:hover { + background-color: #31424e; +} +QPushButton[btn-type="tool-item"] { border: 1px solid #bfccd6; border-radius: 10px; } -QPushButton[btn-type="text-list"]:hover { +QPushButton[btn-type="tool-item"]:hover { border-color: #137cbd; color: #137cbd; + background-color: transparent; } QPushButton[btn-type="expand-toggle"] { diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 9c515ece8c..aada384286 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -989,8 +989,8 @@ class ListItem(QtWidgets.QWidget, ConfigObject): self.add_btn.setFixedSize(self._btn_size, self._btn_size) self.remove_btn.setFixedSize(self._btn_size, self._btn_size) - self.add_btn.setProperty("btn-type", "text-list") - self.remove_btn.setProperty("btn-type", "text-list") + self.add_btn.setProperty("btn-type", "tool-item") + self.remove_btn.setProperty("btn-type", "tool-item") layout.addWidget(self.value_input, 1) layout.addWidget(self.add_btn, 0) @@ -1282,8 +1282,8 @@ class ModifiableDictItem(QtWidgets.QWidget, ConfigObject): self.add_btn.setFocusPolicy(QtCore.Qt.ClickFocus) self.remove_btn.setFocusPolicy(QtCore.Qt.ClickFocus) - self.add_btn.setProperty("btn-type", "text-list") - self.remove_btn.setProperty("btn-type", "text-list") + self.add_btn.setProperty("btn-type", "tool-item") + self.remove_btn.setProperty("btn-type", "tool-item") layout.addWidget(self.key_input, 0) layout.addWidget(self.value_input, 1) From dd7772d36881c0f9c7b9454cb786180198e64ce6 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 28 Aug 2020 13:03:35 +0200 Subject: [PATCH 275/813] fixed dict widget invalid style --- pype/tools/config_setting/config_setting/widgets/inputs.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index aada384286..afe805c69f 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1898,8 +1898,9 @@ class DictWidget(ConfigWidget, ConfigObject): def update_style(self, is_overriden=None): child_modified = self.child_modified + child_invalid = self.child_invalid child_state = self.style_state( - self.child_invalid, self.child_overriden, child_modified + child_invalid, self.child_overriden, child_modified ) if child_state: child_state = "child-{}".format(child_state) @@ -1910,7 +1911,7 @@ class DictWidget(ConfigWidget, ConfigObject): self._child_state = child_state state = self.style_state( - self.is_invalid, self.is_overriden, self.is_modified + child_invalid, self.is_overriden, self.is_modified ) if self._state == state: return From a05e45acebd2894ffdf0544ba4f84a2acaedbd2c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 28 Aug 2020 13:03:50 +0200 Subject: [PATCH 276/813] added invalid colors to style --- .../config_setting/style/style.css | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/pype/tools/config_setting/config_setting/style/style.css b/pype/tools/config_setting/config_setting/style/style.css index 01e6503e6e..c575ed64c3 100644 --- a/pype/tools/config_setting/config_setting/style/style.css +++ b/pype/tools/config_setting/config_setting/style/style.css @@ -47,6 +47,8 @@ QLabel[state="overriden-modified"] {color: #137cbd;} QLabel[state="overriden-modified"]:hover {color: #1798e8;} QLabel[state="overriden"] {color: #ff8c1a;} QLabel[state="overriden"]:hover {color: #ffa64d;} +QLabel[state="invalid"] {color: #ad2e2e; font-weight: bold;} +QLabel[state="invalid"]:hover {color: #ad2e2e; font-weight: bold;} QWidget[input-state="modified"] { border-color: #137cbd; @@ -60,6 +62,10 @@ QWidget[input-state="overriden"] { border-color: #ff8c1a; } +QWidget[input-state="invalid"] { + border-color: #ad2e2e; +} + QPushButton { border: 1px solid #aaaaaa; border-radius: 3px; @@ -96,20 +102,18 @@ QPushButton[btn-type="expand-toggle"] { font-weight: bold; } -#RawJsonInput[state="invalid"] { - border-color: #ab2e46; - border-width: 2px; -} - #DictKey[state="modified"] { - border-left-color: #137cbd; + border-color: #137cbd; } #DictKey[state="overriden"] { - border-left-color: #00f; + border-color: #00f; } #DictKey[state="overriden-modified"] { - border-left-color: #0f0; + border-color: #0f0; +} +#DictKey[state="invalid"] { + border-color: #ad2e2e; } #DictLabel { @@ -136,6 +140,13 @@ QPushButton[btn-type="expand-toggle"] { border-color: #137cbd; } +#ExpandingWidget[state="child-invalid"], #ModifiableDict[state="child-invalid"], #DictWidget[state="child-invalid"] { + border-color: #ad2e2e; +} +#ExpandingWidget[state="child-invalid"]:hover, #ModifiableDict[state="child-invalid"]:hover, #DictWidget[state="child-invalid"]:hover { + border-color: #c93636; +} + #ExpandingWidget[state="child-overriden"], #ModifiableDict[state="child-overriden"], #DictWidget[state="child-overriden"] { border-color: #e67300; } From 76777790523f77aae9a235d8567a010ce4c9f6f3 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 28 Aug 2020 16:06:41 +0200 Subject: [PATCH 277/813] added methods for getting invalid items --- .../config_setting/config_setting/widgets/inputs.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index afe805c69f..be3a1813d3 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -90,6 +90,12 @@ class ConfigObject: "{} does not have implemented `child_invalid`".format(self) ) + def get_invalid(self): + """Returns invalid items all down the hierarchy.""" + raise NotImplementedError( + "{} does not have implemented `get_invalid`".format(self) + ) + def item_value(self): """Value of an item without key.""" raise NotImplementedError( @@ -213,6 +219,12 @@ class InputObject(ConfigObject): def child_invalid(self): return self.is_invalid + def get_invalid(self): + output = [] + if self.is_invalid: + output.append(self) + return output + def reset_children_attributes(self): return From c8e2963c91b0e467d00ac02ea264442066afe91c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 28 Aug 2020 16:31:42 +0200 Subject: [PATCH 278/813] basees won't allow save invalid values --- .../config_setting/widgets/base.py | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/base.py b/pype/tools/config_setting/config_setting/widgets/base.py index d5f9a05aea..34405d2ad0 100644 --- a/pype/tools/config_setting/config_setting/widgets/base.py +++ b/pype/tools/config_setting/config_setting/widgets/base.py @@ -99,6 +99,30 @@ class StudioWidget(QtWidgets.QWidget): self.schema = schema def _save(self): + has_invalid = False + for item in self.input_fields: + if item.child_invalid: + has_invalid = True + + if has_invalid: + invalid_items = [] + for item in self.input_fields: + invalid_items.extend(item.get_invalid()) + msg_box = QtWidgets.QMessageBox( + QtWidgets.QMessageBox.Warning, + "Invalid input", + "There is invalid value in one of inputs." + " Please lead red color and fix them." + ) + msg_box.setStandardButtons(QtWidgets.QMessageBox.Ok) + msg_box.exec_() + + first_invalid_item = invalid_items[0] + self.scroll_widget.ensureWidgetVisible(first_invalid_item) + if first_invalid_item.isVisible(): + first_invalid_item.setFocus(True) + return + all_values = {} for item in self.input_fields: all_values.update(item.config_value()) @@ -401,6 +425,30 @@ class ProjectWidget(QtWidgets.QWidget): self.ignore_value_changes = False def _save(self): + has_invalid = False + for item in self.input_fields: + if item.child_invalid: + has_invalid = True + + if has_invalid: + invalid_items = [] + for item in self.input_fields: + invalid_items.extend(item.get_invalid()) + msg_box = QtWidgets.QMessageBox( + QtWidgets.QMessageBox.Warning, + "Invalid input", + "There is invalid value in one of inputs." + " Please lead red color and fix them." + ) + msg_box.setStandardButtons(QtWidgets.QMessageBox.Ok) + msg_box.exec_() + + first_invalid_item = invalid_items[0] + self.scroll_widget.ensureWidgetVisible(first_invalid_item) + if first_invalid_item.isVisible(): + first_invalid_item.setFocus(True) + return + if self.project_name is None: self._save_defaults() else: From 31227069ffe219f3fad787d974772c1711eb6995 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 28 Aug 2020 16:45:36 +0200 Subject: [PATCH 279/813] update styles after initialization --- .../config_setting/widgets/base.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/base.py b/pype/tools/config_setting/config_setting/widgets/base.py index 34405d2ad0..283a5ad0a4 100644 --- a/pype/tools/config_setting/config_setting/widgets/base.py +++ b/pype/tools/config_setting/config_setting/widgets/base.py @@ -93,10 +93,10 @@ class StudioWidget(QtWidgets.QWidget): self.input_fields.clear() values = {"studio": config.studio_configurations()} - schema = lib.gui_schema("studio_schema", "0_studio_gui_schema") - self.keys = schema.get("keys", []) - self.add_children_gui(schema, values) - self.schema = schema + self.schema = lib.gui_schema("studio_schema", "0_studio_gui_schema") + self.keys = self.schema.get("keys", []) + self.add_children_gui(self.schema, values) + self.hierarchical_style_update() def _save(self): has_invalid = False @@ -395,10 +395,10 @@ class ProjectWidget(QtWidgets.QWidget): def reset(self): values = {"project": config.global_project_configurations()} - schema = lib.gui_schema("projects_schema", "0_project_gui_schema") - self.keys = schema.get("keys", []) - self.add_children_gui(schema, values) - self.schema = schema + self.schema = lib.gui_schema("projects_schema", "0_project_gui_schema") + self.keys = self.schema.get("keys", []) + self.add_children_gui(self.schema, values) + self.hierarchical_style_update() def add_children_gui(self, child_configuration, values): item_type = child_configuration["type"] From de1ed6d639a2dfbbf86343615f80afc302d9d89e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 28 Aug 2020 16:48:26 +0200 Subject: [PATCH 280/813] added default values to inputs --- .../config_setting/widgets/inputs.py | 37 +++++++++++++++++-- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index be3a1813d3..7d25880045 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -239,6 +239,7 @@ class BooleanWidget(ConfigWidget, InputObject): self._as_widget = values is AS_WIDGET self.is_group = input_data.get("is_group", False) + self.default_value = input_data.get("default", NOT_SET) self._state = None @@ -268,14 +269,12 @@ class BooleanWidget(ConfigWidget, InputObject): keys = list(parent_keys) keys.append(self.key) self.keys = keys - - default_value = input_data.get("default", NOT_SET) value = self.value_from_values(values) if value is not NOT_SET: self.checkbox.setChecked(value) - elif default_value is not NOT_SET: - self.checkbox.setChecked(default_value) + elif self.default_value is not NOT_SET: + self.checkbox.setChecked(self.default_value) self.global_value = value self.start_value = self.item_value() @@ -362,6 +361,7 @@ class IntegerWidget(ConfigWidget, InputObject): self._as_widget = values is AS_WIDGET self.is_group = input_data.get("is_group", False) + self.default_value = input_data.get("default", NOT_SET) self._state = None @@ -394,6 +394,9 @@ class IntegerWidget(ConfigWidget, InputObject): if value is not NOT_SET: self.int_input.setValue(value) + elif self.default_value is not NOT_SET: + self.int_input.setValue(self.default_value) + self.global_value = value self.start_value = self.item_value() self.override_value = NOT_SET @@ -472,6 +475,7 @@ class FloatWidget(ConfigWidget, InputObject): self._as_widget = values is AS_WIDGET self.is_group = input_data.get("is_group", False) + self.default_value = input_data.get("default", NOT_SET) self._state = None @@ -514,6 +518,9 @@ class FloatWidget(ConfigWidget, InputObject): if value is not NOT_SET: self.float_input.setValue(value) + elif self.default_value is not NOT_SET: + self.float_input.setValue(self.default_value) + self.start_value = self.item_value() self.global_value = value self.override_value = NOT_SET @@ -590,6 +597,7 @@ class TextSingleLineWidget(ConfigWidget, InputObject): self._as_widget = values is AS_WIDGET self.is_group = input_data.get("is_group", False) + self.default_value = input_data.get("default", NOT_SET) self._state = None @@ -622,6 +630,9 @@ class TextSingleLineWidget(ConfigWidget, InputObject): if value is not NOT_SET: self.text_input.setText(value) + elif self.default_value is not NOT_SET: + self.text_input.setText(self.default_value) + self.global_value = value self.start_value = self.item_value() self.override_value = NOT_SET @@ -700,6 +711,7 @@ class TextMultiLineWidget(ConfigWidget, InputObject): self._as_widget = values is AS_WIDGET self.is_group = input_data.get("is_group", False) + self.default_value = input_data.get("default", NOT_SET) self._state = None @@ -732,6 +744,9 @@ class TextMultiLineWidget(ConfigWidget, InputObject): if value is not NOT_SET: self.text_input.setPlainText(value) + elif self.default_value is not NOT_SET: + self.text_input.setPlainText(self.default_value) + self.global_value = value self.start_value = self.item_value() self.override_value = NOT_SET @@ -860,6 +875,7 @@ class RawJsonWidget(ConfigWidget, InputObject): self._as_widget = values is AS_WIDGET self.is_group = input_data.get("is_group", False) + self.default_value = input_data.get("default", NOT_SET) self._state = None @@ -895,6 +911,9 @@ class RawJsonWidget(ConfigWidget, InputObject): value = self.value_from_values(values) if value is not NOT_SET: self.text_input.set_value(value) + + elif self.default_value is not NOT_SET: + self.text_input.set_value(self.default_value) self._is_invalid = self.text_input.has_invalid_value() self.global_value = value @@ -1050,6 +1069,7 @@ class ListSubWidget(QtWidgets.QWidget, ConfigObject): self.input_fields = [] self.object_type = input_data["object_type"] + self.default_value = input_data.get("default", NOT_SET) self.key = input_data["key"] keys = list(parent_keys) @@ -1061,6 +1081,10 @@ class ListSubWidget(QtWidgets.QWidget, ConfigObject): for item_value in value: self.add_row(value=item_value) + elif self.default_value is not NOT_SET: + for item_value in self.default_value: + self.add_row(value=item_value) + if self.count() == 0: self.add_row() @@ -1392,6 +1416,7 @@ class ModifiableDictSubWidget(QtWidgets.QWidget, ConfigObject): self.input_fields = [] self.object_type = input_data["object_type"] + self.default_value = input_data.get("default", NOT_SET) self.key = input_data["key"] keys = list(parent_keys) @@ -1403,6 +1428,10 @@ class ModifiableDictSubWidget(QtWidgets.QWidget, ConfigObject): for item_key, item_value in value.items(): self.add_row(key=item_key, value=item_value) + elif self.default_value is not NOT_SET: + for item_key, item_value in self.default_value.items(): + self.add_row(key=item_key, value=item_value) + if self.count() == 0: self.add_row() From f6839a79530b490fc9fddb576573576f7b64732a Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 28 Aug 2020 16:48:54 +0200 Subject: [PATCH 281/813] implemented get_invalid for dictionaries --- .../config_setting/widgets/inputs.py | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 7d25880045..e2aa2e01a1 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1775,6 +1775,12 @@ class DictExpandWidget(ExpandingWidget, ConfigObject): return True return False + def get_invalid(self): + output = [] + for input_field in self.input_fields: + output.extend(input_field.get_invalid()) + return output + def item_value(self): output = {} for input_field in self.input_fields: @@ -1989,6 +1995,12 @@ class DictWidget(ConfigWidget, ConfigObject): return True return False + def get_invalid(self): + output = [] + for input_field in self.input_fields: + output.extend(input_field.get_invalid()) + return output + def item_value(self): output = {} for input_field in self.input_fields: @@ -2086,6 +2098,12 @@ class DictInvisible(ConfigWidget, ConfigObject): return True return False + def get_invalid(self): + output = [] + for input_field in self.input_fields: + output.extend(input_field.get_invalid()) + return output + def item_value(self): output = {} for input_field in self.input_fields: @@ -2249,6 +2267,19 @@ class DictFormWidget(ConfigWidget, ConfigObject): return True return False + @property + def child_invalid(self): + for input_field in self.input_fields.values(): + if input_field.child_invalid: + return True + return False + + def get_invalid(self): + output = [] + for input_field in self.input_fields.values(): + output.extend(input_field.get_invalid()) + return output + def add_children_gui(self, child_configuration, values): item_type = child_configuration["type"] key = child_configuration["key"] From 2dc39e276f1961a9c58aff1d3d3c03791d73c6dd Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 28 Aug 2020 16:49:43 +0200 Subject: [PATCH 282/813] set is modified on input init --- .../config_setting/widgets/inputs.py | 38 ++++++++++++++----- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index e2aa2e01a1..20ccf20290 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -276,9 +276,11 @@ class BooleanWidget(ConfigWidget, InputObject): elif self.default_value is not NOT_SET: self.checkbox.setChecked(self.default_value) + self.override_value = NOT_SET self.global_value = value self.start_value = self.item_value() - self.override_value = NOT_SET + + self._is_modified = self.global_value != self.start_value self.checkbox.stateChanged.connect(self._on_value_change) @@ -397,9 +399,11 @@ class IntegerWidget(ConfigWidget, InputObject): elif self.default_value is not NOT_SET: self.int_input.setValue(self.default_value) + self.override_value = NOT_SET self.global_value = value self.start_value = self.item_value() - self.override_value = NOT_SET + + self._is_modified = self.global_value != self.start_value self.int_input.valueChanged.connect(self._on_value_change) @@ -521,9 +525,11 @@ class FloatWidget(ConfigWidget, InputObject): elif self.default_value is not NOT_SET: self.float_input.setValue(self.default_value) + self.override_value = NOT_SET self.start_value = self.item_value() self.global_value = value - self.override_value = NOT_SET + + self._is_modified = self.global_value != self.start_value self.float_input.valueChanged.connect(self._on_value_change) @@ -633,9 +639,11 @@ class TextSingleLineWidget(ConfigWidget, InputObject): elif self.default_value is not NOT_SET: self.text_input.setText(self.default_value) + self.override_value = NOT_SET self.global_value = value self.start_value = self.item_value() - self.override_value = NOT_SET + + self._is_modified = self.global_value != self.start_value self.text_input.textChanged.connect(self._on_value_change) @@ -747,9 +755,11 @@ class TextMultiLineWidget(ConfigWidget, InputObject): elif self.default_value is not NOT_SET: self.text_input.setPlainText(self.default_value) + self.override_value = NOT_SET self.global_value = value self.start_value = self.item_value() - self.override_value = NOT_SET + + self._is_modified = self.global_value != self.start_value self.text_input.textChanged.connect(self._on_value_change) @@ -916,9 +926,11 @@ class RawJsonWidget(ConfigWidget, InputObject): self.text_input.set_value(self.default_value) self._is_invalid = self.text_input.has_invalid_value() + self.override_value = NOT_SET self.global_value = value self.start_value = self.item_value() - self.override_value = NOT_SET + + self._is_modified = self.global_value != self.start_value self.text_input.textChanged.connect(self._on_value_change) @@ -1088,9 +1100,11 @@ class ListSubWidget(QtWidgets.QWidget, ConfigObject): if self.count() == 0: self.add_row() + self.override_value = NOT_SET self.global_value = value self.start_value = self.item_value() - self.override_value = NOT_SET + + self._is_modified = self.global_value != self.start_value def set_value(self, value, *, global_value=False): previous_inputs = tuple(self.input_fields) @@ -1435,9 +1449,12 @@ class ModifiableDictSubWidget(QtWidgets.QWidget, ConfigObject): if self.count() == 0: self.add_row() + self.override_value = NOT_SET + self.global_value = value self.start_value = self.config_value() - self.override_value = NOT_SET + + self._is_modified = self.global_value != self.start_value @property def is_group(self): @@ -1555,8 +1572,11 @@ class ModifiableDict(ExpandingWidget, InputObject): self.key = input_data["key"] - self.global_value = self.item_value() self.override_value = NOT_SET + self.start_value = self.value_widget.start_value + self.global_value = self.value_widget.global_value + + self._is_modified = self.global_value != self.start_value def _on_value_change(self, item=None): if self.ignore_value_changes: From 61e11552667839bbe9b53f7ddcf8f059bd0fa6dd Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 28 Aug 2020 16:50:05 +0200 Subject: [PATCH 283/813] rawjson set's invalid bool even if values changes are ignored --- pype/tools/config_setting/config_setting/widgets/inputs.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 20ccf20290..9fcbd20454 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -962,6 +962,7 @@ class RawJsonWidget(ConfigWidget, InputObject): self.update_style() def _on_value_change(self, item=None): + self._is_invalid = self.text_input.has_invalid_value() if self.ignore_value_changes: return @@ -1064,7 +1065,7 @@ class ListItem(QtWidgets.QWidget, ConfigObject): def config_value(self): return self.value_input.item_value() - +# TODO Move subwidget to main widget class ListSubWidget(QtWidgets.QWidget, ConfigObject): value_changed = QtCore.Signal(object) From ad69ee3e4b9179da221b39de05c2248a39874296 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 28 Aug 2020 16:59:55 +0200 Subject: [PATCH 284/813] allow empty objects in raw json --- pype/tools/config_setting/config_setting/widgets/inputs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 9fcbd20454..b290927ed1 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -851,9 +851,9 @@ class RawJsonInput(QtWidgets.QPlainTextEdit): return hint def set_value(self, value, *, global_value=False): - if not value or value is NOT_SET: + if value is NOT_SET: value = "" - if not isinstance(value, str): + elif not isinstance(value, str): try: value = json.dumps(value, indent=4) except Exception: From 24ab3fcaad29b380fa854b2ac524f085bbd3b15a Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 28 Aug 2020 17:00:17 +0200 Subject: [PATCH 285/813] added few default values --- .../projects_schema/1_plugins_gui_schema.json | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json index 9f40c7871b..dbbcaf2b36 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json @@ -81,7 +81,8 @@ { "type": "boolean", "key": "enabled", - "label": "Enabled" + "label": "Enabled", + "default": true }, { "type": "text-singleline", "key": "note_with_intent_template", @@ -157,11 +158,13 @@ { "type": "boolean", "key": "enabled", - "label": "Enabled" + "label": "Enabled", + "default": true }, { "type": "raw-json", "key": "profiles", - "label": "Profiles" + "label": "Profiles", + "default": [] } ] }, { From 81a433941f1b7adbb4281157d7d1f8f76566f6e1 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 28 Aug 2020 17:04:11 +0200 Subject: [PATCH 286/813] modified default min/max for number inputs --- pype/tools/config_setting/config_setting/widgets/widgets.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/widgets.py b/pype/tools/config_setting/config_setting/widgets/widgets.py index 89f6782cfd..c2de371ffc 100644 --- a/pype/tools/config_setting/config_setting/widgets/widgets.py +++ b/pype/tools/config_setting/config_setting/widgets/widgets.py @@ -5,6 +5,8 @@ class ModifiedIntSpinBox(QtWidgets.QSpinBox): def __init__(self, *args, **kwargs): super(ModifiedIntSpinBox, self).__init__(*args, **kwargs) self.setFocusPolicy(QtCore.Qt.StrongFocus) + self.setMinimum(-99999) + self.setMaximum(99999) def wheelEvent(self, event): if self.hasFocus(): @@ -17,6 +19,8 @@ class ModifiedFloatSpinBox(QtWidgets.QDoubleSpinBox): def __init__(self, *args, **kwargs): super(ModifiedFloatSpinBox, self).__init__(*args, **kwargs) self.setFocusPolicy(QtCore.Qt.StrongFocus) + self.setMinimum(-99999) + self.setMaximum(99999) def wheelEvent(self, event): if self.hasFocus(): From a910969c02b1e3488e4432b9c02fe62d3018df39 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 28 Aug 2020 17:08:44 +0200 Subject: [PATCH 287/813] default number are settable by kwargs --- .../config_setting/widgets/widgets.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/widgets.py b/pype/tools/config_setting/config_setting/widgets/widgets.py index c2de371ffc..0b493d307d 100644 --- a/pype/tools/config_setting/config_setting/widgets/widgets.py +++ b/pype/tools/config_setting/config_setting/widgets/widgets.py @@ -3,10 +3,12 @@ from Qt import QtWidgets, QtCore, QtGui class ModifiedIntSpinBox(QtWidgets.QSpinBox): def __init__(self, *args, **kwargs): + min_value = kwargs.pop("min", -99999) + max_value = kwargs.pop("max", 99999) super(ModifiedIntSpinBox, self).__init__(*args, **kwargs) self.setFocusPolicy(QtCore.Qt.StrongFocus) - self.setMinimum(-99999) - self.setMaximum(99999) + self.setMinimum(min_value) + self.setMaximum(max_value) def wheelEvent(self, event): if self.hasFocus(): @@ -17,10 +19,14 @@ class ModifiedIntSpinBox(QtWidgets.QSpinBox): class ModifiedFloatSpinBox(QtWidgets.QDoubleSpinBox): def __init__(self, *args, **kwargs): + min_value = kwargs.pop("min", -99999) + max_value = kwargs.pop("max", 99999) + decimals = kwargs.pop("decimal", 2) super(ModifiedFloatSpinBox, self).__init__(*args, **kwargs) self.setFocusPolicy(QtCore.Qt.StrongFocus) - self.setMinimum(-99999) - self.setMaximum(99999) + self.setDecimals(decimals) + self.setMinimum(min_value) + self.setMaximum(max_value) def wheelEvent(self, event): if self.hasFocus(): From 0fb411cff071d383d0219a83bdf75e99ccb8f9e0 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 28 Aug 2020 17:15:10 +0200 Subject: [PATCH 288/813] added modifiers for number inputs --- .../studio_schema/1_tray_items.json | 4 +++- .../config_setting/widgets/inputs.py | 16 ++++++++++++++-- .../config_setting/widgets/widgets.py | 8 ++++---- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_tray_items.json b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_tray_items.json index e6f9a41e51..45b1bc65ce 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_tray_items.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_tray_items.json @@ -69,7 +69,9 @@ { "type": "int", "key": "default_port", - "label": "Default Port" + "label": "Default Port", + "minimum": 1, + "maximum": 65535 } ] }, { diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index b290927ed1..4136069d6a 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -355,6 +355,7 @@ class BooleanWidget(ConfigWidget, InputObject): class IntegerWidget(ConfigWidget, InputObject): value_changed = QtCore.Signal(object) + input_modifiers = ("minimum", "maximum") def __init__( self, input_data, values, parent_keys, parent, label_widget=None @@ -373,7 +374,12 @@ class IntegerWidget(ConfigWidget, InputObject): layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(5) - self.int_input = ModifiedIntSpinBox(self) + kwargs = { + modifier: input_data.get(modifier) + for modifier in self.input_modifiers + if input_data.get(modifier) + } + self.int_input = ModifiedIntSpinBox(self, **kwargs) self.setFocusProxy(self.int_input) @@ -471,6 +477,7 @@ class IntegerWidget(ConfigWidget, InputObject): class FloatWidget(ConfigWidget, InputObject): value_changed = QtCore.Signal(object) + input_modifiers = ("minimum", "maximum", "decimal") def __init__( self, input_data, values, parent_keys, parent, label_widget=None @@ -489,7 +496,12 @@ class FloatWidget(ConfigWidget, InputObject): layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(5) - self.float_input = ModifiedFloatSpinBox(self) + kwargs = { + modifier: input_data.get(modifier) + for modifier in self.input_modifiers + if input_data.get(modifier) + } + self.float_input = ModifiedFloatSpinBox(self, **kwargs) self.setFocusProxy(self.float_input) diff --git a/pype/tools/config_setting/config_setting/widgets/widgets.py b/pype/tools/config_setting/config_setting/widgets/widgets.py index 0b493d307d..b824ea8720 100644 --- a/pype/tools/config_setting/config_setting/widgets/widgets.py +++ b/pype/tools/config_setting/config_setting/widgets/widgets.py @@ -3,8 +3,8 @@ from Qt import QtWidgets, QtCore, QtGui class ModifiedIntSpinBox(QtWidgets.QSpinBox): def __init__(self, *args, **kwargs): - min_value = kwargs.pop("min", -99999) - max_value = kwargs.pop("max", 99999) + min_value = kwargs.pop("minimum", -99999) + max_value = kwargs.pop("maximum", 99999) super(ModifiedIntSpinBox, self).__init__(*args, **kwargs) self.setFocusPolicy(QtCore.Qt.StrongFocus) self.setMinimum(min_value) @@ -19,8 +19,8 @@ class ModifiedIntSpinBox(QtWidgets.QSpinBox): class ModifiedFloatSpinBox(QtWidgets.QDoubleSpinBox): def __init__(self, *args, **kwargs): - min_value = kwargs.pop("min", -99999) - max_value = kwargs.pop("max", 99999) + min_value = kwargs.pop("minimum", -99999) + max_value = kwargs.pop("maximum", 99999) decimals = kwargs.pop("decimal", 2) super(ModifiedFloatSpinBox, self).__init__(*args, **kwargs) self.setFocusPolicy(QtCore.Qt.StrongFocus) From 95aa493aaf5f35cf8db77ccd2f0190a081d2f6a9 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 28 Aug 2020 17:21:51 +0200 Subject: [PATCH 289/813] added possibility of input modifier for list and modifiable dict --- .../studio_schema/0_studio_gui_schema.json | 4 ++++ .../config_setting/widgets/inputs.py | 19 +++++++++++++------ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/0_studio_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/0_studio_gui_schema.json index 921122166b..db465fb392 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/0_studio_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/0_studio_gui_schema.json @@ -22,6 +22,10 @@ "children": [{ "type": "dict-modifiable", "object_type": "int", + "input_modifiers": { + "minimum": 0, + "maximum": 300 + }, "is_group": true, "is_file": true, "key": "templates_mapping", diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 4136069d6a..35b8719bb6 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -11,6 +11,8 @@ from .lib import NOT_SET, AS_WIDGET, METADATA_KEY, TypeToKlass class ConfigObject: + input_modifiers = tuple() + default_state = "" _is_overriden = False _is_modified = False @@ -1018,7 +1020,7 @@ class ListItem(QtWidgets.QWidget, ConfigObject): _btn_size = 20 value_changed = QtCore.Signal(object) - def __init__(self, object_type, parent): + def __init__(self, object_type, input_modifiers, parent): self._parent = parent super(ListItem, self).__init__(parent) @@ -1029,7 +1031,7 @@ class ListItem(QtWidgets.QWidget, ConfigObject): ItemKlass = TypeToKlass.types[object_type] self.value_input = ItemKlass( - {}, + input_modifiers, AS_WIDGET, [], self, @@ -1077,6 +1079,7 @@ class ListItem(QtWidgets.QWidget, ConfigObject): def config_value(self): return self.value_input.item_value() + # TODO Move subwidget to main widget class ListSubWidget(QtWidgets.QWidget, ConfigObject): value_changed = QtCore.Signal(object) @@ -1095,6 +1098,7 @@ class ListSubWidget(QtWidgets.QWidget, ConfigObject): self.input_fields = [] self.object_type = input_data["object_type"] self.default_value = input_data.get("default", NOT_SET) + self.input_modifiers = input_data.get("input_modifiers") or {} self.key = input_data["key"] keys = list(parent_keys) @@ -1157,7 +1161,7 @@ class ListSubWidget(QtWidgets.QWidget, ConfigObject): def add_row(self, row=None, value=None): # Create new item - item_widget = ListItem(self.object_type, self) + item_widget = ListItem(self.object_type, self.input_modifiers, self) # Set/unset if new item is single item current_count = self.count() @@ -1318,7 +1322,7 @@ class ModifiableDictItem(QtWidgets.QWidget, ConfigObject): _btn_size = 20 value_changed = QtCore.Signal(object) - def __init__(self, object_type, parent): + def __init__(self, object_type, input_modifiers, parent): self._parent = parent super(ModifiableDictItem, self).__init__(parent) @@ -1333,7 +1337,7 @@ class ModifiableDictItem(QtWidgets.QWidget, ConfigObject): self.key_input.setObjectName("DictKey") self.value_input = ItemKlass( - {}, + input_modifiers, AS_WIDGET, [], self, @@ -1444,6 +1448,7 @@ class ModifiableDictSubWidget(QtWidgets.QWidget, ConfigObject): self.input_fields = [] self.object_type = input_data["object_type"] self.default_value = input_data.get("default", NOT_SET) + self.input_modifiers = input_data.get("input_modifiers") or {} self.key = input_data["key"] keys = list(parent_keys) @@ -1485,7 +1490,9 @@ class ModifiableDictSubWidget(QtWidgets.QWidget, ConfigObject): def add_row(self, row=None, key=None, value=None): # Create new item - item_widget = ModifiableDictItem(self.object_type, self) + item_widget = ModifiableDictItem( + self.object_type, self.input_modifiers, self + ) # Set/unset if new item is single item current_count = self.count() From 7a382f3f2073fdbe34982b50781ad22f3054bc8c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 28 Aug 2020 17:22:51 +0200 Subject: [PATCH 290/813] removed global input_modifiers = tuple() --- pype/tools/config_setting/config_setting/widgets/inputs.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 35b8719bb6..e5a63e635a 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -11,8 +11,6 @@ from .lib import NOT_SET, AS_WIDGET, METADATA_KEY, TypeToKlass class ConfigObject: - input_modifiers = tuple() - default_state = "" _is_overriden = False _is_modified = False From f76331207cec10b3476b8f9dcdb5024d82e25f48 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 31 Aug 2020 10:02:06 +0200 Subject: [PATCH 291/813] fixed json overrides on project change --- pype/tools/config_setting/config_setting/widgets/inputs.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index e5a63e635a..244d3642ae 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -965,9 +965,11 @@ class RawJsonWidget(ConfigWidget, InputObject): self.override_value = override_value if override_value is NOT_SET: self._is_overriden = False + self._was_overriden = False value = self.start_value else: self._is_overriden = True + self._was_overriden = True value = override_value self.set_value(value) @@ -981,6 +983,8 @@ class RawJsonWidget(ConfigWidget, InputObject): self._is_invalid = self.text_input.has_invalid_value() if self._is_invalid: self._is_modified = True + elif self._is_overriden: + self._is_modified = self.item_value() != self.override_value else: self._is_modified = self.item_value() != self.global_value From 0797edccfd0e65e1e0d5712d853c99c2cf5a1f8b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 31 Aug 2020 10:02:32 +0200 Subject: [PATCH 292/813] overrides are loaded with metadatakey --- pype/tools/config_setting/config_setting/widgets/base.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/base.py b/pype/tools/config_setting/config_setting/widgets/base.py index 283a5ad0a4..fd558e1ea3 100644 --- a/pype/tools/config_setting/config_setting/widgets/base.py +++ b/pype/tools/config_setting/config_setting/widgets/base.py @@ -412,12 +412,13 @@ class ProjectWidget(QtWidgets.QWidget): def _on_project_change(self): project_name = self.project_list_widget.project_name() if project_name is None: - overrides = lib.NOT_SET + _overrides = lib.NOT_SET self.is_overidable = False else: - overrides = config.project_configurations_overrides(project_name) + _overrides = config.project_configurations_overrides(project_name) self.is_overidable = True + overrides = lib.convert_overrides_to_gui_data(_overrides) self.project_name = project_name self.ignore_value_changes = True for item in self.input_fields: From de0e9f2effe964ab54bdfc53e4634178ca3f7db1 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 31 Aug 2020 10:02:51 +0200 Subject: [PATCH 293/813] project overrides cancel modifcation changes on save --- pype/tools/config_setting/config_setting/widgets/base.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pype/tools/config_setting/config_setting/widgets/base.py b/pype/tools/config_setting/config_setting/widgets/base.py index fd558e1ea3..b412db5308 100644 --- a/pype/tools/config_setting/config_setting/widgets/base.py +++ b/pype/tools/config_setting/config_setting/widgets/base.py @@ -454,6 +454,7 @@ class ProjectWidget(QtWidgets.QWidget): self._save_defaults() else: self._save_overrides() + self._on_project_change() def _save_overrides(self): _data = {} From 6ea8b1ef051831f211c1e90a1aa06f34f150dba4 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 31 Aug 2020 10:03:00 +0200 Subject: [PATCH 294/813] changed default for few plugins --- .../projects_schema/1_plugins_gui_schema.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json index dbbcaf2b36..c70daab32c 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json @@ -82,7 +82,7 @@ "type": "boolean", "key": "enabled", "label": "Enabled", - "default": true + "default": false }, { "type": "text-singleline", "key": "note_with_intent_template", @@ -397,7 +397,7 @@ "type": "boolean", "key": "enabled", "label": "Enabled", - "default": false + "default": true }, { "type": "raw-json", "key": "nodes", From 78a336939e1b45a32ff8623de57bcec11d8b1b2f Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 31 Aug 2020 10:11:09 +0200 Subject: [PATCH 295/813] added possibility to have expanded expandable dict by default --- pype/tools/config_setting/config_setting/widgets/inputs.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 244d3642ae..b2b3b3990b 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1690,6 +1690,10 @@ class DictExpandWidget(ExpandingWidget, ConfigObject): self.set_content_widget(content_widget) + expanded = input_data.get("expanded", False) + if expanded: + self.toggle_content() + self.setAttribute(QtCore.Qt.WA_StyledBackground) self.content_widget = content_widget From 1bbfae31f9f7b2b25b46b7359fa7a4fe6f7ab028 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 31 Aug 2020 11:05:57 +0200 Subject: [PATCH 296/813] added update_global_values method --- .../config_setting/widgets/inputs.py | 124 ++++++++++++++---- 1 file changed, 97 insertions(+), 27 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index b2b3b3990b..950fdb5bf8 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -262,13 +262,22 @@ class BooleanWidget(ConfigWidget, InputObject): layout.addWidget(self.checkbox, 1) - value = NOT_SET if not self._as_widget: self.label_widget = label_widget + self.key = input_data["key"] keys = list(parent_keys) keys.append(self.key) self.keys = keys + + self.update_global_values(values) + self.override_value = NOT_SET + + self.checkbox.stateChanged.connect(self._on_value_change) + + def update_global_values(self, values): + value = NOT_SET + if not self._as_widget: value = self.value_from_values(values) if value is not NOT_SET: self.checkbox.setChecked(value) @@ -276,14 +285,11 @@ class BooleanWidget(ConfigWidget, InputObject): elif self.default_value is not NOT_SET: self.checkbox.setChecked(self.default_value) - self.override_value = NOT_SET self.global_value = value self.start_value = self.item_value() self._is_modified = self.global_value != self.start_value - self.checkbox.stateChanged.connect(self._on_value_change) - def set_value(self, value, *, global_value=False): # Ignore value change because if `self.isChecked()` has same # value as `value` the `_on_value_change` is not triggered @@ -389,7 +395,6 @@ class IntegerWidget(ConfigWidget, InputObject): layout.addWidget(label_widget, 0) layout.addWidget(self.int_input, 1) - value = NOT_SET if not self._as_widget: self.label_widget = label_widget @@ -398,6 +403,15 @@ class IntegerWidget(ConfigWidget, InputObject): keys.append(self.key) self.keys = keys + self.update_global_values(values) + + self.override_value = NOT_SET + + self.int_input.valueChanged.connect(self._on_value_change) + + def update_global_values(self, values): + value = NOT_SET + if not self._as_widget: value = self.value_from_values(values) if value is not NOT_SET: self.int_input.setValue(value) @@ -405,14 +419,11 @@ class IntegerWidget(ConfigWidget, InputObject): elif self.default_value is not NOT_SET: self.int_input.setValue(self.default_value) - self.override_value = NOT_SET self.global_value = value self.start_value = self.item_value() self._is_modified = self.global_value != self.start_value - self.int_input.valueChanged.connect(self._on_value_change) - def set_value(self, value, *, global_value=False): self.int_input.setValue(value) if global_value: @@ -521,7 +532,6 @@ class FloatWidget(ConfigWidget, InputObject): layout.addWidget(label_widget, 0) layout.addWidget(self.float_input, 1) - value = NOT_SET if not self._as_widget: self.label_widget = label_widget @@ -530,6 +540,15 @@ class FloatWidget(ConfigWidget, InputObject): keys.append(self.key) self.keys = keys + self.update_global_values(values) + + self.override_value = NOT_SET + + self.float_input.valueChanged.connect(self._on_value_change) + + def update_global_values(self, values): + value = NOT_SET + if not self._as_widget: value = self.value_from_values(values) if value is not NOT_SET: self.float_input.setValue(value) @@ -537,14 +556,11 @@ class FloatWidget(ConfigWidget, InputObject): elif self.default_value is not NOT_SET: self.float_input.setValue(self.default_value) - self.override_value = NOT_SET - self.start_value = self.item_value() self.global_value = value + self.start_value = self.item_value() self._is_modified = self.global_value != self.start_value - self.float_input.valueChanged.connect(self._on_value_change) - def set_value(self, value, *, global_value=False): self.float_input.setValue(value) if global_value: @@ -635,7 +651,6 @@ class TextSingleLineWidget(ConfigWidget, InputObject): layout.addWidget(label_widget, 0) layout.addWidget(self.text_input, 1) - value = NOT_SET if not self._as_widget: self.label_widget = label_widget @@ -644,6 +659,15 @@ class TextSingleLineWidget(ConfigWidget, InputObject): keys.append(self.key) self.keys = keys + self.update_global_values(values) + + self.override_value = NOT_SET + + self.text_input.textChanged.connect(self._on_value_change) + + def update_global_values(self, values): + value = NOT_SET + if not self._as_widget: value = self.value_from_values(values) if value is not NOT_SET: self.text_input.setText(value) @@ -651,14 +675,11 @@ class TextSingleLineWidget(ConfigWidget, InputObject): elif self.default_value is not NOT_SET: self.text_input.setText(self.default_value) - self.override_value = NOT_SET self.global_value = value self.start_value = self.item_value() self._is_modified = self.global_value != self.start_value - self.text_input.textChanged.connect(self._on_value_change) - def set_value(self, value, *, global_value=False): self.text_input.setText(value) if global_value: @@ -751,7 +772,6 @@ class TextMultiLineWidget(ConfigWidget, InputObject): layout.addWidget(label_widget, 0) layout.addWidget(self.text_input, 1) - value = NOT_SET if not self._as_widget: self.label_widget = label_widget @@ -760,6 +780,15 @@ class TextMultiLineWidget(ConfigWidget, InputObject): keys.append(self.key) self.keys = keys + self.update_global_values(values) + + self.override_value = NOT_SET + + self.text_input.textChanged.connect(self._on_value_change) + + def update_global_values(self, values): + value = NOT_SET + if not self._as_widget: value = self.value_from_values(values) if value is not NOT_SET: self.text_input.setPlainText(value) @@ -767,14 +796,11 @@ class TextMultiLineWidget(ConfigWidget, InputObject): elif self.default_value is not NOT_SET: self.text_input.setPlainText(self.default_value) - self.override_value = NOT_SET self.global_value = value self.start_value = self.item_value() self._is_modified = self.global_value != self.start_value - self.text_input.textChanged.connect(self._on_value_change) - def set_value(self, value, *, global_value=False): self.text_input.setPlainText(value) if global_value: @@ -921,7 +947,8 @@ class RawJsonWidget(ConfigWidget, InputObject): layout.addWidget(label_widget, 0) layout.addWidget(self.text_input, 1) - value = NOT_SET + self.override_value = NOT_SET + if not self._as_widget: self.label_widget = label_widget @@ -930,22 +957,27 @@ class RawJsonWidget(ConfigWidget, InputObject): keys.append(self.key) self.keys = keys + self.update_global_values(values) + + self.text_input.textChanged.connect(self._on_value_change) + + def update_global_values(self, values): + value = NOT_SET + if not self._as_widget: value = self.value_from_values(values) if value is not NOT_SET: self.text_input.set_value(value) elif self.default_value is not NOT_SET: self.text_input.set_value(self.default_value) + self._is_invalid = self.text_input.has_invalid_value() - self.override_value = NOT_SET self.global_value = value self.start_value = self.item_value() self._is_modified = self.global_value != self.start_value - self.text_input.textChanged.connect(self._on_value_change) - def set_value(self, value, *, global_value=False): self.text_input.set_value(value) if global_value: @@ -1107,6 +1139,13 @@ class ListSubWidget(QtWidgets.QWidget, ConfigObject): keys.append(self.key) self.keys = keys + self.update_global_values(values) + + self.override_value = NOT_SET + + def update_global_values(self, values): + old_inputs = tuple(self.input_fields) + value = self.value_from_values(values) if value is not NOT_SET: for item_value in value: @@ -1119,7 +1158,9 @@ class ListSubWidget(QtWidgets.QWidget, ConfigObject): if self.count() == 0: self.add_row() - self.override_value = NOT_SET + for old_input in old_inputs: + self.remove_row(old_input) + self.global_value = value self.start_value = self.item_value() @@ -1266,6 +1307,9 @@ class ListWidget(ConfigWidget, InputObject): self.value_widget.value_changed.connect(self._on_value_change) + def update_global_values(self, values): + self.value_widget.update_global_values(values) + @property def start_value(self): return self.value_widget.start_value @@ -1433,6 +1477,7 @@ class ModifiableDictItem(QtWidgets.QWidget, ConfigObject): return {key: value} +# TODO Move subwidget to main widget class ModifiableDictSubWidget(QtWidgets.QWidget, ConfigObject): value_changed = QtCore.Signal(object) @@ -1457,6 +1502,11 @@ class ModifiableDictSubWidget(QtWidgets.QWidget, ConfigObject): keys.append(self.key) self.keys = keys + self.update_global_values(values) + + def update_global_values(self, values): + old_inputs = tuple(self.input_fields) + value = self.value_from_values(values) if value is not NOT_SET: for item_key, item_value in value.items(): @@ -1469,7 +1519,8 @@ class ModifiableDictSubWidget(QtWidgets.QWidget, ConfigObject): if self.count() == 0: self.add_row() - self.override_value = NOT_SET + for old_input in old_inputs: + self.remove_row(old_input) self.global_value = value self.start_value = self.config_value() @@ -1600,6 +1651,9 @@ class ModifiableDict(ExpandingWidget, InputObject): self._is_modified = self.global_value != self.start_value + def update_global_values(self, values): + self.value_widget.update_global_values(values) + def _on_value_change(self, item=None): if self.ignore_value_changes: return @@ -1723,6 +1777,10 @@ class DictExpandWidget(ExpandingWidget, ConfigObject): self._is_modified = self.child_modified self._is_overriden = self._was_overriden + def update_global_values(self, values): + for item in self.input_fields: + item.update_global_values(values) + def apply_overrides(self, override_value): # Make sure this is set to False self._is_overriden = False @@ -1943,6 +2001,10 @@ class DictWidget(ConfigWidget, ConfigObject): self._is_modified = self.child_modified self._is_overriden = self._was_overriden + def update_global_values(self, values): + for item in self.input_fields: + item.update_global_values(values) + def apply_overrides(self, override_value): # Make sure this is set to False self._is_overriden = False @@ -2207,6 +2269,10 @@ class DictInvisible(ConfigWidget, ConfigObject): self._is_modified = self.child_modified self._is_overriden = self._was_overriden + def update_global_values(self, values): + for item in self.input_fields: + item.update_global_values(values) + def apply_overrides(self, override_value): self._is_overriden = False for item in self.input_fields: @@ -2294,6 +2360,10 @@ class DictFormWidget(ConfigWidget, ConfigObject): self._is_modified = self.child_modified self._is_overriden = self._was_overriden + def update_global_values(self, values): + for item in self.input_fields.values(): + item.update_global_values(values) + def _on_value_change(self, item=None): if self.ignore_value_changes: return From 66d65e85105adfc79e176d04605775d3d8a345e1 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 31 Aug 2020 11:19:41 +0200 Subject: [PATCH 297/813] global values are updated on save --- .../config_setting/widgets/base.py | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/pype/tools/config_setting/config_setting/widgets/base.py b/pype/tools/config_setting/config_setting/widgets/base.py index b412db5308..afcac74555 100644 --- a/pype/tools/config_setting/config_setting/widgets/base.py +++ b/pype/tools/config_setting/config_setting/widgets/base.py @@ -165,6 +165,16 @@ class StudioWidget(QtWidgets.QWidget): with open(output_path, "w") as file_stream: json.dump(origin_values, file_stream, indent=4) + self._update_global_values() + + def _update_global_values(self): + values = {"studio": config.studio_configurations()} + for input_field in self.input_fields: + input_field.update_global_values(values) + + for input_field in self.input_fields: + input_field.hierarchical_style_update() + def add_children_gui(self, child_configuration, values): item_type = child_configuration["type"] klass = lib.TypeToKlass.types.get(item_type) @@ -454,7 +464,6 @@ class ProjectWidget(QtWidgets.QWidget): self._save_defaults() else: self._save_overrides() - self._on_project_change() def _save_overrides(self): _data = {} @@ -481,6 +490,8 @@ class ProjectWidget(QtWidgets.QWidget): with open(overrides_json_path, "w") as file_stream: json.dump(output_data, file_stream, indent=4) + self._on_project_change() + def _save_defaults(self): output = {} for item in self.input_fields: @@ -534,3 +545,13 @@ class ProjectWidget(QtWidgets.QWidget): print("Saving data to: ", output_path) with open(output_path, "w") as file_stream: json.dump(origin_values, file_stream, indent=4) + + self._update_global_values() + + def _update_global_values(self): + values = {"project": config.global_project_configurations()} + for input_field in self.input_fields: + input_field.update_global_values(values) + + for input_field in self.input_fields: + input_field.hierarchical_style_update() From b0375abaddf98c569a22e93390b9471047909773 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 31 Aug 2020 15:01:16 +0200 Subject: [PATCH 298/813] removed update_style --- .../config_setting/config_setting/widgets/inputs.py | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 950fdb5bf8..4df9bb2736 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -317,7 +317,6 @@ class BooleanWidget(ConfigWidget, InputObject): value = override_value self.set_value(value) - self.update_style() def _on_value_change(self, item=None): if self.ignore_value_changes: @@ -451,7 +450,6 @@ class IntegerWidget(ConfigWidget, InputObject): value = override_value self.set_value(value) - self.update_style() def _on_value_change(self, item=None): if self.ignore_value_changes: @@ -583,7 +581,6 @@ class FloatWidget(ConfigWidget, InputObject): value = override_value self.set_value(value) - self.update_style() def clear_value(self): self.set_value(0) @@ -704,7 +701,6 @@ class TextSingleLineWidget(ConfigWidget, InputObject): value = override_value self.set_value(value) - self.update_style() def clear_value(self): self.set_value("") @@ -823,7 +819,6 @@ class TextMultiLineWidget(ConfigWidget, InputObject): value = override_value self.set_value(value) - self.update_style() def clear_value(self): self.set_value("") @@ -1005,7 +1000,6 @@ class RawJsonWidget(ConfigWidget, InputObject): value = override_value self.set_value(value) - self.update_style() def _on_value_change(self, item=None): self._is_invalid = self.text_input.has_invalid_value() @@ -1348,7 +1342,6 @@ class ListWidget(ConfigWidget, InputObject): self.value_widget.apply_overrides(override_value) self._is_modified = False self._state = None - self.update_style() def update_style(self): state = self.style_state( @@ -1684,7 +1677,6 @@ class ModifiableDict(ExpandingWidget, InputObject): value = override_value self.set_value(value) - self.update_style() def update_style(self): state = self.style_state( @@ -1803,7 +1795,6 @@ class DictExpandWidget(ExpandingWidget, ConfigObject): ) ) self._was_overriden = bool(self._is_overriden) - self.update_style() def _on_value_change(self, item=None): if self.ignore_value_changes: @@ -2026,7 +2017,7 @@ class DictWidget(ConfigWidget, ConfigObject): or self.child_overriden ) ) - self.update_style() + self._was_overriden = bool(self._is_overriden) def _on_value_change(self, item=None): if self.ignore_value_changes: @@ -2290,7 +2281,7 @@ class DictInvisible(ConfigWidget, ConfigObject): or self.child_overriden ) ) - self.update_style() + self._was_overriden = bool(self._is_overriden) def overrides(self): if not self.is_overriden and not self.child_overriden: From 084d9a9a116bc830daf103c4abe9e218da77be10 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 31 Aug 2020 15:02:16 +0200 Subject: [PATCH 299/813] implemented override_value_from_values for override values --- .../config_setting/widgets/inputs.py | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 4df9bb2736..80cb168fda 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -126,6 +126,39 @@ class ConfigObject: value = value[key] return value + def override_value_from_values(self, values, keys=None): + """Global getter of value based on loaded values.""" + if not values or values is AS_WIDGET: + return NOT_SET + + if keys is None: + keys = self.keys + + value = values + if not keys: + return value, False + + parent_value = None + last_key = None + for key in keys: + if not isinstance(value, dict): + raise TypeError( + "Expected dictionary got {}.".format(str(type(value))) + ) + if key not in value: + return NOT_SET, False + + parent_value = value + last_key = key + value = value[key] + + if not parent_value: + return value, False + + metadata = parent_value.get(METADATA_KEY) or {} + groups = metadata.get("group") or tuple() + return value, last_key in groups + def style_state(self, is_invalid, is_overriden, is_modified): items = [] if is_invalid: From 077f5c7163a7bbeea63f8cbcec9b278a27ba1927 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 31 Aug 2020 16:17:56 +0200 Subject: [PATCH 300/813] different logic for applying overrides --- .../config_setting/widgets/base.py | 2 +- .../config_setting/widgets/inputs.py | 276 ++++++------------ 2 files changed, 96 insertions(+), 182 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/base.py b/pype/tools/config_setting/config_setting/widgets/base.py index afcac74555..9c1fee5b6f 100644 --- a/pype/tools/config_setting/config_setting/widgets/base.py +++ b/pype/tools/config_setting/config_setting/widgets/base.py @@ -428,7 +428,7 @@ class ProjectWidget(QtWidgets.QWidget): _overrides = config.project_configurations_overrides(project_name) self.is_overidable = True - overrides = lib.convert_overrides_to_gui_data(_overrides) + overrides = {"project": lib.convert_overrides_to_gui_data(_overrides)} self.project_name = project_name self.ignore_value_changes = True for item in self.input_fields: diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 80cb168fda..3eeea4df6a 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -126,39 +126,6 @@ class ConfigObject: value = value[key] return value - def override_value_from_values(self, values, keys=None): - """Global getter of value based on loaded values.""" - if not values or values is AS_WIDGET: - return NOT_SET - - if keys is None: - keys = self.keys - - value = values - if not keys: - return value, False - - parent_value = None - last_key = None - for key in keys: - if not isinstance(value, dict): - raise TypeError( - "Expected dictionary got {}.".format(str(type(value))) - ) - if key not in value: - return NOT_SET, False - - parent_value = value - last_key = key - value = value[key] - - if not parent_value: - return value, False - - metadata = parent_value.get(METADATA_KEY) or {} - groups = metadata.get("group") or tuple() - return value, last_key in groups - def style_state(self, is_invalid, is_overriden, is_modified): items = [] if is_invalid: @@ -222,6 +189,28 @@ class InputObject(ConfigObject): self._is_modified = False self._was_overriden = False + def apply_overrides(self, parent_values): + self._is_modified = False + self._state = None + + if parent_values is NOT_SET or self.key not in parent_values: + override_value = NOT_SET + else: + override_value = parent_values[self.key] + + self.override_value = override_value + + if override_value is NOT_SET: + self._is_overriden = False + self._was_overriden = False + value = self.start_value + else: + self._is_overriden = True + self._was_overriden = True + value = override_value + + self.set_value(value) + def discard_changes(self): if ( self.is_overidable @@ -336,21 +325,6 @@ class BooleanWidget(ConfigWidget, InputObject): def clear_value(self): self.set_value(False) - def apply_overrides(self, override_value): - self._is_modified = False - self._state = None - self.override_value = override_value - if override_value is NOT_SET: - self._is_overriden = False - self._was_overriden = False - value = self.start_value - else: - self._is_overriden = True - self._was_overriden = True - value = override_value - - self.set_value(value) - def _on_value_change(self, item=None): if self.ignore_value_changes: return @@ -469,21 +443,6 @@ class IntegerWidget(ConfigWidget, InputObject): def reset_value(self): self.set_value(self.start_value) - def apply_overrides(self, override_value): - self._is_modified = False - self._state = None - self.override_value = override_value - if override_value is NOT_SET: - self._is_overriden = False - self._was_overriden = False - value = self.global_value - else: - self._is_overriden = True - self._was_overriden = True - value = override_value - - self.set_value(value) - def _on_value_change(self, item=None): if self.ignore_value_changes: return @@ -602,19 +561,6 @@ class FloatWidget(ConfigWidget, InputObject): def reset_value(self): self.set_value(self.global_value) - def apply_overrides(self, override_value): - self._is_modified = False - self._state = None - self.override_value = override_value - if override_value is NOT_SET: - self._is_overriden = False - value = self.start_value - else: - self._is_overriden = True - value = override_value - - self.set_value(value) - def clear_value(self): self.set_value(0) @@ -720,21 +666,6 @@ class TextSingleLineWidget(ConfigWidget, InputObject): def reset_value(self): self.set_value(self.start_value) - def apply_overrides(self, override_value): - self._is_modified = False - self._state = None - self.override_value = override_value - if override_value is NOT_SET: - self._is_overriden = False - self._was_overriden = False - value = self.start_value - else: - self._is_overriden = True - self._was_overriden = True - value = override_value - - self.set_value(value) - def clear_value(self): self.set_value("") @@ -840,19 +771,6 @@ class TextMultiLineWidget(ConfigWidget, InputObject): def reset_value(self): self.set_value(self.start_value) - def apply_overrides(self, override_value): - self._is_modified = False - self._state = None - self.override_value = override_value - if override_value is NOT_SET: - self._is_overriden = False - value = self.start_value - else: - self._is_overriden = True - value = override_value - - self.set_value(value) - def clear_value(self): self.set_value("") @@ -1019,21 +937,6 @@ class RawJsonWidget(ConfigWidget, InputObject): def clear_value(self): self.set_value("") - def apply_overrides(self, override_value): - self._is_modified = False - self._state = None - self.override_value = override_value - if override_value is NOT_SET: - self._is_overriden = False - self._was_overriden = False - value = self.start_value - else: - self._is_overriden = True - self._was_overriden = True - value = override_value - - self.set_value(value) - def _on_value_change(self, item=None): self._is_invalid = self.text_input.has_invalid_value() if self.ignore_value_changes: @@ -1206,13 +1109,22 @@ class ListSubWidget(QtWidgets.QWidget, ConfigObject): self.start_value = self.item_value() self._on_value_change() - def apply_overrides(self, override_value): + # TODO use same as InputObject + def apply_overrides(self, parent_values): + if parent_values is NOT_SET or self.key not in parent_values: + override_value = NOT_SET + else: + override_value = parent_values[self.key] + self.override_value = override_value + if override_value is NOT_SET: self._is_overriden = False + self._was_overriden = False value = self.start_value else: self._is_overriden = True + self._was_overriden = True value = override_value self.set_value(value) @@ -1372,9 +1284,9 @@ class ListWidget(ConfigWidget, InputObject): self.value_widget.clear_value() def apply_overrides(self, override_value): - self.value_widget.apply_overrides(override_value) self._is_modified = False self._state = None + self.value_widget.apply_overrides(override_value) def update_style(self): state = self.style_state( @@ -1696,21 +1608,6 @@ class ModifiableDict(ExpandingWidget, InputObject): self.update_style() - def apply_overrides(self, override_value): - self._state = None - self._is_modified = False - self.override_value = override_value - if override_value is NOT_SET: - self._is_overriden = False - self._was_overriden = False - value = self.global_value - else: - self._is_overriden = True - self._was_overriden = True - value = override_value - - self.set_value(value) - def update_style(self): state = self.style_state( self.is_invalid, self.is_overriden, self.is_modified @@ -1806,27 +1703,30 @@ class DictExpandWidget(ExpandingWidget, ConfigObject): for item in self.input_fields: item.update_global_values(values) - def apply_overrides(self, override_value): + def apply_overrides(self, parent_values): # Make sure this is set to False - self._is_overriden = False self._state = None self._child_state = None + + metadata = {} + groups = tuple() + override_values = NOT_SET + if parent_values is not NOT_SET: + metadata = parent_values.get(METADATA_KEY) or metadata + groups = metadata.get("groups") or groups + override_values = parent_values.get(self.key, override_values) + + self._is_overriden = self.key in groups + for item in self.input_fields: - if override_value is NOT_SET: - child_value = NOT_SET - else: - child_value = override_value.get(item.key, NOT_SET) + item.apply_overrides(override_values) - item.apply_overrides(child_value) - - self._is_overriden = ( - self.is_group - and self.is_overidable - and ( - override_value is not NOT_SET - or self.child_overriden + if not self._is_overriden: + self._is_overriden = ( + self.is_group + and self.is_overidable + and self.child_overriden ) - ) self._was_overriden = bool(self._is_overriden) def _on_value_change(self, item=None): @@ -2029,27 +1929,30 @@ class DictWidget(ConfigWidget, ConfigObject): for item in self.input_fields: item.update_global_values(values) - def apply_overrides(self, override_value): + def apply_overrides(self, parent_values): # Make sure this is set to False - self._is_overriden = False self._state = None self._child_state = None + + metadata = {} + groups = tuple() + override_values = NOT_SET + if parent_values is not NOT_SET: + metadata = parent_values.get(METADATA_KEY) or metadata + groups = metadata.get("groups") or groups + override_values = parent_values.get(self.key, override_values) + + self._is_overriden = self.key in groups + for item in self.input_fields: - if override_value is NOT_SET: - child_value = NOT_SET - else: - child_value = override_value.get(item.key, NOT_SET) + item.apply_overrides(override_values) - item.apply_overrides(child_value) - - self._is_overriden = ( - self.is_group - and self.is_overidable - and ( - override_value is not NOT_SET - or self.child_overriden + if not self._is_overriden: + self._is_overriden = ( + self.is_group + and self.is_overidable + and self.child_overriden ) - ) self._was_overriden = bool(self._is_overriden) def _on_value_change(self, item=None): @@ -2297,23 +2200,30 @@ class DictInvisible(ConfigWidget, ConfigObject): for item in self.input_fields: item.update_global_values(values) - def apply_overrides(self, override_value): - self._is_overriden = False - for item in self.input_fields: - if override_value is NOT_SET: - child_value = NOT_SET - else: - child_value = override_value.get(item.key, NOT_SET) - item.apply_overrides(child_value) + def apply_overrides(self, parent_values): + # Make sure this is set to False + self._state = None + self._child_state = None - self._is_overriden = ( - self.is_group - and self.is_overidable - and ( - override_value is not None - or self.child_overriden + metadata = {} + groups = tuple() + override_values = NOT_SET + if parent_values is not NOT_SET: + metadata = parent_values.get(METADATA_KEY) or metadata + groups = metadata.get("groups") or groups + override_values = parent_values.get(self.key, override_values) + + self._is_overriden = self.key in groups + + for item in self.input_fields: + item.apply_overrides(override_values) + + if not self._is_overriden: + self._is_overriden = ( + self.is_group + and self.is_overidable + and self.child_overriden ) - ) self._was_overriden = bool(self._is_overriden) def overrides(self): @@ -2377,6 +2287,10 @@ class DictFormWidget(ConfigWidget, ConfigObject): return super(DictFormWidget, self).mouseReleaseEvent(event) + def apply_overrides(self, parent_values): + for item in self.input_fields.values(): + item.apply_overrides(parent_values) + def discard_changes(self): for item in self.input_fields.values(): item.discard_changes() From b4e091c3fc50166192d3b86aa128e6a63a397660 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Mon, 31 Aug 2020 16:45:36 +0200 Subject: [PATCH 301/813] strip dot from repre names in single frame renders --- pype/plugins/global/publish/submit_publish_job.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/plugins/global/publish/submit_publish_job.py b/pype/plugins/global/publish/submit_publish_job.py index ab5d6cf9b2..758872e717 100644 --- a/pype/plugins/global/publish/submit_publish_job.py +++ b/pype/plugins/global/publish/submit_publish_job.py @@ -428,7 +428,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): "to render, don't know what to do " "with them.") col = rem[0] - _, ext = os.path.splitext(col) + ext = os.path.splitext(col)[1].lstrip(".") else: # but we really expect only one collection. # Nothing else make sense. From 618b2f4825aa87608be50ba4e5491149950a7002 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 31 Aug 2020 17:21:16 +0200 Subject: [PATCH 302/813] added Timers manager to tray modules --- .../config_gui_schema/studio_schema/1_tray_items.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_tray_items.json b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_tray_items.json index 45b1bc65ce..4e0e040dfe 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_tray_items.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_tray_items.json @@ -44,6 +44,10 @@ "type": "boolean", "key": "Idle Manager", "label": "Idle Manager" + }, { + "type": "boolean", + "key": "Timers Manager", + "label": "Timers Manager" }, { "type": "boolean", "key": "Rest Api", From 1d72f9d01310d5ee72c6e5a51d875f13157c09ac Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 31 Aug 2020 17:22:18 +0200 Subject: [PATCH 303/813] fix method name --- pype/tools/config_setting/config_setting/widgets/inputs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 3eeea4df6a..e054f918cd 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1036,7 +1036,7 @@ class ListItem(QtWidgets.QWidget, ConfigObject): def on_remove_clicked(self): if self.is_single: - self.value_input.clear() + self.value_input.clear_value() else: self.parent().remove_row(self) From 8276c7397154f3423dd184c54aee642b7770110f Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 31 Aug 2020 17:22:28 +0200 Subject: [PATCH 304/813] added exclude ports to schema --- .../config_gui_schema/studio_schema/1_tray_items.json | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_tray_items.json b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_tray_items.json index 4e0e040dfe..849019c89e 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_tray_items.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_tray_items.json @@ -76,6 +76,15 @@ "label": "Default Port", "minimum": 1, "maximum": 65535 + }, { + "type": "list", + "object_type": "int", + "key": "exclude_ports", + "label": "Exclude ports", + "input_modifiers": { + "minimum": 1, + "maximum": 65535 + } } ] }, { From 85df0e3dbbb507d6eab569e6403401dca0f34484 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 31 Aug 2020 17:59:24 +0200 Subject: [PATCH 305/813] item widget in list is after buttons --- .../config_setting/widgets/inputs.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index e054f918cd..5d41ce836c 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -993,15 +993,6 @@ class ListItem(QtWidgets.QWidget, ConfigObject): layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(3) - ItemKlass = TypeToKlass.types[object_type] - self.value_input = ItemKlass( - input_modifiers, - AS_WIDGET, - [], - self, - None - ) - self.add_btn = QtWidgets.QPushButton("+") self.remove_btn = QtWidgets.QPushButton("-") @@ -1014,13 +1005,22 @@ class ListItem(QtWidgets.QWidget, ConfigObject): self.add_btn.setProperty("btn-type", "tool-item") self.remove_btn.setProperty("btn-type", "tool-item") - layout.addWidget(self.value_input, 1) layout.addWidget(self.add_btn, 0) layout.addWidget(self.remove_btn, 0) self.add_btn.clicked.connect(self.on_add_clicked) self.remove_btn.clicked.connect(self.on_remove_clicked) + ItemKlass = TypeToKlass.types[object_type] + self.value_input = ItemKlass( + input_modifiers, + AS_WIDGET, + [], + self, + None + ) + layout.addWidget(self.value_input, 1) + self.value_input.value_changed.connect(self._on_value_change) self.is_single = False From 2e8abd02cb29511726b6f860511b4b31a4dc01a9 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 31 Aug 2020 18:00:57 +0200 Subject: [PATCH 306/813] empty list is shown as disabled first item --- .../config_setting/widgets/inputs.py | 51 ++++++++----------- 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 5d41ce836c..d197e85d1e 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1023,7 +1023,9 @@ class ListItem(QtWidgets.QWidget, ConfigObject): self.value_input.value_changed.connect(self._on_value_change) - self.is_single = False + def set_as_empty(self, is_empty=True): + self.value_input.setEnabled(not is_empty) + self.remove_btn.setEnabled(not is_empty) def _on_value_change(self, item=None): self.value_changed.emit(self) @@ -1032,16 +1034,18 @@ class ListItem(QtWidgets.QWidget, ConfigObject): return self.parent().input_fields.index(self) def on_add_clicked(self): - self.parent().add_row(row=self.row() + 1) + if self.value_input.isEnabled(): + self.parent().add_row(row=self.row() + 1) + else: + self.set_as_empty(False) def on_remove_clicked(self): - if self.is_single: - self.value_input.clear_value() - else: - self.parent().remove_row(self) + self.parent().remove_row(self) def config_value(self): - return self.value_input.item_value() + if self.value_input.isEnabled(): + return self.value_input.item_value() + return NOT_SET # TODO Move subwidget to main widget @@ -1085,12 +1089,12 @@ class ListSubWidget(QtWidgets.QWidget, ConfigObject): for item_value in self.default_value: self.add_row(value=item_value) - if self.count() == 0: - self.add_row() - for old_input in old_inputs: self.remove_row(old_input) + if self.count() == 0: + self.add_row(is_empty=True) + self.global_value = value self.start_value = self.item_value() @@ -1141,18 +1145,11 @@ class ListSubWidget(QtWidgets.QWidget, ConfigObject): def count(self): return len(self.input_fields) - def add_row(self, row=None, value=None): + def add_row(self, row=None, value=None, is_empty=False): # Create new item item_widget = ListItem(self.object_type, self.input_modifiers, self) - - # Set/unset if new item is single item - current_count = self.count() - if current_count == 0: - item_widget.is_single = True - elif current_count == 1: - for _input_field in self.input_fields: - _input_field.is_single = False - + if is_empty: + item_widget.set_as_empty() item_widget.value_changed.connect(self._on_value_change) if row is None: @@ -1186,12 +1183,8 @@ class ListSubWidget(QtWidgets.QWidget, ConfigObject): item_widget.setParent(None) item_widget.deleteLater() - current_count = self.count() - if current_count == 0: - self.add_row() - elif current_count == 1: - for _input_field in self.input_fields: - _input_field.is_single = True + if self.count() == 0: + self.add_row(is_empty=True) self._on_value_change() self.parent().updateGeometry() @@ -1199,9 +1192,9 @@ class ListSubWidget(QtWidgets.QWidget, ConfigObject): def item_value(self): output = [] for item in self.input_fields: - text = item.config_value() - if text: - output.append(text) + value = item.config_value() + if value is not NOT_SET: + output.append(value) return output From ed2d4175ed22d6139f4270f7cbc78860dc22a0fd Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 31 Aug 2020 18:08:07 +0200 Subject: [PATCH 307/813] added color for disabled widgets --- pype/tools/config_setting/config_setting/style/style.css | 4 ++++ pype/tools/config_setting/config_setting/widgets/inputs.py | 1 + 2 files changed, 5 insertions(+) diff --git a/pype/tools/config_setting/config_setting/style/style.css b/pype/tools/config_setting/config_setting/style/style.css index c575ed64c3..b39eaf0802 100644 --- a/pype/tools/config_setting/config_setting/style/style.css +++ b/pype/tools/config_setting/config_setting/style/style.css @@ -5,6 +5,10 @@ QWidget { border-radius: 0px; } +QWidget:disabled { + background-color: #4e6474; +} + QMenu { border: 1px solid #555555; background-color: #1d272f; diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index d197e85d1e..08bc90766f 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1026,6 +1026,7 @@ class ListItem(QtWidgets.QWidget, ConfigObject): def set_as_empty(self, is_empty=True): self.value_input.setEnabled(not is_empty) self.remove_btn.setEnabled(not is_empty) + self._on_value_change() def _on_value_change(self, item=None): self.value_changed.emit(self) From 8bca9da7902cdf3422d7f3dd35b8a132d85d309c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 31 Aug 2020 18:45:25 +0200 Subject: [PATCH 308/813] removed ConfigWidget --- .../config_setting/widgets/inputs.py | 66 +++++++++++++++---- .../config_setting/widgets/widgets.py | 40 +---------- 2 files changed, 57 insertions(+), 49 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 08bc90766f..f603514784 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -2,7 +2,6 @@ import json import logging from Qt import QtWidgets, QtCore, QtGui from .widgets import ( - ConfigWidget, ExpandingWidget, ModifiedIntSpinBox, ModifiedFloatSpinBox @@ -11,6 +10,8 @@ from .lib import NOT_SET, AS_WIDGET, METADATA_KEY, TypeToKlass class ConfigObject: + allow_actions = True + default_state = "" _is_overriden = False _is_modified = False @@ -173,6 +174,49 @@ class ConfigObject: "Method `hierarchical_style_update` not implemented!" ) + def mouseReleaseEvent(self, event): + if self.allow_actions and event.button() == QtCore.Qt.RightButton: + menu = QtWidgets.QMenu() + + actions_mapping = {} + if self.child_modified: + action = QtWidgets.QAction("Discard changes") + actions_mapping[action] = self._discard_changes + menu.addAction(action) + + if ( + not self.any_parent_overriden() + and (self.is_overriden or self.child_overriden) + ): + # TODO better label + action = QtWidgets.QAction("Remove override") + actions_mapping[action] = self._remove_overrides + menu.addAction(action) + + if not actions_mapping: + action = QtWidgets.QAction("< No action >") + actions_mapping[action] = None + menu.addAction(action) + + result = menu.exec_(QtGui.QCursor.pos()) + if result: + to_run = actions_mapping[result] + if to_run: + to_run() + return + + mro = type(self).mro() + index = mro.index(self.__class__) + item = None + for idx in range(index + 1, len(mro)): + _item = mro[idx] + if hasattr(_item, "mouseReleaseEvent"): + item = _item + break + + if item: + return item.mouseReleaseEvent(self, event) + class InputObject(ConfigObject): def overrides(self): @@ -251,7 +295,7 @@ class InputObject(ConfigObject): return -class BooleanWidget(ConfigWidget, InputObject): +class BooleanWidget(QtWidgets.QWidget, InputObject): value_changed = QtCore.Signal(object) def __init__( @@ -365,7 +409,7 @@ class BooleanWidget(ConfigWidget, InputObject): return self.checkbox.isChecked() -class IntegerWidget(ConfigWidget, InputObject): +class IntegerWidget(QtWidgets.QWidget, InputObject): value_changed = QtCore.Signal(object) input_modifiers = ("minimum", "maximum") @@ -476,7 +520,7 @@ class IntegerWidget(ConfigWidget, InputObject): return self.int_input.value() -class FloatWidget(ConfigWidget, InputObject): +class FloatWidget(QtWidgets.QWidget, InputObject): value_changed = QtCore.Signal(object) input_modifiers = ("minimum", "maximum", "decimal") @@ -597,7 +641,7 @@ class FloatWidget(ConfigWidget, InputObject): return self.float_input.value() -class TextSingleLineWidget(ConfigWidget, InputObject): +class TextSingleLineWidget(QtWidgets.QWidget, InputObject): value_changed = QtCore.Signal(object) def __init__( @@ -702,7 +746,7 @@ class TextSingleLineWidget(ConfigWidget, InputObject): return self.text_input.text() -class TextMultiLineWidget(ConfigWidget, InputObject): +class TextMultiLineWidget(QtWidgets.QWidget, InputObject): value_changed = QtCore.Signal(object) def __init__( @@ -859,7 +903,7 @@ class RawJsonInput(QtWidgets.QPlainTextEdit): super(RawJsonInput, self).resizeEvent(event) -class RawJsonWidget(ConfigWidget, InputObject): +class RawJsonWidget(QtWidgets.QWidget, InputObject): value_changed = QtCore.Signal(object) def __init__( @@ -1200,7 +1244,7 @@ class ListSubWidget(QtWidgets.QWidget, ConfigObject): return output -class ListWidget(ConfigWidget, InputObject): +class ListWidget(QtWidgets.QWidget, InputObject): value_changed = QtCore.Signal(object) def __init__( @@ -1841,7 +1885,7 @@ class DictExpandWidget(ExpandingWidget, ConfigObject): return {self.key: values}, self.is_group -class DictWidget(ConfigWidget, ConfigObject): +class DictWidget(QtWidgets.QWidget, ConfigObject): value_changed = QtCore.Signal(object) def __init__( @@ -2068,7 +2112,7 @@ class DictWidget(ConfigWidget, ConfigObject): return {self.key: values}, self.is_group -class DictInvisible(ConfigWidget, ConfigObject): +class DictInvisible(QtWidgets.QWidget, ConfigObject): # TODO is not overridable by itself value_changed = QtCore.Signal(object) allow_actions = False @@ -2244,7 +2288,7 @@ class FormLabel(QtWidgets.QLabel): # Proxy for form layout -class DictFormWidget(ConfigWidget, ConfigObject): +class DictFormWidget(QtWidgets.QWidget, ConfigObject): value_changed = QtCore.Signal(object) allow_actions = False diff --git a/pype/tools/config_setting/config_setting/widgets/widgets.py b/pype/tools/config_setting/config_setting/widgets/widgets.py index b824ea8720..e551d1e9c3 100644 --- a/pype/tools/config_setting/config_setting/widgets/widgets.py +++ b/pype/tools/config_setting/config_setting/widgets/widgets.py @@ -1,4 +1,4 @@ -from Qt import QtWidgets, QtCore, QtGui +from Qt import QtWidgets, QtCore class ModifiedIntSpinBox(QtWidgets.QSpinBox): @@ -48,43 +48,7 @@ class ClickableWidget(QtWidgets.QLabel): super(ClickableWidget, self).mouseReleaseEvent(event) -class ConfigWidget(QtWidgets.QWidget): - allow_actions = True - - def mouseReleaseEvent(self, event): - if self.allow_actions and event.button() == QtCore.Qt.RightButton: - menu = QtWidgets.QMenu() - - actions_mapping = {} - if self.child_modified: - action = QtWidgets.QAction("Discard changes") - actions_mapping[action] = self._discard_changes - menu.addAction(action) - - if ( - not self.any_parent_overriden() - and (self.is_overriden or self.child_overriden) - ): - # TODO better label - action = QtWidgets.QAction("Remove override") - actions_mapping[action] = self._remove_overrides - menu.addAction(action) - - if not actions_mapping: - action = QtWidgets.QAction("< No action >") - actions_mapping[action] = None - menu.addAction(action) - - result = menu.exec_(QtGui.QCursor.pos()) - if result: - to_run = actions_mapping[result] - if to_run: - to_run() - return - super(ConfigWidget, self).mouseReleaseEvent(event) - - -class ExpandingWidget(ConfigWidget): +class ExpandingWidget(QtWidgets.QWidget): def __init__(self, label, parent): super(ExpandingWidget, self).__init__(parent) self.setObjectName("ExpandingWidget") From e7875892e992105d0698d0f2b510914408d63537 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 31 Aug 2020 18:45:47 +0200 Subject: [PATCH 309/813] added better disabled style --- pype/tools/config_setting/config_setting/style/style.css | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pype/tools/config_setting/config_setting/style/style.css b/pype/tools/config_setting/config_setting/style/style.css index b39eaf0802..996b73b9cd 100644 --- a/pype/tools/config_setting/config_setting/style/style.css +++ b/pype/tools/config_setting/config_setting/style/style.css @@ -5,10 +5,6 @@ QWidget { border-radius: 0px; } -QWidget:disabled { - background-color: #4e6474; -} - QMenu { border: 1px solid #555555; background-color: #1d272f; @@ -36,6 +32,10 @@ QLineEdit, QSpinBox, QDoubleSpinBox, QPlainTextEdit, QTextEdit { background-color: #1d272f; } +QLineEdit:disabled, QSpinBox:disabled, QDoubleSpinBox:disabled, QPlainTextEdit:disabled, QTextEdit:disabled, QPushButton:disabled { + background-color: #4e6474; +} + QLineEdit:focus, QSpinBox:focus, QDoubleSpinBox:focus, QPlainTextEdit:focus, QTextEdit:focus { border: 1px solid #ffffff; } From a1d3616bb59d12d52c3b7c2bc97ced31d6a6c4ee Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 31 Aug 2020 19:23:56 +0200 Subject: [PATCH 310/813] removed list subwidget --- .../config_setting/widgets/inputs.py | 200 +++++++----------- 1 file changed, 74 insertions(+), 126 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index f603514784..e4a1fba096 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1093,26 +1093,49 @@ class ListItem(QtWidgets.QWidget, ConfigObject): return NOT_SET -# TODO Move subwidget to main widget -class ListSubWidget(QtWidgets.QWidget, ConfigObject): +class ListWidget(QtWidgets.QWidget, InputObject): value_changed = QtCore.Signal(object) - def __init__(self, input_data, values, parent_keys, parent): + def __init__( + self, input_data, values, parent_keys, parent, label_widget=None + ): self._parent = parent - super(ListSubWidget, self).__init__(parent) - self.setObjectName("ListSubWidget") + super(ListWidget, self).__init__(parent) + self.setObjectName("ListWidget") - layout = QtWidgets.QVBoxLayout(self) - layout.setContentsMargins(0, 5, 0, 5) - layout.setSpacing(3) - self.setLayout(layout) + self._state = None + self.is_group = input_data.get("is_group", False) - self.input_fields = [] self.object_type = input_data["object_type"] self.default_value = input_data.get("default", NOT_SET) self.input_modifiers = input_data.get("input_modifiers") or {} + self.input_fields = [] + + inputs_widget = QtWidgets.QWidget(self) + inputs_widget.setAttribute(QtCore.Qt.WA_StyledBackground) + + inputs_layout = QtWidgets.QVBoxLayout(inputs_widget) + inputs_layout.setContentsMargins(0, 5, 0, 5) + inputs_layout.setSpacing(3) + + self.inputs_widget = inputs_widget + self.inputs_layout = inputs_layout + + layout = QtWidgets.QVBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(0) + + if not label_widget: + label = input_data["label"] + label_widget = QtWidgets.QLabel(label) + layout.addWidget(label_widget) + + self.label_widget = label_widget + + layout.addWidget(inputs_widget) + self.key = input_data["key"] keys = list(parent_keys) keys.append(self.key) @@ -1122,10 +1145,22 @@ class ListSubWidget(QtWidgets.QWidget, ConfigObject): self.override_value = NOT_SET + def count(self): + return len(self.input_fields) + + def reset_value(self): + self.set_value(self.start_value) + + def clear_value(self): + self.set_value([]) + def update_global_values(self, values): old_inputs = tuple(self.input_fields) value = self.value_from_values(values) + + self.global_value = value + if value is not NOT_SET: for item_value in value: self.add_row(value=item_value) @@ -1140,7 +1175,6 @@ class ListSubWidget(QtWidgets.QWidget, ConfigObject): if self.count() == 0: self.add_row(is_empty=True) - self.global_value = value self.start_value = self.item_value() self._is_modified = self.global_value != self.start_value @@ -1158,37 +1192,16 @@ class ListSubWidget(QtWidgets.QWidget, ConfigObject): self.start_value = self.item_value() self._on_value_change() - # TODO use same as InputObject - def apply_overrides(self, parent_values): - if parent_values is NOT_SET or self.key not in parent_values: - override_value = NOT_SET - else: - override_value = parent_values[self.key] - - self.override_value = override_value - - if override_value is NOT_SET: - self._is_overriden = False - self._was_overriden = False - value = self.start_value - else: - self._is_overriden = True - self._was_overriden = True - value = override_value - - self.set_value(value) - - def reset_value(self): - self.set_value(self.start_value) - - def clear_value(self): - self.set_value([]) - def _on_value_change(self, item=None): - self.value_changed.emit(self) + if self.ignore_value_changes: + return + self._is_modified = self.item_value() != self.global_value + if self.is_overidable: + self._is_overriden = True - def count(self): - return len(self.input_fields) + self.update_style() + + self.value_changed.emit(self) def add_row(self, row=None, value=None, is_empty=False): # Create new item @@ -1234,97 +1247,27 @@ class ListSubWidget(QtWidgets.QWidget, ConfigObject): self._on_value_change() self.parent().updateGeometry() - def item_value(self): - output = [] - for item in self.input_fields: - value = item.config_value() - if value is not NOT_SET: - output.append(value) + def apply_overrides(self, parent_values): + if parent_values is NOT_SET or self.key not in parent_values: + override_value = NOT_SET + else: + override_value = parent_values[self.key] - return output + self.override_value = override_value - -class ListWidget(QtWidgets.QWidget, InputObject): - value_changed = QtCore.Signal(object) - - def __init__( - self, input_data, values, parent_keys, parent, label_widget=None - ): - self._parent = parent - - self.is_group = input_data.get("is_group", False) - - self._state = None - - super(ListWidget, self).__init__(parent) - self.setObjectName("ListWidget") - - layout = QtWidgets.QVBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(0) - - if not label_widget: - label = input_data["label"] - label_widget = QtWidgets.QLabel(label) - layout.addWidget(label_widget) - - self.label_widget = label_widget - self.key = input_data["key"] - keys = list(parent_keys) - keys.append(self.key) - self.keys = keys - - self.value_widget = ListSubWidget( - input_data, values, parent_keys, self - ) - self.value_widget.setAttribute(QtCore.Qt.WA_StyledBackground) - - layout.addWidget(self.value_widget) - self.setLayout(layout) - - self.value_widget.value_changed.connect(self._on_value_change) - - def update_global_values(self, values): - self.value_widget.update_global_values(values) - - @property - def start_value(self): - return self.value_widget.start_value - - @property - def global_value(self): - return self.value_widget.global_value - - @property - def override_value(self): - return self.value_widget.override_value - - def _on_value_change(self, item=None): - if self.ignore_value_changes: - return - self._is_modified = self.item_value() != self.global_value - if self.is_overidable: + if override_value is NOT_SET: + self._is_overriden = False + self._was_overriden = False + value = self.start_value + else: self._is_overriden = True + self._was_overriden = True + value = override_value - self.update_style() - - self.value_changed.emit(self) - - def set_value(self, value, *, global_value=False): - self.value_widget.set_value(value, global_value=global_value) - if global_value: - self._on_value_change() - - def reset_value(self): - self.value_widget.reset_value() - - def clear_value(self): - self.value_widget.clear_value() - - def apply_overrides(self, override_value): self._is_modified = False self._state = None - self.value_widget.apply_overrides(override_value) + + self.set_value(value) def update_style(self): state = self.style_state( @@ -1337,7 +1280,12 @@ class ListWidget(QtWidgets.QWidget, InputObject): self.label_widget.style().polish(self.label_widget) def item_value(self): - return self.value_widget.item_value() + output = [] + for item in self.input_fields: + value = item.config_value() + if value is not NOT_SET: + output.append(value) + return output class ModifiableDictItem(QtWidgets.QWidget, ConfigObject): From 0f1dc4271ff5da6d1b67a6a99380edaf2745795e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 31 Aug 2020 19:28:04 +0200 Subject: [PATCH 311/813] list changes cleanup --- .../config_setting/widgets/inputs.py | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index e4a1fba096..6c33ffacc0 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1028,8 +1028,8 @@ class ListItem(QtWidgets.QWidget, ConfigObject): _btn_size = 20 value_changed = QtCore.Signal(object) - def __init__(self, object_type, input_modifiers, parent): - self._parent = parent + def __init__(self, object_type, input_modifiers, config_parent, parent): + self._parent = config_parent super(ListItem, self).__init__(parent) @@ -1076,16 +1076,16 @@ class ListItem(QtWidgets.QWidget, ConfigObject): self.value_changed.emit(self) def row(self): - return self.parent().input_fields.index(self) + return self._parent.input_fields.index(self) def on_add_clicked(self): if self.value_input.isEnabled(): - self.parent().add_row(row=self.row() + 1) + self._parent.add_row(row=self.row() + 1) else: self.set_as_empty(False) def on_remove_clicked(self): - self.parent().remove_row(self) + self._parent.remove_row(self) def config_value(self): if self.value_input.isEnabled(): @@ -1205,16 +1205,18 @@ class ListWidget(QtWidgets.QWidget, InputObject): def add_row(self, row=None, value=None, is_empty=False): # Create new item - item_widget = ListItem(self.object_type, self.input_modifiers, self) + item_widget = ListItem( + self.object_type, self.input_modifiers, self, self.inputs_widget + ) if is_empty: item_widget.set_as_empty() item_widget.value_changed.connect(self._on_value_change) if row is None: - self.layout().addWidget(item_widget) + self.inputs_layout.addWidget(item_widget) self.input_fields.append(item_widget) else: - self.layout().insertWidget(row, item_widget) + self.inputs_layout.insertWidget(row, item_widget) self.input_fields.insert(row, item_widget) previous_input = None @@ -1231,12 +1233,12 @@ class ListWidget(QtWidgets.QWidget, InputObject): item_widget.value_input.set_value(value, global_value=True) else: self._on_value_change() - self.parent().updateGeometry() + self.updateGeometry() def remove_row(self, item_widget): item_widget.value_changed.disconnect() - self.layout().removeWidget(item_widget) + self.inputs_layout.removeWidget(item_widget) self.input_fields.remove(item_widget) item_widget.setParent(None) item_widget.deleteLater() @@ -1245,7 +1247,7 @@ class ListWidget(QtWidgets.QWidget, InputObject): self.add_row(is_empty=True) self._on_value_change() - self.parent().updateGeometry() + self.updateGeometry() def apply_overrides(self, parent_values): if parent_values is NOT_SET or self.key not in parent_values: From 7849c707e1a756a8e6605eb3ad367a46896af6eb Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 31 Aug 2020 19:33:26 +0200 Subject: [PATCH 312/813] added possibility to set key as overriden --- .../config_setting/widgets/inputs.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 6c33ffacc0..208bfa6685 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -169,6 +169,14 @@ class ConfigObject: ) ) + def _set_as_overriden(self): + self.ignore_value_changes = True + self.set_as_overriden() + self.ignore_value_changes = False + + def set_as_overriden(self): + self._is_overriden = True + def hierarchical_style_update(self): raise NotImplementedError( "Method `hierarchical_style_update` not implemented!" @@ -184,6 +192,15 @@ class ConfigObject: actions_mapping[action] = self._discard_changes menu.addAction(action) + if ( + self.is_overidable + and not self.is_overriden + and not self.any_parent_is_group + ): + action = QtWidgets.QAction("Set as overriden") + actions_mapping[action] = self._set_as_overriden + menu.addAction(action) + if ( not self.any_parent_overriden() and (self.is_overriden or self.child_overriden) From 1ab9162b3b94e1d757bb62c3377263d45883504e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 31 Aug 2020 19:55:50 +0200 Subject: [PATCH 313/813] is group is abstract attribute --- .../config_setting/widgets/inputs.py | 32 ++++++++++++------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 208bfa6685..d8121367e3 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -13,10 +13,13 @@ class ConfigObject: allow_actions = True default_state = "" + _is_overriden = False _is_modified = False _was_overriden = False _is_invalid = False + _is_group = False + _log = None @property @@ -45,6 +48,11 @@ class ConfigObject: """Value set in is not valid.""" return self._is_invalid + @property + def is_group(self): + """Value set in is not valid.""" + return self._is_group + @property def is_overidable(self): """Should care about overrides.""" @@ -321,7 +329,7 @@ class BooleanWidget(QtWidgets.QWidget, InputObject): self._parent = parent self._as_widget = values is AS_WIDGET - self.is_group = input_data.get("is_group", False) + self._is_group = input_data.get("is_group", False) self.default_value = input_data.get("default", NOT_SET) self._state = None @@ -436,7 +444,7 @@ class IntegerWidget(QtWidgets.QWidget, InputObject): self._parent = parent self._as_widget = values is AS_WIDGET - self.is_group = input_data.get("is_group", False) + self._is_group = input_data.get("is_group", False) self.default_value = input_data.get("default", NOT_SET) self._state = None @@ -547,7 +555,7 @@ class FloatWidget(QtWidgets.QWidget, InputObject): self._parent = parent self._as_widget = values is AS_WIDGET - self.is_group = input_data.get("is_group", False) + self._is_group = input_data.get("is_group", False) self.default_value = input_data.get("default", NOT_SET) self._state = None @@ -667,7 +675,7 @@ class TextSingleLineWidget(QtWidgets.QWidget, InputObject): self._parent = parent self._as_widget = values is AS_WIDGET - self.is_group = input_data.get("is_group", False) + self._is_group = input_data.get("is_group", False) self.default_value = input_data.get("default", NOT_SET) self._state = None @@ -772,7 +780,7 @@ class TextMultiLineWidget(QtWidgets.QWidget, InputObject): self._parent = parent self._as_widget = values is AS_WIDGET - self.is_group = input_data.get("is_group", False) + self._is_group = input_data.get("is_group", False) self.default_value = input_data.get("default", NOT_SET) self._state = None @@ -929,7 +937,7 @@ class RawJsonWidget(QtWidgets.QWidget, InputObject): self._parent = parent self._as_widget = values is AS_WIDGET - self.is_group = input_data.get("is_group", False) + self._is_group = input_data.get("is_group", False) self.default_value = input_data.get("default", NOT_SET) self._state = None @@ -1122,7 +1130,7 @@ class ListWidget(QtWidgets.QWidget, InputObject): self.setObjectName("ListWidget") self._state = None - self.is_group = input_data.get("is_group", False) + self._is_group = input_data.get("is_group", False) self.object_type = input_data["object_type"] self.default_value = input_data.get("default", NOT_SET) @@ -1572,7 +1580,7 @@ class ModifiableDict(ExpandingWidget, InputObject): self.any_parent_is_group = any_parent_is_group - self.is_group = input_data.get("is_group", False) + self._is_group = input_data.get("is_group", False) self._state = None super(ModifiableDict, self).__init__(input_data["label"], parent) @@ -1656,7 +1664,7 @@ class DictExpandWidget(ExpandingWidget, ConfigObject): self.any_parent_is_group = any_parent_is_group - self.is_group = input_data.get("is_group", False) + self._is_group = input_data.get("is_group", False) self._state = None self._child_state = None @@ -1870,7 +1878,7 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): self.any_parent_is_group = any_parent_is_group - self.is_group = input_data.get("is_group", False) + self._is_group = input_data.get("is_group", False) self._state = None self._child_state = None @@ -2094,7 +2102,7 @@ class DictInvisible(QtWidgets.QWidget, ConfigObject): any_parent_is_group = parent.any_parent_is_group self.any_parent_is_group = any_parent_is_group - self.is_group = input_data.get("is_group", False) + self._is_group = input_data.get("is_group", False) super(DictInvisible, self).__init__(parent) self.setObjectName("DictInvisible") @@ -2270,7 +2278,7 @@ class DictFormWidget(QtWidgets.QWidget, ConfigObject): self.any_parent_is_group = any_parent_is_group - self.is_group = False + self._is_group = False super(DictFormWidget, self).__init__(parent) From 724fe35c84aff0cb00503d4f3fc5f88e0b0766d3 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 31 Aug 2020 19:56:07 +0200 Subject: [PATCH 314/813] set as overriden is abstract method --- .../config_setting/widgets/inputs.py | 51 ++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index d8121367e3..1261f3b91b 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -183,7 +183,9 @@ class ConfigObject: self.ignore_value_changes = False def set_as_overriden(self): - self._is_overriden = True + raise NotImplementedError( + "Method `set_as_overriden` not implemented!" + ) def hierarchical_style_update(self): raise NotImplementedError( @@ -298,6 +300,9 @@ class InputObject(ConfigObject): self._is_modified = False self._is_overriden = self._was_overriden + def set_as_overriden(self): + self._is_overriden = True + @property def child_modified(self): return self.is_modified @@ -1712,6 +1717,17 @@ class DictExpandWidget(ExpandingWidget, ConfigObject): self._is_modified = self.child_modified self._is_overriden = self._was_overriden + def set_as_overriden(self): + if self.is_overriden: + return + + if self.is_group: + self._is_overriden = True + return + + for item in self.input_fields: + item.set_as_overriden() + def update_global_values(self, values): for item in self.input_fields: item.update_global_values(values) @@ -1938,6 +1954,17 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): self._is_modified = self.child_modified self._is_overriden = self._was_overriden + def set_as_overriden(self): + if self.is_overriden: + return + + if self.is_group: + self._is_overriden = True + return + + for item in self.input_fields: + item.set_as_overriden() + def update_global_values(self, values): for item in self.input_fields: item.update_global_values(values) @@ -2209,6 +2236,17 @@ class DictInvisible(QtWidgets.QWidget, ConfigObject): self._is_modified = self.child_modified self._is_overriden = self._was_overriden + def set_as_overriden(self): + if self.is_overriden: + return + + if self.is_group: + self._is_overriden = True + return + + for item in self.input_fields: + item.set_as_overriden() + def update_global_values(self, values): for item in self.input_fields: item.update_global_values(values) @@ -2311,6 +2349,17 @@ class DictFormWidget(QtWidgets.QWidget, ConfigObject): self._is_modified = self.child_modified self._is_overriden = self._was_overriden + def set_as_overriden(self): + if self.is_overriden: + return + + if self.is_group: + self._is_overriden = True + return + + for item in self.input_fields: + item.set_as_overriden() + def update_global_values(self, values): for item in self.input_fields.values(): item.update_global_values(values) From e7a451775611dfe0e14039f2db110e4404a3673d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 31 Aug 2020 20:09:01 +0200 Subject: [PATCH 315/813] fixed determination of overriden keys --- .../tools/config_setting/config_setting/widgets/inputs.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 1261f3b91b..0c35f7d10d 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1821,7 +1821,7 @@ class DictExpandWidget(ExpandingWidget, ConfigObject): @property def child_overriden(self): for input_field in self.input_fields: - if input_field.child_overriden: + if input_field.is_overriden or input_field.child_overriden: return True return False @@ -2059,7 +2059,7 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): @property def child_overriden(self): for input_field in self.input_fields: - if input_field.child_overriden: + if input_field.is_overriden or input_field.child_overriden: return True return False @@ -2162,7 +2162,7 @@ class DictInvisible(QtWidgets.QWidget, ConfigObject): @property def child_overriden(self): for input_field in self.input_fields: - if input_field.child_overriden: + if input_field.is_overriden or input_field.child_overriden: return True return False @@ -2379,7 +2379,7 @@ class DictFormWidget(QtWidgets.QWidget, ConfigObject): @property def child_overriden(self): for input_field in self.input_fields.values(): - if input_field.child_overriden: + if input_field.is_overriden or input_field.child_overriden: return True return False From d7f48119ed3b7966a413872882b5f3fde38cdb13 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 31 Aug 2020 20:09:55 +0200 Subject: [PATCH 316/813] added testing function --- .../config_setting/config_setting/widgets/tests.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/tests.py b/pype/tools/config_setting/config_setting/widgets/tests.py index 16c97a85ef..fc53e38ad5 100644 --- a/pype/tools/config_setting/config_setting/widgets/tests.py +++ b/pype/tools/config_setting/config_setting/widgets/tests.py @@ -1,6 +1,17 @@ from Qt import QtWidgets, QtCore +def indented_print(data, indent=0): + spaces = " " * (indent * 4) + if not isinstance(data, dict): + print("{}{}".format(spaces, data)) + return + + for key, value in data.items(): + print("{}{}".format(spaces, key)) + indented_print(value, indent + 1) + + class SelectableMenu(QtWidgets.QMenu): selection_changed = QtCore.Signal() From 5986041bf0caaa37be01d6b7175adb5bf38b3949 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 31 Aug 2020 20:26:22 +0200 Subject: [PATCH 317/813] get rid of subwidget of modifiable dictionary --- .../config_setting/widgets/inputs.py | 262 ++++++++---------- 1 file changed, 122 insertions(+), 140 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 0c35f7d10d..91882de958 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1324,8 +1324,8 @@ class ModifiableDictItem(QtWidgets.QWidget, ConfigObject): _btn_size = 20 value_changed = QtCore.Signal(object) - def __init__(self, object_type, input_modifiers, parent): - self._parent = parent + def __init__(self, object_type, input_modifiers, config_parent, parent): + self._parent = config_parent super(ModifiableDictItem, self).__init__(parent) @@ -1413,17 +1413,17 @@ class ModifiableDictItem(QtWidgets.QWidget, ConfigObject): self.key_input.style().polish(self.key_input) def row(self): - return self.parent().input_fields.index(self) + return self._parent.input_fields.index(self) def on_add_clicked(self): - self.parent().add_row(row=self.row() + 1) + self._parent.add_row(row=self.row() + 1) def on_remove_clicked(self): if self.is_single: self.value_input.clear_value() self.key_input.setText("") else: - self.parent().remove_row(self) + self._parent.remove_row(self) def config_value(self): key = self.key_input.text() @@ -1433,22 +1433,44 @@ class ModifiableDictItem(QtWidgets.QWidget, ConfigObject): return {key: value} -# TODO Move subwidget to main widget -class ModifiableDictSubWidget(QtWidgets.QWidget, ConfigObject): +class ModifiableDict(ExpandingWidget, InputObject): + # Should be used only for dictionary with one datatype as value + # TODO this is actually input field (do not care if is group or not) value_changed = QtCore.Signal(object) - def __init__(self, input_data, values, parent_keys, parent): + def __init__( + self, input_data, values, parent_keys, parent, + label_widget=None + ): self._parent = parent - super(ModifiableDictSubWidget, self).__init__(parent) - self.setObjectName("ModifiableDictSubWidget") + super(ModifiableDict, self).__init__(input_data["label"], parent) + self.setObjectName("ModifiableDict") - layout = QtWidgets.QVBoxLayout(self) - layout.setContentsMargins(5, 5, 0, 5) - layout.setSpacing(5) - self.setLayout(layout) + self._state = None self.input_fields = [] + + any_parent_is_group = parent.is_group + if not any_parent_is_group: + any_parent_is_group = parent.any_parent_is_group + + self.any_parent_is_group = any_parent_is_group + + self._is_group = input_data.get("is_group", False) + + inputs_widget = QtWidgets.QWidget(self) + inputs_widget.setAttribute(QtCore.Qt.WA_StyledBackground) + + inputs_layout = QtWidgets.QVBoxLayout(inputs_widget) + inputs_layout.setContentsMargins(5, 5, 0, 5) + inputs_layout.setSpacing(5) + + self.set_content_widget(inputs_widget) + + self.inputs_widget = inputs_widget + self.inputs_layout = inputs_layout + self.object_type = input_data["object_type"] self.default_value = input_data.get("default", NOT_SET) self.input_modifiers = input_data.get("input_modifiers") or {} @@ -1458,12 +1480,20 @@ class ModifiableDictSubWidget(QtWidgets.QWidget, ConfigObject): keys.append(self.key) self.keys = keys + self.override_value = NOT_SET + self.update_global_values(values) + def count(self): + return len(self.input_fields) + def update_global_values(self, values): old_inputs = tuple(self.input_fields) value = self.value_from_values(values) + + self.global_value = value + if value is not NOT_SET: for item_key, item_value in value.items(): self.add_row(key=item_key, value=item_value) @@ -1478,137 +1508,22 @@ class ModifiableDictSubWidget(QtWidgets.QWidget, ConfigObject): for old_input in old_inputs: self.remove_row(old_input) - self.global_value = value - self.start_value = self.config_value() + self.start_value = self.item_value() self._is_modified = self.global_value != self.start_value - @property - def is_group(self): - return self._parent.is_group + def set_value(self, value, *, global_value=False): + previous_inputs = tuple(self.input_fields) + for item_key, item_value in value.items(): + self.add_row(key=item_key, value=item_value) - @property - def any_parent_is_group(self): - return self._parent.any_parent_is_group + for input_field in previous_inputs: + self.remove_row(input_field) - def _on_value_change(self, item=None): - self.value_changed.emit(self) - - def count(self): - return len(self.input_fields) - - def add_row(self, row=None, key=None, value=None): - # Create new item - item_widget = ModifiableDictItem( - self.object_type, self.input_modifiers, self - ) - - # Set/unset if new item is single item - current_count = self.count() - if current_count == 0: - item_widget.is_single = True - elif current_count == 1: - for _input_field in self.input_fields: - _input_field.is_single = False - - item_widget.value_changed.connect(self._on_value_change) - - if row is None: - self.layout().addWidget(item_widget) - self.input_fields.append(item_widget) - else: - self.layout().insertWidget(row, item_widget) - self.input_fields.insert(row, item_widget) - - previous_input = None - for input_field in self.input_fields: - if previous_input is not None: - self.setTabOrder( - previous_input, input_field.key_input - ) - previous_input = input_field.value_input.focusProxy() - self.setTabOrder( - input_field.key_input, previous_input - ) - - # Set value if entered value is not None - # else (when add button clicked) trigger `_on_value_change` - if value is not None and key is not None: - item_widget.default_key = key - item_widget.key_input.setText(key) - item_widget.value_input.set_value(value, global_value=True) - else: + if global_value: + self.global_value = value + self.start_value = self.item_value() self._on_value_change() - self.parent().updateGeometry() - - def remove_row(self, item_widget): - item_widget.value_changed.disconnect() - - self.layout().removeWidget(item_widget) - self.input_fields.remove(item_widget) - item_widget.setParent(None) - item_widget.deleteLater() - - current_count = self.count() - if current_count == 0: - self.add_row() - elif current_count == 1: - for _input_field in self.input_fields: - _input_field.is_single = True - - self._on_value_change() - self.parent().updateGeometry() - - def config_value(self): - output = {} - for item in self.input_fields: - item_value = item.config_value() - if item_value: - output.update(item_value) - return output - - -class ModifiableDict(ExpandingWidget, InputObject): - # Should be used only for dictionary with one datatype as value - # TODO this is actually input field (do not care if is group or not) - value_changed = QtCore.Signal(object) - - def __init__( - self, input_data, values, parent_keys, parent, - label_widget=None - ): - self._parent = parent - - any_parent_is_group = parent.is_group - if not any_parent_is_group: - any_parent_is_group = parent.any_parent_is_group - - self.any_parent_is_group = any_parent_is_group - - self._is_group = input_data.get("is_group", False) - self._state = None - - super(ModifiableDict, self).__init__(input_data["label"], parent) - self.setObjectName("ModifiableDict") - - self.value_widget = ModifiableDictSubWidget( - input_data, values, parent_keys, self - ) - self.value_widget.setAttribute(QtCore.Qt.WA_StyledBackground) - self.value_widget.value_changed.connect(self._on_value_change) - - self.set_content_widget(self.value_widget) - - self.key = input_data["key"] - - self.override_value = NOT_SET - self.start_value = self.value_widget.start_value - self.global_value = self.value_widget.global_value - - self._is_modified = self.global_value != self.start_value - - def update_global_values(self, values): - self.value_widget.update_global_values(values) def _on_value_change(self, item=None): if self.ignore_value_changes: @@ -1647,7 +1562,74 @@ class ModifiableDict(ExpandingWidget, InputObject): self._state = state def item_value(self): - return self.value_widget.config_value() + output = {} + for item in self.input_fields: + item_value = item.config_value() + if item_value: + output.update(item_value) + return output + + def add_row(self, row=None, key=None, value=None): + # Create new item + item_widget = ModifiableDictItem( + self.object_type, self.input_modifiers, self, self.inputs_widget + ) + + # Set/unset if new item is single item + current_count = self.count() + if current_count == 0: + item_widget.is_single = True + elif current_count == 1: + for _input_field in self.input_fields: + _input_field.is_single = False + + item_widget.value_changed.connect(self._on_value_change) + + if row is None: + self.inputs_layout.addWidget(item_widget) + self.input_fields.append(item_widget) + else: + self.inputs_layout.insertWidget(row, item_widget) + self.input_fields.insert(row, item_widget) + + previous_input = None + for input_field in self.input_fields: + if previous_input is not None: + self.setTabOrder( + previous_input, input_field.key_input + ) + previous_input = input_field.value_input.focusProxy() + self.setTabOrder( + input_field.key_input, previous_input + ) + + # Set value if entered value is not None + # else (when add button clicked) trigger `_on_value_change` + if value is not None and key is not None: + item_widget.default_key = key + item_widget.key_input.setText(key) + item_widget.value_input.set_value(value, global_value=True) + else: + self._on_value_change() + self.parent().updateGeometry() + + def remove_row(self, item_widget): + item_widget.value_changed.disconnect() + + self.inputs_layout.removeWidget(item_widget) + self.input_fields.remove(item_widget) + item_widget.setParent(None) + item_widget.deleteLater() + + current_count = self.count() + if current_count == 0: + self.add_row() + elif current_count == 1: + for _input_field in self.input_fields: + _input_field.is_single = True + + self._on_value_change() + self.parent().updateGeometry() # Dictionaries From 416de13fe270b4512609c4da58a48a212881ee06 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 31 Aug 2020 20:34:33 +0200 Subject: [PATCH 318/813] fixed form group for overrides --- .../config_setting/config_setting/widgets/inputs.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 91882de958..873481e352 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -2331,6 +2331,13 @@ class DictFormWidget(QtWidgets.QWidget, ConfigObject): self._is_modified = self.child_modified self._is_overriden = self._was_overriden + def remove_overrides(self): + self._is_overriden = False + self._is_modified = False + self._was_overriden = False + for item in self.input_fields.values(): + item.remove_overrides() + def set_as_overriden(self): if self.is_overriden: return @@ -2350,6 +2357,8 @@ class DictFormWidget(QtWidgets.QWidget, ConfigObject): if self.ignore_value_changes: return self.value_changed.emit(self) + if self.any_parent_is_group: + self.hierarchical_style_update() @property def child_modified(self): @@ -2419,7 +2428,7 @@ class DictFormWidget(QtWidgets.QWidget, ConfigObject): values = {} groups = [] - for input_field in self.input_fields: + for input_field in self.input_fields.values(): value, is_group = input_field.overrides() if value is not NOT_SET: values.update(value) @@ -2427,7 +2436,7 @@ class DictFormWidget(QtWidgets.QWidget, ConfigObject): groups.extend(value.keys()) if groups: values[METADATA_KEY] = {"groups": groups} - return {self.key: values}, self.is_group + return values, self.is_group TypeToKlass.types["boolean"] = BooleanWidget From 008204f7d9b3a30f77392b17da2d38aacb9a9f43 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 31 Aug 2020 20:35:14 +0200 Subject: [PATCH 319/813] added form widget to overridable configurations --- .../projects_schema/1_plugins_gui_schema.json | 73 ++++++++++--------- 1 file changed, 39 insertions(+), 34 deletions(-) diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json index c70daab32c..bed839e6dc 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json @@ -21,40 +21,45 @@ "is_group": true, "children": [ { - "type": "boolean", - "key": "enabled", - "label": "Enabled", - "default": true - }, { - "type": "text-singleline", - "key": "deadline_department", - "label": "Deadline apartment", - "default": "" - }, { - "type": "int", - "key": "deadline_priority", - "label": "Deadline priority", - "default": 50 - }, { - "type": "text-singleline", - "key": "deadline_pool", - "label": "Deadline pool", - "default": "" - }, { - "type": "text-singleline", - "key": "deadline_pool_secondary", - "label": "Deadline pool (secondary)", - "default": "" - }, { - "type": "text-singleline", - "key": "deadline_group", - "label": "Deadline Group", - "default": "" - }, { - "type": "int", - "key": "deadline_chunk_size", - "label": "Deadline Chunk size", - "default": 10 + "type": "dict-form", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled", + "default": true + }, { + "type": "text-singleline", + "key": "deadline_department", + "label": "Deadline apartment", + "default": "" + }, { + "type": "int", + "key": "deadline_priority", + "label": "Deadline priority", + "default": 50 + }, { + "type": "text-singleline", + "key": "deadline_pool", + "label": "Deadline pool", + "default": "" + }, { + "type": "text-singleline", + "key": "deadline_pool_secondary", + "label": "Deadline pool (secondary)", + "default": "" + }, { + "type": "text-singleline", + "key": "deadline_group", + "label": "Deadline Group", + "default": "" + }, { + "type": "int", + "key": "deadline_chunk_size", + "label": "Deadline Chunk size", + "default": 10 + } + ] } ] } From 2e1dbec431df446c941b297694fe695e3643443f Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 1 Sep 2020 09:45:51 +0200 Subject: [PATCH 320/813] added any_parent_is_group to RawJson --- pype/tools/config_setting/config_setting/widgets/inputs.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 873481e352..82befa3276 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -942,6 +942,12 @@ class RawJsonWidget(QtWidgets.QWidget, InputObject): self._parent = parent self._as_widget = values is AS_WIDGET + any_parent_is_group = parent.is_group + if not any_parent_is_group: + any_parent_is_group = parent.any_parent_is_group + + self.any_parent_is_group = any_parent_is_group + self._is_group = input_data.get("is_group", False) self.default_value = input_data.get("default", NOT_SET) From 1cafdb0aa89e0fd8c6fcb1deadd53ecb1afad0f9 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 1 Sep 2020 09:46:10 +0200 Subject: [PATCH 321/813] input_fields in FormWidget are in list not dict --- .../config_setting/widgets/inputs.py | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 82befa3276..8c9e4a054b 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -2308,7 +2308,7 @@ class DictFormWidget(QtWidgets.QWidget, ConfigObject): super(DictFormWidget, self).__init__(parent) - self.input_fields = {} + self.input_fields = [] self.content_layout = QtWidgets.QFormLayout(self) self.keys = list(parent_keys) @@ -2327,11 +2327,11 @@ class DictFormWidget(QtWidgets.QWidget, ConfigObject): super(DictFormWidget, self).mouseReleaseEvent(event) def apply_overrides(self, parent_values): - for item in self.input_fields.values(): + for item in self.input_fields: item.apply_overrides(parent_values) def discard_changes(self): - for item in self.input_fields.values(): + for item in self.input_fields: item.discard_changes() self._is_modified = self.child_modified @@ -2341,7 +2341,7 @@ class DictFormWidget(QtWidgets.QWidget, ConfigObject): self._is_overriden = False self._is_modified = False self._was_overriden = False - for item in self.input_fields.values(): + for item in self.input_fields: item.remove_overrides() def set_as_overriden(self): @@ -2356,7 +2356,7 @@ class DictFormWidget(QtWidgets.QWidget, ConfigObject): item.set_as_overriden() def update_global_values(self, values): - for item in self.input_fields.values(): + for item in self.input_fields: item.update_global_values(values) def _on_value_change(self, item=None): @@ -2368,34 +2368,33 @@ class DictFormWidget(QtWidgets.QWidget, ConfigObject): @property def child_modified(self): - for input_field in self.input_fields.values(): + for input_field in self.input_fields: if input_field.child_modified: return True return False @property def child_overriden(self): - for input_field in self.input_fields.values(): + for input_field in self.input_fields: if input_field.is_overriden or input_field.child_overriden: return True return False @property def child_invalid(self): - for input_field in self.input_fields.values(): + for input_field in self.input_fields: if input_field.child_invalid: return True return False def get_invalid(self): output = [] - for input_field in self.input_fields.values(): + for input_field in self.input_fields: output.extend(input_field.get_invalid()) return output def add_children_gui(self, child_configuration, values): item_type = child_configuration["type"] - key = child_configuration["key"] # Pop label to not be set in child label = child_configuration["label"] @@ -2410,16 +2409,16 @@ class DictFormWidget(QtWidgets.QWidget, ConfigObject): item.value_changed.connect(self._on_value_change) self.content_layout.addRow(label_widget, item) - self.input_fields[key] = item + self.input_fields.append(item) return item def hierarchical_style_update(self): - for input_field in self.input_fields.values(): + for input_field in self.input_fields: input_field.hierarchical_style_update() def item_value(self): output = {} - for input_field in self.input_fields.values(): + for input_field in self.input_fields: # TODO maybe merge instead of update should be used # NOTE merge is custom function which merges 2 dicts output.update(input_field.config_value()) @@ -2434,7 +2433,7 @@ class DictFormWidget(QtWidgets.QWidget, ConfigObject): values = {} groups = [] - for input_field in self.input_fields.values(): + for input_field in self.input_fields: value, is_group = input_field.overrides() if value is not NOT_SET: values.update(value) From bfc4a7d595bd152a67a585a8c23e6fa3b629fdfd Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 1 Sep 2020 10:10:59 +0200 Subject: [PATCH 322/813] DictWidget can be also expandable --- .../config_setting/widgets/inputs.py | 85 +++++++++++-------- 1 file changed, 50 insertions(+), 35 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 8c9e4a054b..8e66a5b1c2 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1874,50 +1874,22 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): raise TypeError("Can't use \"{}\" as widget item.".format( self.__class__.__name__ )) + + super(DictWidget, self).__init__(parent) + self.setObjectName("DictWidget") + + self._state = None + self._child_state = None + self._parent = parent any_parent_is_group = parent.is_group if not any_parent_is_group: any_parent_is_group = parent.any_parent_is_group - self.any_parent_is_group = any_parent_is_group self._is_group = input_data.get("is_group", False) - self._state = None - self._child_state = None - - super(DictWidget, self).__init__(parent) - self.setObjectName("DictWidget") - - body_widget = QtWidgets.QWidget(self) - - label_widget = QtWidgets.QLabel( - input_data["label"], parent=body_widget - ) - label_widget.setObjectName("DictLabel") - - content_widget = QtWidgets.QWidget(body_widget) - content_layout = QtWidgets.QVBoxLayout(content_widget) - content_layout.setContentsMargins(3, 3, 0, 3) - - body_layout = QtWidgets.QVBoxLayout(body_widget) - body_layout.setContentsMargins(0, 0, 0, 0) - body_layout.setSpacing(5) - body_layout.addWidget(label_widget) - body_layout.addWidget(content_widget) - - self.setAttribute(QtCore.Qt.WA_StyledBackground) - - main_layout = QtWidgets.QHBoxLayout(self) - main_layout.setContentsMargins(5, 5, 0, 5) - main_layout.setSpacing(0) - main_layout.addWidget(body_widget) - - self.label_widget = label_widget - self.content_widget = content_widget - self.content_layout = content_layout - self.input_fields = [] self.key = input_data["key"] @@ -1925,6 +1897,49 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): keys.append(self.key) self.keys = keys + main_layout = QtWidgets.QHBoxLayout(self) + main_layout.setSpacing(0) + + expandable = input_data.get("expandable", True) + if expandable: + main_layout.setContentsMargins(0, 0, 0, 0) + body_widget = ExpandingWidget(input_data["label"], self) + else: + main_layout.setContentsMargins(5, 5, 0, 5) + body_widget = QtWidgets.QWidget(self) + + main_layout.addWidget(body_widget) + + content_widget = QtWidgets.QWidget(body_widget) + content_layout = QtWidgets.QVBoxLayout(content_widget) + content_layout.setContentsMargins(3, 3, 0, 3) + + self.content_widget = content_widget + self.content_layout = content_layout + + if not expandable: + label_widget = QtWidgets.QLabel( + input_data["label"], parent=body_widget + ) + label_widget.setObjectName("DictLabel") + + body_layout = QtWidgets.QVBoxLayout(body_widget) + body_layout.setContentsMargins(0, 0, 0, 0) + body_layout.setSpacing(5) + body_layout.addWidget(label_widget) + body_layout.addWidget(content_widget) + + self.label_widget = label_widget + + else: + body_widget.set_content_widget(content_widget) + self.label_widget = body_widget.label_widget + expanded = input_data.get("expanded", False) + if expanded: + self.toggle_content() + + self.setAttribute(QtCore.Qt.WA_StyledBackground) + for child_data in input_data.get("children", []): self.add_children_gui(child_data, values) From cedb8dd2d78650806058f09a5b9c8db51ebc15aa Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 1 Sep 2020 10:11:22 +0200 Subject: [PATCH 323/813] added expandable false to current dict types --- .../projects_schema/1_plugins_gui_schema.json | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json index bed839e6dc..4b7b19a74d 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json @@ -16,6 +16,7 @@ "children": [ { "type": "dict", + "expandable": false, "key": "ExtractCelactionDeadline", "label": "ExtractCelactionDeadline", "is_group": true, @@ -79,6 +80,7 @@ "children": [ { "type": "dict", + "expandable": false, "key": "IntegrateFtrackNote", "label": "IntegrateFtrackNote", "is_group": true, @@ -118,6 +120,7 @@ "children": [ { "type": "dict", + "expandable": false, "key": "IntegrateMasterVersion", "label": "IntegrateMasterVersion", "is_group": true, @@ -130,6 +133,7 @@ ] }, { "type": "dict", + "expandable": false, "key": "ExtractJpegEXR", "label": "ExtractJpegEXR", "is_group": true, @@ -156,6 +160,7 @@ ] }, { "type": "dict", + "expandable": false, "key": "ExtractReview", "label": "ExtractReview", "is_group": true, @@ -174,6 +179,7 @@ ] }, { "type": "dict", + "expandable": false, "key": "ExtractBurnin", "label": "ExtractBurnin", "is_group": true, @@ -227,6 +233,7 @@ ] }, { "type": "dict", + "expandable": false, "key": "IntegrateAssetNew", "label": "IntegrateAssetNew", "is_group": true, @@ -239,6 +246,7 @@ ] }, { "type": "dict", + "expandable": false, "key": "ProcessSubmittedJobOnFarm", "label": "ProcessSubmittedJobOnFarm", "is_group": true, @@ -278,6 +286,7 @@ "children": [ { "type": "dict", + "expandable": false, "key": "ValidateModelName", "label": "Validate Model Name", "is_group": true, @@ -299,6 +308,7 @@ ] }, { "type": "dict", + "expandable": false, "key": "ValidateAssemblyName", "label": "Validate Assembly Name", "is_group": true, @@ -311,6 +321,7 @@ ] }, { "type": "dict", + "expandable": false, "key": "ValidateShaderName", "label": "ValidateShaderName", "is_group": true, @@ -328,6 +339,7 @@ ] }, { "type": "dict", + "expandable": false, "key": "ValidateMeshHasOverlappingUVs", "label": "ValidateMeshHasOverlappingUVs", "is_group": true, @@ -360,6 +372,7 @@ "children": [ { "type": "dict", + "expandable": false, "key": "CreateWriteRender", "label": "CreateWriteRender", "is_group": true, @@ -373,6 +386,7 @@ ] }, { "type": "dict", + "expandable": false, "key": "CreateWritePrerender", "label": "CreateWritePrerender", "is_group": true, @@ -394,6 +408,7 @@ "children": [ { "type": "dict", + "expandable": false, "key": "ExtractThumbnail", "label": "ExtractThumbnail", "is_group": true, @@ -411,6 +426,7 @@ ] }, { "type": "dict", + "expandable": false, "key": "ValidateNukeWriteKnobs", "label": "ValidateNukeWriteKnobs", "is_group": true, @@ -428,6 +444,7 @@ ] }, { "type": "dict", + "expandable": false, "key": "ExtractReviewDataLut", "label": "ExtractReviewDataLut", "is_group": true, @@ -441,6 +458,7 @@ ] }, { "type": "dict", + "expandable": false, "key": "ExtractReviewDataMov", "label": "ExtractReviewDataMov", "is_group": true, @@ -459,6 +477,7 @@ ] }, { "type": "dict", + "expandable": false, "key": "ExtractSlateFrame", "label": "ExtractSlateFrame", "is_group": true, @@ -472,6 +491,7 @@ ] }, { "type": "dict", + "expandable": false, "key": "NukeSubmitDeadline", "label": "NukeSubmitDeadline", "is_group": true, @@ -520,6 +540,7 @@ "children": [ { "type": "dict", + "expandable": false, "key": "CollectInstanceVersion", "label": "Collect Instance Version", "is_group": true, @@ -533,6 +554,7 @@ ] }, { "type": "dict", + "expandable": false, "key": "ExtractReviewCutUpVideo", "label": "Extract Review Cut Up Video", "is_group": true, @@ -567,6 +589,7 @@ "children": [ { "type": "dict", + "expandable": false, "key": "CreateShotClip", "label": "Create Shot Clip", "is_group": true, From 32ac3a77d7afd073e26b1b1fac94f6444c8754c6 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 1 Sep 2020 10:16:23 +0200 Subject: [PATCH 324/813] extended modified attribute --- pype/tools/config_setting/config_setting/widgets/inputs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 8e66a5b1c2..4a23b93a85 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -2049,7 +2049,7 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): @property def is_modified(self): if self.is_group: - return self.child_modified + return self._is_modified or self.child_modified return False @property From 00b5ac261a6379f529de684de040c79858416287 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 1 Sep 2020 10:19:49 +0200 Subject: [PATCH 325/813] removed dict-expanding input type as is duplicated --- .../1_ftrack_projects_gui_schema.json | 9 +- .../projects_schema/1_plugins_gui_schema.json | 57 +++-- .../1_applications_gui_schema.json | 3 +- .../studio_schema/1_intents_gui_schema.json | 3 +- .../studio_schema/1_tools_gui_schema.json | 3 +- .../studio_schema/1_tray_items.json | 13 +- .../config_setting/widgets/inputs.py | 226 ------------------ 7 files changed, 58 insertions(+), 256 deletions(-) diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_ftrack_projects_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_ftrack_projects_gui_schema.json index ac696d18d0..51c05aeb3a 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_ftrack_projects_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_ftrack_projects_gui_schema.json @@ -1,11 +1,13 @@ { "key": "ftrack", - "type": "dict-expanding", + "type": "dict", + "expandable": true, "label": "Ftrack", "children": [ { "key": "status_update", - "type": "dict-expanding", + "type": "dict", + "expandable": true, "label": "Status updates", "is_group": true, "is_file": true, @@ -22,7 +24,8 @@ ] }, { "key": "status_version_to_task", - "type": "dict-expanding", + "type": "dict", + "expandable": true, "label": "Version status to Task status", "is_group": true, "is_file": true, diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json index 4b7b19a74d..bb323c9803 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json @@ -1,15 +1,18 @@ { - "type": "dict-expanding", + "type": "dict", + "expandable": true, "key": "plugins", "label": "Plugins", "children": [ { - "type": "dict-expanding", + "type": "dict", + "expandable": true, "key": "celaction", "label": "CelAction", "children": [ { - "type": "dict-expanding", + "type": "dict", + "expandable": true, "key": "publish", "label": "Publish plugins", "is_file": true, @@ -68,12 +71,14 @@ } ] }, { - "type": "dict-expanding", + "type": "dict", + "expandable": true, "key": "ftrack", "label": "Ftrack", "children": [ { - "type": "dict-expanding", + "type": "dict", + "expandable": true, "key": "publish", "label": "Publish plugins", "is_file": true, @@ -108,12 +113,14 @@ } ] }, { - "type": "dict-expanding", + "type": "dict", + "expandable": true, "key": "global", "label": "Global", "children": [ { - "type": "dict-expanding", + "type": "dict", + "expandable": true, "key": "publish", "label": "Publish plugins", "is_file": true, @@ -189,7 +196,8 @@ "key": "enabled", "label": "Enabled" }, { - "type": "dict-expanding", + "type": "dict", + "expandable": true, "key": "options", "label": "Burnin formating options", "children": [ @@ -274,12 +282,14 @@ } ] }, { - "type": "dict-expanding", + "type": "dict", + "expandable": true, "key": "maya", "label": "Maya", "children": [ { - "type": "dict-expanding", + "type": "dict", + "expandable": true, "key": "publish", "label": "Publish plugins", "is_file": true, @@ -360,12 +370,14 @@ } ] }, { - "type": "dict-expanding", + "type": "dict", + "expandable": true, "key": "nuke", "label": "Nuke", "children": [ { - "type": "dict-expanding", + "type": "dict", + "expandable": true, "key": "create", "label": "Create plugins", "is_file": true, @@ -401,7 +413,8 @@ } ] }, { - "type": "dict-expanding", + "type": "dict", + "expandable": true, "key": "publish", "label": "Publish plugins", "is_file": true, @@ -528,12 +541,14 @@ } ] }, { - "type": "dict-expanding", + "type": "dict", + "expandable": true, "key": "nukestudio", "label": "NukeStudio", "children": [ { - "type": "dict-expanding", + "type": "dict", + "expandable": true, "key": "publish", "label": "Publish plugins", "is_file": true, @@ -577,12 +592,14 @@ } ] }, { - "type": "dict-expanding", + "type": "dict", + "expandable": true, "key": "resolve", "label": "DaVinci Resolve", "children": [ { - "type": "dict-expanding", + "type": "dict", + "expandable": true, "key": "create", "label": "Creator plugins", "is_file": true, @@ -617,12 +634,14 @@ } ] }, { - "type": "dict-expanding", + "type": "dict", + "expandable": true, "key": "standalonepublisher", "label": "StandalonePublisher", "children": [ { - "type": "dict-expanding", + "type": "dict", + "expandable": true, "key": "publish", "label": "Publish plugins", "is_file": true, diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_applications_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_applications_gui_schema.json index 2e60ed360d..bc2c9f239c 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_applications_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_applications_gui_schema.json @@ -1,7 +1,8 @@ { "key": "applications", - "type": "dict-expanding", + "type": "dict", "label": "Applications", + "expandable": true, "is_file": true, "is_group": true, "children": [ diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_intents_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_intents_gui_schema.json index 37d525cfb6..18f3d42ba7 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_intents_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_intents_gui_schema.json @@ -1,7 +1,8 @@ { "key": "intent", - "type": "dict-expanding", + "type": "dict", "label": "Intent Setting", + "expandable": true, "is_group": true, "is_file": true, "children": [ diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_tools_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_tools_gui_schema.json index 4c905a3826..bf35326d1c 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_tools_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_tools_gui_schema.json @@ -1,7 +1,8 @@ { "key": "tools", - "type": "dict-expanding", + "type": "dict", "label": "Tools", + "expandable": true, "is_group": true, "is_file": true, "children": [ diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_tray_items.json b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_tray_items.json index 849019c89e..7cea724f29 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_tray_items.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_tray_items.json @@ -1,7 +1,8 @@ { "key": "tray_modules", - "type": "dict-expanding", + "type": "dict", "label": "Modules", + "expandable": true, "is_file": true, "is_group": true, "children": [ @@ -65,10 +66,10 @@ "type": "dict-invisible", "children": [ { - "type": "dict-expanding", + "type": "dict", "key": "Rest Api", "label": "Rest Api", - "MISINGKEYCONF": {"exclude_ports": []}, + "expandable": true, "children": [ { "type": "int", @@ -88,9 +89,10 @@ } ] }, { - "type": "dict-expanding", + "type": "dict", "key": "Timers Manager", "label": "Timers Manager", + "expandable": true, "children": [ { "type": "float", @@ -103,9 +105,10 @@ } ] }, { - "type": "dict-expanding", + "type": "dict", "key": "Clockify", "label": "Clockify", + "expandable": true, "children": [ { "type": "text-singleline", diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 4a23b93a85..f0feb6fce3 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1639,231 +1639,6 @@ class ModifiableDict(ExpandingWidget, InputObject): # Dictionaries -class DictExpandWidget(ExpandingWidget, ConfigObject): - value_changed = QtCore.Signal(object) - - def __init__( - self, input_data, values, parent_keys, parent, label_widget=None - ): - if values is AS_WIDGET: - raise TypeError("Can't use \"{}\" as widget item.".format( - self.__class__.__name__ - )) - self._parent = parent - - any_parent_is_group = parent.is_group - if not any_parent_is_group: - any_parent_is_group = parent.any_parent_is_group - - self.any_parent_is_group = any_parent_is_group - - self._is_group = input_data.get("is_group", False) - - self._state = None - self._child_state = None - - super(DictExpandWidget, self).__init__(input_data["label"], parent) - - content_widget = QtWidgets.QWidget(self) - content_widget.setVisible(False) - - content_layout = QtWidgets.QVBoxLayout(content_widget) - content_layout.setContentsMargins(3, 3, 0, 3) - - self.set_content_widget(content_widget) - - expanded = input_data.get("expanded", False) - if expanded: - self.toggle_content() - - self.setAttribute(QtCore.Qt.WA_StyledBackground) - - self.content_widget = content_widget - self.content_layout = content_layout - - self.input_fields = [] - - self.key = input_data["key"] - keys = list(parent_keys) - keys.append(self.key) - self.keys = keys - - for child_data in input_data.get("children", []): - self.add_children_gui(child_data, values) - - def remove_overrides(self): - self._is_overriden = False - self._is_modified = False - self._was_overriden = False - for item in self.input_fields: - item.remove_overrides() - - def discard_changes(self): - for item in self.input_fields: - item.discard_changes() - - self._is_modified = self.child_modified - self._is_overriden = self._was_overriden - - def set_as_overriden(self): - if self.is_overriden: - return - - if self.is_group: - self._is_overriden = True - return - - for item in self.input_fields: - item.set_as_overriden() - - def update_global_values(self, values): - for item in self.input_fields: - item.update_global_values(values) - - def apply_overrides(self, parent_values): - # Make sure this is set to False - self._state = None - self._child_state = None - - metadata = {} - groups = tuple() - override_values = NOT_SET - if parent_values is not NOT_SET: - metadata = parent_values.get(METADATA_KEY) or metadata - groups = metadata.get("groups") or groups - override_values = parent_values.get(self.key, override_values) - - self._is_overriden = self.key in groups - - for item in self.input_fields: - item.apply_overrides(override_values) - - if not self._is_overriden: - self._is_overriden = ( - self.is_group - and self.is_overidable - and self.child_overriden - ) - self._was_overriden = bool(self._is_overriden) - - def _on_value_change(self, item=None): - if self.ignore_value_changes: - return - - if self.is_group: - if self.is_overidable: - self._is_overriden = True - - # TODO update items - if item is not None: - for _item in self.input_fields: - if _item is not item: - _item.update_style() - - self.value_changed.emit(self) - - self.update_style() - - def hierarchical_style_update(self): - self.update_style() - for input_field in self.input_fields: - input_field.hierarchical_style_update() - - def update_style(self, is_overriden=None): - child_modified = self.child_modified - child_state = self.style_state( - self.child_invalid, self.child_overriden, child_modified - ) - if child_state: - child_state = "child-{}".format(child_state) - - if child_state != self._child_state: - self.setProperty("state", child_state) - self.style().polish(self) - self._child_state = child_state - - state = self.style_state( - self.is_invalid, self.is_overriden, self.is_modified - ) - if self._state == state: - return - - self.label_widget.setProperty("state", state) - self.label_widget.style().polish(self.label_widget) - - self._state = state - - @property - def is_modified(self): - if self.is_group: - return self.child_modified - return False - - @property - def child_modified(self): - for input_field in self.input_fields: - if input_field.child_modified: - return True - return False - - @property - def child_overriden(self): - for input_field in self.input_fields: - if input_field.is_overriden or input_field.child_overriden: - return True - return False - - @property - def child_invalid(self): - for input_field in self.input_fields: - if input_field.child_invalid: - return True - return False - - def get_invalid(self): - output = [] - for input_field in self.input_fields: - output.extend(input_field.get_invalid()) - return output - - def item_value(self): - output = {} - for input_field in self.input_fields: - # TODO maybe merge instead of update should be used - # NOTE merge is custom function which merges 2 dicts - output.update(input_field.config_value()) - return output - - def add_children_gui(self, child_configuration, values): - item_type = child_configuration["type"] - klass = TypeToKlass.types.get(item_type) - - item = klass( - child_configuration, values, self.keys, self - ) - item.value_changed.connect(self._on_value_change) - self.content_layout.addWidget(item) - - self.input_fields.append(item) - return item - - 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 - - class DictWidget(QtWidgets.QWidget, ConfigObject): value_changed = QtCore.Signal(object) @@ -2467,7 +2242,6 @@ TypeToKlass.types["int"] = IntegerWidget TypeToKlass.types["float"] = FloatWidget TypeToKlass.types["dict-modifiable"] = ModifiableDict TypeToKlass.types["dict"] = DictWidget -TypeToKlass.types["dict-expanding"] = DictExpandWidget TypeToKlass.types["dict-form"] = DictFormWidget TypeToKlass.types["dict-invisible"] = DictInvisible TypeToKlass.types["list"] = ListWidget From 8f4a7138e5104cb6b89e03678ad321e121df5b12 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 1 Sep 2020 11:26:44 +0200 Subject: [PATCH 326/813] form widget has update_style method --- pype/tools/config_setting/config_setting/widgets/inputs.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index f0feb6fce3..dd1af3eb9d 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -2127,6 +2127,10 @@ class DictFormWidget(QtWidgets.QWidget, ConfigObject): self._is_modified = self.child_modified self._is_overriden = self._was_overriden + def update_style(self): + for item in self.input_fields: + item.update_style() + def remove_overrides(self): self._is_overriden = False self._is_modified = False From c954fafeb0e4a9fbaafe2c416cd7971a1dd1ac6c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 1 Sep 2020 11:29:10 +0200 Subject: [PATCH 327/813] trigger hierarchical_style_update instead of triggering update_style for each item --- pype/tools/config_setting/config_setting/widgets/inputs.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index dd1af3eb9d..9896fda4e1 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1986,12 +1986,7 @@ class DictInvisible(QtWidgets.QWidget, ConfigObject): if self.is_group: if self.is_overidable: self._is_overriden = True - # TODO update items - if item is not None: - is_overriden = self.is_overriden - for _item in self.input_fields: - if _item is not item: - _item.update_style(is_overriden) + self.hierarchical_style_update() self.value_changed.emit(self) From 5393b79d987c81bd51f4e3c3d8c4348b4546b062 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 1 Sep 2020 11:31:24 +0200 Subject: [PATCH 328/813] label widget in dictionary widget is wrapped with QWidget --- .../config_setting/widgets/inputs.py | 39 +++++++++++-------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 9896fda4e1..f6fc0310aa 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1692,26 +1692,33 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): self.content_widget = content_widget self.content_layout = content_layout - if not expandable: - label_widget = QtWidgets.QLabel( - input_data["label"], parent=body_widget - ) - label_widget.setObjectName("DictLabel") - - body_layout = QtWidgets.QVBoxLayout(body_widget) - body_layout.setContentsMargins(0, 0, 0, 0) - body_layout.setSpacing(5) - body_layout.addWidget(label_widget) - body_layout.addWidget(content_widget) - - self.label_widget = label_widget - - else: + if expandable: body_widget.set_content_widget(content_widget) self.label_widget = body_widget.label_widget expanded = input_data.get("expanded", False) if expanded: - self.toggle_content() + body_widget.toggle_content() + + else: + top_widget = QtWidgets.QWidget(body_widget) + top_layout = QtWidgets.QHBoxLayout(top_widget) + top_layout.setContentsMargins(0, 0, 0, 0) + top_layout.setSpacing(5) + + label_widget = QtWidgets.QLabel( + input_data["label"], parent=top_widget + ) + label_widget.setObjectName("DictLabel") + + top_layout.addWidget(label_widget) + + body_layout = QtWidgets.QVBoxLayout(body_widget) + body_layout.setContentsMargins(0, 0, 0, 0) + body_layout.setSpacing(5) + body_layout.addWidget(top_widget) + body_layout.addWidget(content_widget) + + self.label_widget = label_widget self.setAttribute(QtCore.Qt.WA_StyledBackground) From f994d56464fa2f0fe6e104d385a22e5c266554e8 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 1 Sep 2020 11:35:58 +0200 Subject: [PATCH 329/813] it is possible to add checkbox next to label instead of child input --- .../config_setting/widgets/inputs.py | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index f6fc0310aa..56ce22bdaf 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1722,6 +1722,31 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): self.setAttribute(QtCore.Qt.WA_StyledBackground) + checkbox_widget = None + checkbox_key = input_data.get("checkbox_key") + if checkbox_key: + checkbox_input_data = { + "key": checkbox_key, + "label": "test" + } + if expandable: + checkbox_widget = BooleanWidget( + checkbox_input_data, values, self.keys, self, + label_widget=self.label_widget, + # parent_widget=body_widget.top_part + ) + body_widget.top_part.layout().addWidget(checkbox_widget) + else: + checkbox_widget = BooleanWidget( + checkbox_input_data, values, self.keys, self, + label_widget=self.label_widget, + # parent_widget=top_widget + ) + top_layout.addWidget(checkbox_widget) + + self.input_fields.append(checkbox_widget) + checkbox_widget.value_changed.connect(self._on_value_change) + for child_data in input_data.get("children", []): self.add_children_gui(child_data, values) @@ -2129,10 +2154,6 @@ class DictFormWidget(QtWidgets.QWidget, ConfigObject): self._is_modified = self.child_modified self._is_overriden = self._was_overriden - def update_style(self): - for item in self.input_fields: - item.update_style() - def remove_overrides(self): self._is_overriden = False self._is_modified = False From 354b5b26f4c81bde9185c247364d87847ca0d51b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 1 Sep 2020 11:44:51 +0200 Subject: [PATCH 330/813] added validation for checkbox_key --- .../config_setting/config_setting/widgets/lib.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/lib.py b/pype/tools/config_setting/config_setting/widgets/lib.py index fe4e514aaf..ac18f09669 100644 --- a/pype/tools/config_setting/config_setting/widgets/lib.py +++ b/pype/tools/config_setting/config_setting/widgets/lib.py @@ -219,20 +219,24 @@ def validate_is_group_is_unique_in_hierarchy( def validate_keys_are_unique(schema_data, keys=None): + children = schema_data.get("children") + if not children: + return + is_top = keys is None if keys is None: keys = [schema_data["key"]] else: keys.append(schema_data["key"]) - children = schema_data.get("children") - if not children: - return - child_queue = Queue() for child in children: child_queue.put(child) + checkbox_key = schema_data.get("checkbox_key") + if checkbox_key: + child_queue.put({"key": checkbox_key}) + child_inputs = [] while not child_queue.empty(): child = child_queue.get() From 2960dcb1e0808b7623369bd49602fc4e93d467c3 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 1 Sep 2020 11:45:09 +0200 Subject: [PATCH 331/813] expandable widget has ability to hide toolbox button --- .../config_setting/config_setting/widgets/widgets.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/widgets.py b/pype/tools/config_setting/config_setting/widgets/widgets.py index e551d1e9c3..d90242600e 100644 --- a/pype/tools/config_setting/config_setting/widgets/widgets.py +++ b/pype/tools/config_setting/config_setting/widgets/widgets.py @@ -53,6 +53,8 @@ class ExpandingWidget(QtWidgets.QWidget): super(ExpandingWidget, self).__init__(parent) self.setObjectName("ExpandingWidget") + self.toolbox_hidden = False + top_part = ClickableWidget(parent=self) button_size = QtCore.QSize(5, 5) @@ -82,6 +84,12 @@ class ExpandingWidget(QtWidgets.QWidget): self.top_part.clicked.connect(self._top_part_clicked) self.button_toggle.clicked.connect(self.toggle_content) + def hide_toolbox(self): + self.button_toggle.setArrowType(QtCore.Qt.NoArrow) + self.toolbox_hidden = True + self.content_widget.setVisible(False) + self.parent().updateGeometry() + def set_content_widget(self, content_widget): main_layout = QtWidgets.QVBoxLayout(self) main_layout.setContentsMargins(9, 9, 0, 9) @@ -98,6 +106,8 @@ class ExpandingWidget(QtWidgets.QWidget): self.toggle_content(not self.button_toggle.isChecked()) def toggle_content(self, *args): + if self.toolbox_hidden: + return if len(args) > 0: checked = args[0] else: From 977855566b51f484890955c1e520eaee1f9a1d15 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 1 Sep 2020 11:45:34 +0200 Subject: [PATCH 332/813] checkbox creation is same --- .../config_setting/widgets/inputs.py | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 56ce22bdaf..477fdc9292 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1726,22 +1726,15 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): checkbox_key = input_data.get("checkbox_key") if checkbox_key: checkbox_input_data = { - "key": checkbox_key, - "label": "test" + "key": checkbox_key } + checkbox_widget = BooleanWidget( + checkbox_input_data, values, self.keys, self, + label_widget=self.label_widget + ) if expandable: - checkbox_widget = BooleanWidget( - checkbox_input_data, values, self.keys, self, - label_widget=self.label_widget, - # parent_widget=body_widget.top_part - ) body_widget.top_part.layout().addWidget(checkbox_widget) else: - checkbox_widget = BooleanWidget( - checkbox_input_data, values, self.keys, self, - label_widget=self.label_widget, - # parent_widget=top_widget - ) top_layout.addWidget(checkbox_widget) self.input_fields.append(checkbox_widget) From 7b679f5e79239565875b490c7d89208ed4d2e7f7 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 1 Sep 2020 11:45:56 +0200 Subject: [PATCH 333/813] trigger hierarchical_style_update instead of individual update_style on items --- pype/tools/config_setting/config_setting/widgets/inputs.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 477fdc9292..3c5c246c43 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1806,11 +1806,7 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): if self.is_overidable: self._is_overriden = True - # TODO update items - if item is not None: - for _item in self.input_fields: - if _item is not item: - _item.update_style() + self.hierarchical_style_update() self.value_changed.emit(self) From 3800fc91dc3e9b796ff95e60a4f232b6c017a13d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 1 Sep 2020 11:46:19 +0200 Subject: [PATCH 334/813] hide content and arrow if only checkbox key is set in dict widget --- pype/tools/config_setting/config_setting/widgets/inputs.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 3c5c246c43..64d2cbb761 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1740,7 +1740,11 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): self.input_fields.append(checkbox_widget) checkbox_widget.value_changed.connect(self._on_value_change) - for child_data in input_data.get("children", []): + children_data = input_data.get("children", []) + if expandable and checkbox_widget and not children_data: + body_widget.hide_toolbox() + + for child_data in children_data: self.add_children_gui(child_data, values) def remove_overrides(self): From 5e3599b8d08272923bac24f5f1bf623c50563249 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 1 Sep 2020 12:33:57 +0200 Subject: [PATCH 335/813] expandign widget can set custom context margins --- pype/tools/config_setting/config_setting/widgets/widgets.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/widgets.py b/pype/tools/config_setting/config_setting/widgets/widgets.py index d90242600e..ba6a3c6629 100644 --- a/pype/tools/config_setting/config_setting/widgets/widgets.py +++ b/pype/tools/config_setting/config_setting/widgets/widgets.py @@ -90,9 +90,11 @@ class ExpandingWidget(QtWidgets.QWidget): self.content_widget.setVisible(False) self.parent().updateGeometry() - def set_content_widget(self, content_widget): + def set_content_widget(self, content_widget, margins=None): main_layout = QtWidgets.QVBoxLayout(self) - main_layout.setContentsMargins(9, 9, 0, 9) + if margins is None: + margins = (9, 9, 0, 9) + main_layout.setContentsMargins(*margins) content_widget.setVisible(False) From bbea0da42c57fe6c488e7e5f25e617068d995c4c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 1 Sep 2020 12:34:21 +0200 Subject: [PATCH 336/813] ExpandingWidget style is not used anymore --- .../config_setting/config_setting/style/style.css | 12 ++++++------ .../config_setting/config_setting/widgets/widgets.py | 1 - 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/pype/tools/config_setting/config_setting/style/style.css b/pype/tools/config_setting/config_setting/style/style.css index 996b73b9cd..937259ba7b 100644 --- a/pype/tools/config_setting/config_setting/style/style.css +++ b/pype/tools/config_setting/config_setting/style/style.css @@ -124,7 +124,7 @@ QPushButton[btn-type="expand-toggle"] { font-weight: bold; } -#ExpandingWidget, #ModifiableDict, #DictWidget { +#ModifiableDict, #DictWidget { border-style: solid; border-color: #455c6e; border-left-width: 3px; @@ -132,22 +132,22 @@ QPushButton[btn-type="expand-toggle"] { border-right-width: 0px; border-top-width: 0px; } -#ExpandingWidget:hover, #ModifiableDict:hover, #DictWidget:hover { +#ModifiableDict:hover, #DictWidget:hover { border-color: #62839d; } -#ExpandingWidget[state="child-modified"], #ModifiableDict[state="child-modified"], #DictWidget[state="child-modified"] { +#ModifiableDict[state="child-modified"], #DictWidget[state="child-modified"] { border-color: #106aa2; } -#ExpandingWidget[state="child-modified"]:hover, #ModifiableDict[state="child-modified"]:hover, #DictWidget[state="child-modified"]:hover { +#ModifiableDict[state="child-modified"]:hover, #DictWidget[state="child-modified"]:hover { border-color: #137cbd; } -#ExpandingWidget[state="child-invalid"], #ModifiableDict[state="child-invalid"], #DictWidget[state="child-invalid"] { +#ModifiableDict[state="child-invalid"], #DictWidget[state="child-invalid"] { border-color: #ad2e2e; } -#ExpandingWidget[state="child-invalid"]:hover, #ModifiableDict[state="child-invalid"]:hover, #DictWidget[state="child-invalid"]:hover { +#ModifiableDict[state="child-invalid"]:hover, #DictWidget[state="child-invalid"]:hover { border-color: #c93636; } diff --git a/pype/tools/config_setting/config_setting/widgets/widgets.py b/pype/tools/config_setting/config_setting/widgets/widgets.py index ba6a3c6629..c0045e37fd 100644 --- a/pype/tools/config_setting/config_setting/widgets/widgets.py +++ b/pype/tools/config_setting/config_setting/widgets/widgets.py @@ -51,7 +51,6 @@ class ClickableWidget(QtWidgets.QLabel): class ExpandingWidget(QtWidgets.QWidget): def __init__(self, label, parent): super(ExpandingWidget, self).__init__(parent) - self.setObjectName("ExpandingWidget") self.toolbox_hidden = False From 9bd6f5961c315a17148383164ab8b23fdddd84c5 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 1 Sep 2020 12:34:46 +0200 Subject: [PATCH 337/813] expanding widget can hide arrow and keep context showed --- pype/tools/config_setting/config_setting/widgets/widgets.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/widgets.py b/pype/tools/config_setting/config_setting/widgets/widgets.py index c0045e37fd..db41fda1f6 100644 --- a/pype/tools/config_setting/config_setting/widgets/widgets.py +++ b/pype/tools/config_setting/config_setting/widgets/widgets.py @@ -83,10 +83,10 @@ class ExpandingWidget(QtWidgets.QWidget): self.top_part.clicked.connect(self._top_part_clicked) self.button_toggle.clicked.connect(self.toggle_content) - def hide_toolbox(self): + def hide_toolbox(self, hide_content=False): self.button_toggle.setArrowType(QtCore.Qt.NoArrow) self.toolbox_hidden = True - self.content_widget.setVisible(False) + self.content_widget.setVisible(not hide_content) self.parent().updateGeometry() def set_content_widget(self, content_widget, margins=None): From 5c806ca77e7254896a5c43616bdde20f693def77 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 1 Sep 2020 12:35:15 +0200 Subject: [PATCH 338/813] content margins are same for both cases --- pype/tools/config_setting/config_setting/widgets/inputs.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 64d2cbb761..2fb83566b0 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1673,14 +1673,13 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): self.keys = keys main_layout = QtWidgets.QHBoxLayout(self) + main_layout.setContentsMargins(5, 5, 0, 5) main_layout.setSpacing(0) expandable = input_data.get("expandable", True) if expandable: - main_layout.setContentsMargins(0, 0, 0, 0) body_widget = ExpandingWidget(input_data["label"], self) else: - main_layout.setContentsMargins(5, 5, 0, 5) body_widget = QtWidgets.QWidget(self) main_layout.addWidget(body_widget) From fd58aeb7fdd5a68018c171fae3c44e20ae396c74 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 1 Sep 2020 12:35:42 +0200 Subject: [PATCH 339/813] as body widget is always used expanding widget in dict widget --- .../config_setting/widgets/inputs.py | 35 ++++--------------- 1 file changed, 7 insertions(+), 28 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 2fb83566b0..3737d8c858 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1676,11 +1676,7 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): main_layout.setContentsMargins(5, 5, 0, 5) main_layout.setSpacing(0) - expandable = input_data.get("expandable", True) - if expandable: - body_widget = ExpandingWidget(input_data["label"], self) - else: - body_widget = QtWidgets.QWidget(self) + body_widget = ExpandingWidget(input_data["label"], self) main_layout.addWidget(body_widget) @@ -1691,33 +1687,16 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): self.content_widget = content_widget self.content_layout = content_layout + body_widget.set_content_widget(content_widget, (4, 4, 0, 4)) + self.label_widget = body_widget.label_widget + + expandable = input_data.get("expandable", True) if expandable: - body_widget.set_content_widget(content_widget) - self.label_widget = body_widget.label_widget expanded = input_data.get("expanded", False) if expanded: body_widget.toggle_content() - else: - top_widget = QtWidgets.QWidget(body_widget) - top_layout = QtWidgets.QHBoxLayout(top_widget) - top_layout.setContentsMargins(0, 0, 0, 0) - top_layout.setSpacing(5) - - label_widget = QtWidgets.QLabel( - input_data["label"], parent=top_widget - ) - label_widget.setObjectName("DictLabel") - - top_layout.addWidget(label_widget) - - body_layout = QtWidgets.QVBoxLayout(body_widget) - body_layout.setContentsMargins(0, 0, 0, 0) - body_layout.setSpacing(5) - body_layout.addWidget(top_widget) - body_layout.addWidget(content_widget) - - self.label_widget = label_widget + body_widget.hide_toolbox(hide_content=False) self.setAttribute(QtCore.Qt.WA_StyledBackground) @@ -1741,7 +1720,7 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): children_data = input_data.get("children", []) if expandable and checkbox_widget and not children_data: - body_widget.hide_toolbox() + body_widget.hide_toolbox(hide_content=True) for child_data in children_data: self.add_children_gui(child_data, values) From 01ae1ef294226fce524b6045d660400dcb8f5de1 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 1 Sep 2020 12:55:26 +0200 Subject: [PATCH 340/813] checkbox key requires to have widget specification in children --- .../config_setting/widgets/inputs.py | 59 ++++++++++--------- .../config_setting/widgets/lib.py | 4 -- 2 files changed, 30 insertions(+), 33 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 3737d8c858..cf1e7b017e 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1684,47 +1684,33 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): content_layout = QtWidgets.QVBoxLayout(content_widget) content_layout.setContentsMargins(3, 3, 0, 3) + body_widget.set_content_widget(content_widget, (4, 4, 0, 4)) + + self.body_widget = body_widget self.content_widget = content_widget self.content_layout = content_layout - body_widget.set_content_widget(content_widget, (4, 4, 0, 4)) self.label_widget = body_widget.label_widget + self.setAttribute(QtCore.Qt.WA_StyledBackground) + + self.checkbox_widget = None + self.checkbox_key = input_data.get("checkbox_key") + + for child_data in input_data.get("children", []): + self.add_children_gui(child_data, values) + expandable = input_data.get("expandable", True) - if expandable: + if len(self.input_fields) == 1 and self.checkbox_widget: + body_widget.hide_toolbox(hide_content=True) + + elif expandable: expanded = input_data.get("expanded", False) if expanded: body_widget.toggle_content() else: body_widget.hide_toolbox(hide_content=False) - self.setAttribute(QtCore.Qt.WA_StyledBackground) - - checkbox_widget = None - checkbox_key = input_data.get("checkbox_key") - if checkbox_key: - checkbox_input_data = { - "key": checkbox_key - } - checkbox_widget = BooleanWidget( - checkbox_input_data, values, self.keys, self, - label_widget=self.label_widget - ) - if expandable: - body_widget.top_part.layout().addWidget(checkbox_widget) - else: - top_layout.addWidget(checkbox_widget) - - self.input_fields.append(checkbox_widget) - checkbox_widget.value_changed.connect(self._on_value_change) - - children_data = input_data.get("children", []) - if expandable and checkbox_widget and not children_data: - body_widget.hide_toolbox(hide_content=True) - - for child_data in children_data: - self.add_children_gui(child_data, values) - def remove_overrides(self): self._is_overriden = False self._is_modified = False @@ -1868,6 +1854,10 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): def add_children_gui(self, child_configuration, values): item_type = child_configuration["type"] klass = TypeToKlass.types.get(item_type) + if self.checkbox_key and not self.checkbox_widget: + key = child_configuration.get("key") + if key == self.checkbox_key: + return self._add_checkbox_child(child_configuration, values) item = klass( child_configuration, values, self.keys, self @@ -1878,6 +1868,17 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): self.input_fields.append(item) return item + def _add_checkbox_child(self, child_configuration, values): + item = BooleanWidget( + child_configuration, values, self.keys, self, self.label_widget + ) + item.value_changed.connect(self._on_value_change) + + self.body_widget.top_part.layout().addWidget(item) + self.checkbox_widget = item + self.input_fields.append(item) + return item + def overrides(self): if not self.is_overriden and not self.child_overriden: return NOT_SET, False diff --git a/pype/tools/config_setting/config_setting/widgets/lib.py b/pype/tools/config_setting/config_setting/widgets/lib.py index ac18f09669..c416f7a5b0 100644 --- a/pype/tools/config_setting/config_setting/widgets/lib.py +++ b/pype/tools/config_setting/config_setting/widgets/lib.py @@ -233,10 +233,6 @@ def validate_keys_are_unique(schema_data, keys=None): for child in children: child_queue.put(child) - checkbox_key = schema_data.get("checkbox_key") - if checkbox_key: - child_queue.put({"key": checkbox_key}) - child_inputs = [] while not child_queue.empty(): child = child_queue.get() From aae3fbf971afbcf96956aea5903badbee10e7a79 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 1 Sep 2020 13:01:03 +0200 Subject: [PATCH 341/813] modified few schemas to match new dict checkbox key --- .../projects_schema/1_plugins_gui_schema.json | 69 ++++++++++++------- 1 file changed, 43 insertions(+), 26 deletions(-) diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json index bb323c9803..19d5127a95 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json @@ -19,20 +19,21 @@ "children": [ { "type": "dict", - "expandable": false, + "expandable": true, + "checkbox_key": "enabled", "key": "ExtractCelactionDeadline", "label": "ExtractCelactionDeadline", "is_group": true, "children": [ { + "type": "boolean", + "key": "enabled", + "label": "Enabled", + "default": true + }, { "type": "dict-form", "children": [ { - "type": "boolean", - "key": "enabled", - "label": "Enabled", - "default": true - }, { "type": "text-singleline", "key": "deadline_department", "label": "Deadline apartment", @@ -85,7 +86,8 @@ "children": [ { "type": "dict", - "expandable": false, + "expandable": true, + "checkbox_key": "enabled", "key": "IntegrateFtrackNote", "label": "IntegrateFtrackNote", "is_group": true, @@ -127,7 +129,8 @@ "children": [ { "type": "dict", - "expandable": false, + "expandable": true, + "checkbox_key": "enabled", "key": "IntegrateMasterVersion", "label": "IntegrateMasterVersion", "is_group": true, @@ -140,7 +143,8 @@ ] }, { "type": "dict", - "expandable": false, + "expandable": true, + "checkbox_key": "enabled", "key": "ExtractJpegEXR", "label": "ExtractJpegEXR", "is_group": true, @@ -167,9 +171,10 @@ ] }, { "type": "dict", - "expandable": false, + "expandable": true, "key": "ExtractReview", "label": "ExtractReview", + "checkbox_key": "enabled", "is_group": true, "children": [ { @@ -186,9 +191,10 @@ ] }, { "type": "dict", - "expandable": false, + "expandable": true, "key": "ExtractBurnin", "label": "ExtractBurnin", + "checkbox_key": "enabled", "is_group": true, "children": [ { @@ -241,7 +247,7 @@ ] }, { "type": "dict", - "expandable": false, + "expandable": true, "key": "IntegrateAssetNew", "label": "IntegrateAssetNew", "is_group": true, @@ -254,9 +260,10 @@ ] }, { "type": "dict", - "expandable": false, + "expandable": true, "key": "ProcessSubmittedJobOnFarm", "label": "ProcessSubmittedJobOnFarm", + "checkbox_key": "enabled", "is_group": true, "children": [ { @@ -296,9 +303,10 @@ "children": [ { "type": "dict", - "expandable": false, + "expandable": true, "key": "ValidateModelName", "label": "Validate Model Name", + "checkbox_key": "enabled", "is_group": true, "children": [ { @@ -318,9 +326,10 @@ ] }, { "type": "dict", - "expandable": false, + "expandable": true, "key": "ValidateAssemblyName", "label": "Validate Assembly Name", + "checkbox_key": "enabled", "is_group": true, "children": [ { @@ -331,9 +340,10 @@ ] }, { "type": "dict", - "expandable": false, + "expandable": true, "key": "ValidateShaderName", "label": "ValidateShaderName", + "checkbox_key": "enabled", "is_group": true, "children": [ { @@ -349,9 +359,10 @@ ] }, { "type": "dict", - "expandable": false, + "expandable": true, "key": "ValidateMeshHasOverlappingUVs", "label": "ValidateMeshHasOverlappingUVs", + "checkbox_key": "enabled", "is_group": true, "children": [ { @@ -421,7 +432,8 @@ "children": [ { "type": "dict", - "expandable": false, + "expandable": true, + "checkbox_key": "enabled", "key": "ExtractThumbnail", "label": "ExtractThumbnail", "is_group": true, @@ -439,7 +451,8 @@ ] }, { "type": "dict", - "expandable": false, + "expandable": true, + "checkbox_key": "enabled", "key": "ValidateNukeWriteKnobs", "label": "ValidateNukeWriteKnobs", "is_group": true, @@ -457,7 +470,8 @@ ] }, { "type": "dict", - "expandable": false, + "expandable": true, + "checkbox_key": "enabled", "key": "ExtractReviewDataLut", "label": "ExtractReviewDataLut", "is_group": true, @@ -471,7 +485,8 @@ ] }, { "type": "dict", - "expandable": false, + "expandable": true, + "checkbox_key": "enabled", "key": "ExtractReviewDataMov", "label": "ExtractReviewDataMov", "is_group": true, @@ -490,7 +505,7 @@ ] }, { "type": "dict", - "expandable": false, + "expandable": true, "key": "ExtractSlateFrame", "label": "ExtractSlateFrame", "is_group": true, @@ -504,7 +519,7 @@ ] }, { "type": "dict", - "expandable": false, + "expandable": true, "key": "NukeSubmitDeadline", "label": "NukeSubmitDeadline", "is_group": true, @@ -555,7 +570,8 @@ "children": [ { "type": "dict", - "expandable": false, + "expandable": true, + "checkbox_key": "enabled", "key": "CollectInstanceVersion", "label": "Collect Instance Version", "is_group": true, @@ -569,7 +585,8 @@ ] }, { "type": "dict", - "expandable": false, + "expandable": true, + "checkbox_key": "enabled", "key": "ExtractReviewCutUpVideo", "label": "Extract Review Cut Up Video", "is_group": true, @@ -606,7 +623,7 @@ "children": [ { "type": "dict", - "expandable": false, + "expandable": true, "key": "CreateShotClip", "label": "Create Shot Clip", "is_group": true, From ce6e64e5734dcde2a7c0f0b5282f7e1f9532c2e9 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 1 Sep 2020 14:45:09 +0200 Subject: [PATCH 342/813] adde is_nullable to inputs --- .../config_setting/widgets/inputs.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index cf1e7b017e..f1eee2b33b 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -19,6 +19,7 @@ class ConfigObject: _was_overriden = False _is_invalid = False _is_group = False + _is_nullable = False _log = None @@ -53,6 +54,10 @@ class ConfigObject: """Value set in is not valid.""" return self._is_group + @property + def is_nullable(self): + return self._is_nullable + @property def is_overidable(self): """Should care about overrides.""" @@ -335,6 +340,7 @@ class BooleanWidget(QtWidgets.QWidget, InputObject): self._as_widget = values is AS_WIDGET self._is_group = input_data.get("is_group", False) + self._is_nullable = input_data.get("is_nullable", False) self.default_value = input_data.get("default", NOT_SET) self._state = None @@ -450,6 +456,7 @@ class IntegerWidget(QtWidgets.QWidget, InputObject): self._as_widget = values is AS_WIDGET self._is_group = input_data.get("is_group", False) + self._is_nullable = input_data.get("is_nullable", False) self.default_value = input_data.get("default", NOT_SET) self._state = None @@ -561,6 +568,7 @@ class FloatWidget(QtWidgets.QWidget, InputObject): self._as_widget = values is AS_WIDGET self._is_group = input_data.get("is_group", False) + self._is_nullable = input_data.get("is_nullable", False) self.default_value = input_data.get("default", NOT_SET) self._state = None @@ -681,6 +689,7 @@ class TextSingleLineWidget(QtWidgets.QWidget, InputObject): self._as_widget = values is AS_WIDGET self._is_group = input_data.get("is_group", False) + self._is_nullable = input_data.get("is_nullable", False) self.default_value = input_data.get("default", NOT_SET) self._state = None @@ -786,6 +795,7 @@ class TextMultiLineWidget(QtWidgets.QWidget, InputObject): self._as_widget = values is AS_WIDGET self._is_group = input_data.get("is_group", False) + self._is_nullable = input_data.get("is_nullable", False) self.default_value = input_data.get("default", NOT_SET) self._state = None @@ -949,6 +959,7 @@ class RawJsonWidget(QtWidgets.QWidget, InputObject): self.any_parent_is_group = any_parent_is_group self._is_group = input_data.get("is_group", False) + self._is_nullable = input_data.get("is_nullable", False) self.default_value = input_data.get("default", NOT_SET) self._state = None @@ -1142,6 +1153,7 @@ class ListWidget(QtWidgets.QWidget, InputObject): self._state = None self._is_group = input_data.get("is_group", False) + self._is_nullable = input_data.get("is_nullable", False) self.object_type = input_data["object_type"] self.default_value = input_data.get("default", NOT_SET) @@ -1464,6 +1476,7 @@ class ModifiableDict(ExpandingWidget, InputObject): self.any_parent_is_group = any_parent_is_group self._is_group = input_data.get("is_group", False) + self._is_nullable = input_data.get("is_nullable", False) inputs_widget = QtWidgets.QWidget(self) inputs_widget.setAttribute(QtCore.Qt.WA_StyledBackground) @@ -1664,6 +1677,7 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): self.any_parent_is_group = any_parent_is_group self._is_group = input_data.get("is_group", False) + self._is_nullable = input_data.get("is_nullable", False) self.input_fields = [] From a57fbd8f407cc115bb64674173116440378a9fe6 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 1 Sep 2020 15:02:53 +0200 Subject: [PATCH 343/813] feat(nuke): adding image loader --- pype/plugins/nuke/load/load_image.py | 233 ++++++++++++++++++++++++ pype/plugins/nuke/load/load_sequence.py | 18 +- 2 files changed, 242 insertions(+), 9 deletions(-) create mode 100644 pype/plugins/nuke/load/load_image.py diff --git a/pype/plugins/nuke/load/load_image.py b/pype/plugins/nuke/load/load_image.py new file mode 100644 index 0000000000..377d52aa14 --- /dev/null +++ b/pype/plugins/nuke/load/load_image.py @@ -0,0 +1,233 @@ +import re +import nuke + +from avalon.vendor import qargparse +from avalon import api, io + +from pype.hosts.nuke import presets + + +class LoadImage(api.Loader): + """Load still image into Nuke""" + + families = [ + "render2d", "source", "plate", + "render", "prerender", "review", + "image" + ] + representations = ["exr", "dpx", "jpg", "jpeg", "png", "psd"] + + label = "Load Image" + order = -10 + icon = "image" + color = "white" + + options = [ + qargparse.Integer( + "frame_number", + label="Frame Number", + default=int(nuke.root()["first_frame"].getValue()), + min=1, + max=999999, + help="What frame is reading from?" + ) + ] + + def load(self, context, name, namespace, options): + from avalon.nuke import ( + containerise, + viewer_update_and_undo_stop + ) + self.log.info("__ options: `{}`".format(options)) + frame_number = options.get("frame_number", 1) + + version = context['version'] + version_data = version.get("data", {}) + repr_id = context["representation"]["_id"] + + self.log.info("version_data: {}\n".format(version_data)) + self.log.debug( + "Representation id `{}` ".format(repr_id)) + + last = first = int(frame_number) + + # Fallback to asset name when namespace is None + if namespace is None: + namespace = context['asset']['name'] + + file = self.fname + + if not file: + repr_id = context["representation"]["_id"] + self.log.warning( + "Representation id `{}` is failing to load".format(repr_id)) + return + + file = file.replace("\\", "/") + + repr_cont = context["representation"]["context"] + frame = repr_cont.get("frame") + if frame: + padding = len(frame) + file = file.replace( + frame, + format(frame_number, "0{}".format(padding))) + + read_name = "Read_{0}_{1}_{2}".format( + repr_cont["asset"], + repr_cont["subset"], + repr_cont["representation"]) + + # Create the Loader with the filename path set + with viewer_update_and_undo_stop(): + r = nuke.createNode( + "Read", + "name {}".format(read_name)) + r["file"].setValue(file) + + # Set colorspace defined in version data + colorspace = context["version"]["data"].get("colorspace") + if colorspace: + r["colorspace"].setValue(str(colorspace)) + + # load nuke presets for Read's colorspace + read_clrs_presets = presets.get_colorspace_preset().get( + "nuke", {}).get("read", {}) + + # check if any colorspace presets for read is mathing + preset_clrsp = next((read_clrs_presets[k] + for k in read_clrs_presets + if bool(re.search(k, file))), + None) + if preset_clrsp is not None: + r["colorspace"].setValue(str(preset_clrsp)) + + r["origfirst"].setValue(first) + r["first"].setValue(first) + r["origlast"].setValue(last) + r["last"].setValue(last) + + # add additional metadata from the version to imprint Avalon knob + add_keys = ["source", "colorspace", "author", "fps", "version"] + + data_imprint = { + "frameStart": first, + "frameEnd": last + } + for k in add_keys: + if k == 'version': + data_imprint.update({k: context["version"]['name']}) + else: + data_imprint.update( + {k: context["version"]['data'].get(k, str(None))}) + + data_imprint.update({"objectName": read_name}) + + r["tile_color"].setValue(int("0x4ecd25ff", 16)) + + return containerise(r, + name=name, + namespace=namespace, + context=context, + loader=self.__class__.__name__, + data=data_imprint) + + def switch(self, container, representation): + self.update(container, representation) + + def update(self, container, representation): + """Update the Loader's path + + Nuke automatically tries to reset some variables when changing + the loader's path to a new file. These automatic changes are to its + inputs: + + """ + + from avalon.nuke import ( + update_container + ) + + node = nuke.toNode(container["objectName"]) + frame_number = node["first"].value() + + assert node.Class() == "Read", "Must be Read" + + repr_cont = representation["context"] + + file = api.get_representation_path(representation) + + if not file: + repr_id = representation["_id"] + self.log.warning( + "Representation id `{}` is failing to load".format(repr_id)) + return + + file = file.replace("\\", "/") + + frame = repr_cont.get("frame") + if frame: + padding = len(frame) + file = file.replace( + frame, + format(frame_number, "0{}".format(padding))) + + # Get start frame from version data + version = io.find_one({ + "type": "version", + "_id": representation["parent"] + }) + + # get all versions in list + versions = io.find({ + "type": "version", + "parent": version["parent"] + }).distinct('name') + + max_version = max(versions) + + version_data = version.get("data", {}) + + last = first = int(frame_number) + + # Set the global in to the start frame of the sequence + node["origfirst"].setValue(first) + node["first"].setValue(first) + node["origlast"].setValue(last) + node["last"].setValue(last) + + updated_dict = {} + updated_dict.update({ + "representation": str(representation["_id"]), + "frameStart": str(first), + "frameEnd": str(last), + "version": str(version.get("name")), + "colorspace": version_data.get("colorspace"), + "source": version_data.get("source"), + "fps": str(version_data.get("fps")), + "author": version_data.get("author"), + "outputDir": version_data.get("outputDir"), + }) + + # change color of node + if version.get("name") not in [max_version]: + node["tile_color"].setValue(int("0xd84f20ff", 16)) + else: + node["tile_color"].setValue(int("0x4ecd25ff", 16)) + + # Update the imprinted representation + update_container( + node, + updated_dict + ) + self.log.info("udated to version: {}".format(version.get("name"))) + + def remove(self, container): + + from avalon.nuke import viewer_update_and_undo_stop + + node = nuke.toNode(container['objectName']) + assert node.Class() == "Read", "Must be Read" + + with viewer_update_and_undo_stop(): + nuke.delete(node) diff --git a/pype/plugins/nuke/load/load_sequence.py b/pype/plugins/nuke/load/load_sequence.py index 601e28c7c1..c5ce288540 100644 --- a/pype/plugins/nuke/load/load_sequence.py +++ b/pype/plugins/nuke/load/load_sequence.py @@ -120,12 +120,12 @@ class LoadSequence(api.Loader): if "#" not in file: frame = repr_cont.get("frame") padding = len(frame) - file = file.replace(frame, "#"*padding) + file = file.replace(frame, "#" * padding) read_name = "Read_{0}_{1}_{2}".format( - repr_cont["asset"], - repr_cont["subset"], - repr_cont["representation"]) + repr_cont["asset"], + repr_cont["subset"], + repr_cont["representation"]) # Create the Loader with the filename path set with viewer_update_and_undo_stop(): @@ -250,7 +250,7 @@ class LoadSequence(api.Loader): if "#" not in file: frame = repr_cont.get("frame") padding = len(frame) - file = file.replace(frame, "#"*padding) + file = file.replace(frame, "#" * padding) # Get start frame from version data version = io.find_one({ @@ -276,10 +276,10 @@ class LoadSequence(api.Loader): last = version_data.get("frameEnd") if first is None: - self.log.warning("Missing start frame for updated version" - "assuming starts at frame 0 for: " - "{} ({})".format( - node['name'].value(), representation)) + self.log.warning( + "Missing start frame for updated version" + "assuming starts at frame 0 for: " + "{} ({})".format(node['name'].value(), representation)) first = 0 first -= self.handle_start From 4970aaf5124cc7e0bfc4d5c1b252e486542da993 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 1 Sep 2020 14:22:30 +0100 Subject: [PATCH 344/813] Remove extra dash --- 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 718943855c..156896a759 100644 --- a/pype/scripts/otio_burnin.py +++ b/pype/scripts/otio_burnin.py @@ -526,7 +526,7 @@ def burnins_from_data( bit_rate = burnin._streams[0].get("bit_rate") if bit_rate: - ffmpeg_args.append("--b:v {}".format(bit_rate)) + ffmpeg_args.append("-b:v {}".format(bit_rate)) pix_fmt = burnin._streams[0].get("pix_fmt") if pix_fmt: From dabe68f28cd1c391a2b4930af40d60b801bc78e2 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 1 Sep 2020 16:50:00 +0200 Subject: [PATCH 345/813] initial commit for anatomy widget --- .../projects_schema/0_project_gui_schema.json | 4 ++ .../config_setting/widgets/__init__.py | 4 +- .../config_setting/widgets/anatomy_inputs.py | 63 +++++++++++++++++++ 3 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/0_project_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/0_project_gui_schema.json index 10641d5eda..91bacf2e5a 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/0_project_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/0_project_gui_schema.json @@ -3,6 +3,10 @@ "type": "dict-invisible", "children": [ { + "type": "anatomy", + "key": "anatomy", + "is_file": true + }, { "type": "schema", "children": [ "1_plugins_gui_schema" diff --git a/pype/tools/config_setting/config_setting/widgets/__init__.py b/pype/tools/config_setting/config_setting/widgets/__init__.py index 0682f00324..034692898c 100644 --- a/pype/tools/config_setting/config_setting/widgets/__init__.py +++ b/pype/tools/config_setting/config_setting/widgets/__init__.py @@ -1,8 +1,10 @@ from .window import MainWidget # TODO properly register inputs to TypeToKlass class from . import inputs +from . import anatomy_inputs __all__ = [ "MainWidget", - "inputs" + "inputs", + "anatomy_inputs" ] diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py new file mode 100644 index 0000000000..addd0d6666 --- /dev/null +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -0,0 +1,63 @@ +import json +import logging +from Qt import QtWidgets, QtCore, QtGui +from .widgets import ( + ExpandingWidget, + ModifiedIntSpinBox, + ModifiedFloatSpinBox +) +from .inputs import ConfigObject, InputObject +from .lib import NOT_SET, AS_WIDGET, METADATA_KEY, TypeToKlass + + +class AnatomyWidget(QtWidgets.QWidget, InputObject): + value_changed = QtCore.Signal(object) + + def __init__( + self, input_data, values, parent_keys, parent, label_widget=None + ): + self._parent = parent + self._as_widget = values is AS_WIDGET + + self._is_group = True + self._state = None + + self.key = "anatomy" + self.start_value = None + + super(AnatomyWidget, self).__init__(parent) + + layout = QtWidgets.QHBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(5) + label = QtWidgets.QLabel("Test") + layout.addWidget(label) + + self.override_value = NOT_SET + + def update_global_values(self, values): + print("* update_global_values") + + def set_value(self, value, *, global_value=False): + print("* set_value") + + def clear_value(self): + print("* clear_value") + + def _on_value_change(self, item=None): + print("* _on_value_change") + + def update_style(self): + print("* update_style") + + def item_value(self): + print("* item_value") + + +class TemplatesWidget: + pass + + + + +TypeToKlass.types["anatomy"] = AnatomyWidget From 13ee08a4ac73fdf4b5e000224da2bbb715b4cc12 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 1 Sep 2020 17:55:24 +0200 Subject: [PATCH 346/813] initial commit of path input --- .../config_setting/widgets/inputs.py | 283 ++++++++++++++++++ 1 file changed, 283 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index f1eee2b33b..1416bbebda 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -2247,8 +2247,291 @@ class DictFormWidget(QtWidgets.QWidget, ConfigObject): return values, self.is_group +class PathInput(QtWidgets.QLineEdit): + def clear_end_path(self): + value = self.text().strip() + print("clearing") + if value.endswith("/"): + while value and value[-1] == "/": + value = value[:-1] + self.setText(value) + + def keyPressEvent(self, event): + if event.key() == QtCore.Qt.Key_Backslash: + event.accept() + new_event = QtGui.QKeyEvent( + event.type(), + QtCore.Qt.Key_Slash, + event.modifiers(), + "/", + event.isAutoRepeat(), + event.count() + ) + QtWidgets.QApplication.sendEvent(self, new_event) + return + super(PathInput, self).keyPressEvent(event) + + def focusOutEvent(self, event): + super(PathInput, self).focusOutEvent(event) + self.clear_end_path() + + +class PathWidgetInput(QtWidgets.QWidget, InputObject): + value_changed = QtCore.Signal(object) + + def __init__( + self, input_data, values, parent_keys, parent, label_widget=None + ): + self._parent = parent + self._as_widget = values is AS_WIDGET + + self._is_group = input_data.get("is_group", False) + self._is_nullable = input_data.get("is_nullable", False) + self.default_value = input_data.get("default", NOT_SET) + + self._state = None + + super(PathWidgetInput, self).__init__(parent) + + layout = QtWidgets.QHBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(5) + + self.path_input = PathInput(self) + + self.setFocusProxy(self.path_input) + + if not self._as_widget and not label_widget: + label = input_data["label"] + label_widget = QtWidgets.QLabel(label) + layout.addWidget(label_widget, 0) + layout.addWidget(self.path_input, 1) + + if not self._as_widget: + self.label_widget = label_widget + + self.key = input_data["key"] + keys = list(parent_keys) + keys.append(self.key) + self.keys = keys + + self.update_global_values(values) + + self.override_value = NOT_SET + + self.path_input.textChanged.connect(self._on_value_change) + + def update_global_values(self, values): + value = NOT_SET + if not self._as_widget: + value = self.value_from_values(values) + if value is not NOT_SET: + self.path_input.setText(value) + + elif self.default_value is not NOT_SET: + self.path_input.setText(self.default_value) + + self.global_value = value + self.start_value = self.item_value() + + self._is_modified = self.global_value != self.start_value + + def set_value(self, value, *, global_value=False): + self.path_input.setText(value) + if global_value: + self.start_value = self.item_value() + self.global_value = self.item_value() + self._on_value_change() + + def reset_value(self): + self.set_value(self.start_value) + + def clear_value(self): + self.set_value("") + + def focusOutEvent(self, event): + super(PathInput, self).focusOutEvent(event) + value = self.item_value().strip() + if value.endswith("/"): + while value and value[-1] == "/": + value = value[:-1] + self.set_value(value) + + def _on_value_change(self, item=None): + if self.ignore_value_changes: + return + + self._is_modified = self.item_value() != self.global_value + if self.is_overidable: + self._is_overriden = True + + self.update_style() + + self.value_changed.emit(self) + + def update_style(self): + state = self.style_state( + self.is_invalid, self.is_overriden, self.is_modified + ) + if self._state == state: + return + + if self._as_widget: + property_name = "input-state" + widget = self.path_input + else: + property_name = "state" + widget = self.label_widget + + widget.setProperty(property_name, state) + widget.style().polish(widget) + + def item_value(self): + return self.path_input.text() + + +class PathWidget(QtWidgets.QWidget, InputObject): + value_changed = QtCore.Signal(object) + + platforms = ("windows", "darwin", "linux") + platform_labels_mapping = { + "windows": "Windows", + "darwin": "MacOS", + "linux": "Linux" + } + platform_separators = { + "windows": ";", + "darwin": ":", + "linux": ":" + } + + def __init__( + self, input_data, values, parent_keys, parent, label_widget=None + ): + super(PathWidget, self).__init__(parent) + + self._parent = parent + self._as_widget = values is AS_WIDGET + + self._is_group = input_data.get("is_group", False) + self._is_nullable = input_data.get("is_nullable", False) + + self.default_value = input_data.get("default", NOT_SET) + self.multi_platform = input_data.get("multi_platform", NOT_SET) + self.multi_path = input_data.get("multi_path", NOT_SET) + + self.override_value = NOT_SET + + self._state = None + + self.input_fields = [] + + if not self._as_widget: + self.key = input_data["key"] + keys = list(parent_keys) + keys.append(self.key) + self.keys = keys + + if not self.multi_platform and not self.multi_path: + layout = QtWidgets.QVBoxLayout(self) + else: + layout = QtWidgets.QHBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(5) + + if not self._as_widget and not label_widget: + label = input_data["label"] + label_widget = QtWidgets.QLabel(label) + layout.addWidget(label_widget, 0) + + self.label_widget = label_widget + + self.content_widget = QtWidgets.QWidget(self) + self.content_layout = QtWidgets.QVBoxLayout(self.content_widget) + + layout.addWidget(self.content_widget) + + self.create_gui() + + self.update_global_values(values) + + def create_gui(self): + if self.multi_platform and self.multi_path: + pass + elif self.multi_platform: + pass + elif self.multi_path: + pass + else: + text_input = QtWidgets.QLineEdit(self.content_widget) + self.setFocusProxy(text_input) + self.content_layout.addWidget(text_input, 1) + self.input_fields.append(text_input) + + def update_global_values(self, values): + value = NOT_SET + if not self._as_widget: + value = self.value_from_values(values) + if value is not NOT_SET: + self.text_input.setText(value) + + elif self.default_value is not NOT_SET: + self.text_input.setText(self.default_value) + + self.global_value = value + self.start_value = self.item_value() + + self._is_modified = self.global_value != self.start_value + + def set_value(self, value, *, global_value=False): + self.text_input.setText(value) + if global_value: + self.start_value = self.item_value() + self.global_value = self.item_value() + self._on_value_change() + + def reset_value(self): + self.set_value(self.start_value) + + def clear_value(self): + self.set_value("") + + def _on_value_change(self, item=None): + if self.ignore_value_changes: + return + + self._is_modified = self.item_value() != self.global_value + if self.is_overidable: + self._is_overriden = True + + self.update_style() + + self.value_changed.emit(self) + + def update_style(self): + state = self.style_state( + self.is_invalid, self.is_overriden, self.is_modified + ) + if self._state == state: + return + + if self._as_widget: + property_name = "input-state" + widget = self.text_input + else: + property_name = "state" + widget = self.label_widget + + widget.setProperty(property_name, state) + widget.style().polish(widget) + + def item_value(self): + return self.text_input.text() + + TypeToKlass.types["boolean"] = BooleanWidget TypeToKlass.types["text-singleline"] = TextSingleLineWidget +TypeToKlass.types["path-input"] = PathWidgetInput TypeToKlass.types["text-multiline"] = TextMultiLineWidget TypeToKlass.types["raw-json"] = RawJsonWidget TypeToKlass.types["int"] = IntegerWidget From cd6c83913fd3af359875c38d17bc1ee93bfc95ae Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 2 Sep 2020 08:56:29 +0100 Subject: [PATCH 347/813] Nuke: skip thumbnail creation on farm submission. The thumbnail is being created when publishing from the baked colourspace movie. --- pype/plugins/global/publish/extract_jpeg.py | 5 +++++ pype/plugins/nuke/publish/extract_thumbnail.py | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/pype/plugins/global/publish/extract_jpeg.py b/pype/plugins/global/publish/extract_jpeg.py index 89a4bbd664..d23ce4360f 100644 --- a/pype/plugins/global/publish/extract_jpeg.py +++ b/pype/plugins/global/publish/extract_jpeg.py @@ -81,6 +81,11 @@ class ExtractJpegEXR(pyblish.api.InstancePlugin): jpeg_items.append("-i {}".format(full_input_path)) # output arguments from presets jpeg_items.extend(ffmpeg_args.get("output") or []) + + # If its a movie file, we just want one frame. + if repre["ext"] == "mov": + jpeg_items.append("-vframes 1") + # output file jpeg_items.append(full_output_path) diff --git a/pype/plugins/nuke/publish/extract_thumbnail.py b/pype/plugins/nuke/publish/extract_thumbnail.py index a3ef09bc9f..a53cb4d146 100644 --- a/pype/plugins/nuke/publish/extract_thumbnail.py +++ b/pype/plugins/nuke/publish/extract_thumbnail.py @@ -15,10 +15,12 @@ class ExtractThumbnail(pype.api.Extractor): order = pyblish.api.ExtractorOrder + 0.01 label = "Extract Thumbnail" - families = ["review", "render.farm"] + families = ["review"] hosts = ["nuke"] def process(self, instance): + if "render.farm" in instance.data["families"]: + return with anlib.maintained_selection(): self.log.debug("instance: {}".format(instance)) From 5b74678220750d0da52c9f290bcbc1299fc7106e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 2 Sep 2020 10:50:27 +0200 Subject: [PATCH 348/813] PathWidgetInput cleanup --- .../config_setting/widgets/inputs.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 1416bbebda..24d0259baf 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -2250,13 +2250,13 @@ class DictFormWidget(QtWidgets.QWidget, ConfigObject): class PathInput(QtWidgets.QLineEdit): def clear_end_path(self): value = self.text().strip() - print("clearing") if value.endswith("/"): while value and value[-1] == "/": value = value[:-1] self.setText(value) def keyPressEvent(self, event): + # Always change backslash `\` for forwardslash `/` if event.key() == QtCore.Qt.Key_Backslash: event.accept() new_event = QtGui.QKeyEvent( @@ -2276,7 +2276,7 @@ class PathInput(QtWidgets.QLineEdit): self.clear_end_path() -class PathWidgetInput(QtWidgets.QWidget, InputObject): +class PathInputWidget(QtWidgets.QWidget, InputObject): value_changed = QtCore.Signal(object) def __init__( @@ -2291,7 +2291,7 @@ class PathWidgetInput(QtWidgets.QWidget, InputObject): self._state = None - super(PathWidgetInput, self).__init__(parent) + super(PathInputWidget, self).__init__(parent) layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) @@ -2350,12 +2350,8 @@ class PathWidgetInput(QtWidgets.QWidget, InputObject): self.set_value("") def focusOutEvent(self, event): + self.path_input.clear_end_path() super(PathInput, self).focusOutEvent(event) - value = self.item_value().strip() - if value.endswith("/"): - while value and value[-1] == "/": - value = value[:-1] - self.set_value(value) def _on_value_change(self, item=None): if self.ignore_value_changes: @@ -2531,7 +2527,7 @@ class PathWidget(QtWidgets.QWidget, InputObject): TypeToKlass.types["boolean"] = BooleanWidget TypeToKlass.types["text-singleline"] = TextSingleLineWidget -TypeToKlass.types["path-input"] = PathWidgetInput +TypeToKlass.types["path-input"] = PathInputWidget TypeToKlass.types["text-multiline"] = TextMultiLineWidget TypeToKlass.types["raw-json"] = RawJsonWidget TypeToKlass.types["int"] = IntegerWidget From bf4205f23d8f4467376696a3b2b0263c75b69665 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 2 Sep 2020 11:08:03 +0200 Subject: [PATCH 349/813] path widget is partially completed --- .../config_setting/widgets/inputs.py | 107 ++++++++++++------ 1 file changed, 73 insertions(+), 34 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 24d0259baf..63db5cb2e2 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -2413,8 +2413,8 @@ class PathWidget(QtWidgets.QWidget, InputObject): self._is_nullable = input_data.get("is_nullable", False) self.default_value = input_data.get("default", NOT_SET) - self.multi_platform = input_data.get("multi_platform", NOT_SET) - self.multi_path = input_data.get("multi_path", NOT_SET) + self.multiplatform = input_data.get("multiplatform", False) + self.multipath = input_data.get("multipath", False) self.override_value = NOT_SET @@ -2428,10 +2428,10 @@ class PathWidget(QtWidgets.QWidget, InputObject): keys.append(self.key) self.keys = keys - if not self.multi_platform and not self.multi_path: - layout = QtWidgets.QVBoxLayout(self) - else: + if not self.multiplatform and not self.multipath: layout = QtWidgets.QHBoxLayout(self) + else: + layout = QtWidgets.QVBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(5) @@ -2441,30 +2441,69 @@ class PathWidget(QtWidgets.QWidget, InputObject): layout.addWidget(label_widget, 0) self.label_widget = label_widget + layout.addWidget(label_widget) self.content_widget = QtWidgets.QWidget(self) self.content_layout = QtWidgets.QVBoxLayout(self.content_widget) + self.content_layout.setSpacing(0) + self.content_layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self.content_widget) - self.create_gui() + self.create_gui(values) self.update_global_values(values) - def create_gui(self): - if self.multi_platform and self.multi_path: - pass - elif self.multi_platform: - pass - elif self.multi_path: - pass - else: - text_input = QtWidgets.QLineEdit(self.content_widget) - self.setFocusProxy(text_input) - self.content_layout.addWidget(text_input, 1) - self.input_fields.append(text_input) + def create_gui(self, values): + if not self.multiplatform and not self.multipath: + input_data = {"key": self.key} + path_input = PathInputWidget( + input_data, values, self.keys, self, self.label_widget + ) + self.setFocusProxy(path_input) + self.content_layout.addWidget(path_input) + self.input_fields.append(path_input) + path_input.value_changed.connect(self._on_value_change) + return + + input_data_for_list = { + "object_type": "path-input" + } + if not self.multiplatform: + input_data_for_list["key"] = self.key + input_widget = ListWidget( + input_data_for_list, values, self.keys, self, self.label_widget + ) + self.setFocusProxy(input_widget) + self.content_layout.addWidget(input_widget) + self.input_fields.append(input_widget) + input_widget.value_changed.connect(self._on_value_change) + return + + proxy_widget = QtWidgets.QWidget(self.content_widget) + proxy_layout = QtWidgets.QFormLayout(proxy_widget) + for platform_key in self.platforms: + platform_label = self.platform_labels_mapping[platform_key] + label_widget = QtWidgets.QLabel(platform_label, proxy_widget) + if self.multipath: + input_data_for_list["key"] = platform_key + input_widget = ListWidget( + input_data_for_list, values, self.keys, self, label_widget + ) + else: + input_data = {"key": platform_key} + input_widget = PathInputWidget( + input_data, values, self.keys, self, label_widget + ) + proxy_layout.addRow(label_widget, input_widget) + self.input_fields.append(input_widget) + input_widget.value_changed.connect(self._on_value_change) + + self.setFocusProxy(self.input_fields[0]) + self.content_layout.addWidget(proxy_widget) def update_global_values(self, values): + print(self.__class__.__name__, "* TODO implement `update_global_values`") value = NOT_SET if not self._as_widget: value = self.value_from_values(values) @@ -2480,6 +2519,7 @@ class PathWidget(QtWidgets.QWidget, InputObject): self._is_modified = self.global_value != self.start_value def set_value(self, value, *, global_value=False): + print(self.__class__.__name__, "* TODO implement `set_value`") self.text_input.setText(value) if global_value: self.start_value = self.item_value() @@ -2487,12 +2527,15 @@ class PathWidget(QtWidgets.QWidget, InputObject): self._on_value_change() def reset_value(self): + print(self.__class__.__name__, "* TODO implement `reset_value`") self.set_value(self.start_value) def clear_value(self): + print(self.__class__.__name__, "* TODO implement `clear_value`") self.set_value("") def _on_value_change(self, item=None): + print(self.__class__.__name__, "* TODO implement `_on_value_change`") if self.ignore_value_changes: return @@ -2505,29 +2548,25 @@ class PathWidget(QtWidgets.QWidget, InputObject): self.value_changed.emit(self) def update_style(self): - state = self.style_state( - self.is_invalid, self.is_overriden, self.is_modified - ) - if self._state == state: - return - - if self._as_widget: - property_name = "input-state" - widget = self.text_input - else: - property_name = "state" - widget = self.label_widget - - widget.setProperty(property_name, state) - widget.style().polish(widget) + print(self.__class__.__name__, "* TODO implement `update_style`") def item_value(self): - return self.text_input.text() + if not self.multiplatform and not self.multipath: + return self.input_fields[0].item_value() + + if not self.multiplatform: + return self.input_fields[0].item_value() + + output = {} + for input_field in self.input_fields: + output.update(input_field.config_value()) + return output TypeToKlass.types["boolean"] = BooleanWidget TypeToKlass.types["text-singleline"] = TextSingleLineWidget TypeToKlass.types["path-input"] = PathInputWidget +TypeToKlass.types["path-widget"] = PathWidget TypeToKlass.types["text-multiline"] = TextMultiLineWidget TypeToKlass.types["raw-json"] = RawJsonWidget TypeToKlass.types["int"] = IntegerWidget From a8b4daa4646e66c326dd1c9926800cdc6a853c2e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 2 Sep 2020 11:09:13 +0200 Subject: [PATCH 350/813] PathInput moved to widgets --- .../config_setting/widgets/inputs.py | 32 ++----------------- .../config_setting/widgets/widgets.py | 31 +++++++++++++++++- 2 files changed, 32 insertions(+), 31 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 63db5cb2e2..812d005153 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -4,7 +4,8 @@ from Qt import QtWidgets, QtCore, QtGui from .widgets import ( ExpandingWidget, ModifiedIntSpinBox, - ModifiedFloatSpinBox + ModifiedFloatSpinBox, + PathInput ) from .lib import NOT_SET, AS_WIDGET, METADATA_KEY, TypeToKlass @@ -2247,35 +2248,6 @@ class DictFormWidget(QtWidgets.QWidget, ConfigObject): return values, self.is_group -class PathInput(QtWidgets.QLineEdit): - def clear_end_path(self): - value = self.text().strip() - if value.endswith("/"): - while value and value[-1] == "/": - value = value[:-1] - self.setText(value) - - def keyPressEvent(self, event): - # Always change backslash `\` for forwardslash `/` - if event.key() == QtCore.Qt.Key_Backslash: - event.accept() - new_event = QtGui.QKeyEvent( - event.type(), - QtCore.Qt.Key_Slash, - event.modifiers(), - "/", - event.isAutoRepeat(), - event.count() - ) - QtWidgets.QApplication.sendEvent(self, new_event) - return - super(PathInput, self).keyPressEvent(event) - - def focusOutEvent(self, event): - super(PathInput, self).focusOutEvent(event) - self.clear_end_path() - - class PathInputWidget(QtWidgets.QWidget, InputObject): value_changed = QtCore.Signal(object) diff --git a/pype/tools/config_setting/config_setting/widgets/widgets.py b/pype/tools/config_setting/config_setting/widgets/widgets.py index db41fda1f6..8e0dda42fd 100644 --- a/pype/tools/config_setting/config_setting/widgets/widgets.py +++ b/pype/tools/config_setting/config_setting/widgets/widgets.py @@ -1,4 +1,4 @@ -from Qt import QtWidgets, QtCore +from Qt import QtWidgets, QtCore, QtGui class ModifiedIntSpinBox(QtWidgets.QSpinBox): @@ -35,6 +35,35 @@ class ModifiedFloatSpinBox(QtWidgets.QDoubleSpinBox): event.ignore() +class PathInput(QtWidgets.QLineEdit): + def clear_end_path(self): + value = self.text().strip() + if value.endswith("/"): + while value and value[-1] == "/": + value = value[:-1] + self.setText(value) + + def keyPressEvent(self, event): + # Always change backslash `\` for forwardslash `/` + if event.key() == QtCore.Qt.Key_Backslash: + event.accept() + new_event = QtGui.QKeyEvent( + event.type(), + QtCore.Qt.Key_Slash, + event.modifiers(), + "/", + event.isAutoRepeat(), + event.count() + ) + QtWidgets.QApplication.sendEvent(self, new_event) + return + super(PathInput, self).keyPressEvent(event) + + def focusOutEvent(self, event): + super(PathInput, self).focusOutEvent(event) + self.clear_end_path() + + class ClickableWidget(QtWidgets.QLabel): clicked = QtCore.Signal() From 78c88a9429cfb0c7b6de22da6b332c1a97aae383 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 2 Sep 2020 11:16:49 +0200 Subject: [PATCH 351/813] fixed ListItem right click --- .../tools/config_setting/config_setting/widgets/inputs.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 812d005153..dbc220d89a 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1140,6 +1140,14 @@ class ListItem(QtWidgets.QWidget, ConfigObject): return self.value_input.item_value() return NOT_SET + @property + def child_modified(self): + return self.value_input.child_modified + + @property + def child_overriden(self): + return self.value_input.child_overriden + class ListWidget(QtWidgets.QWidget, InputObject): value_changed = QtCore.Signal(object) From 22ec0a03b9116ce42e31c0d0ea71f4f05840d68e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 2 Sep 2020 11:39:59 +0200 Subject: [PATCH 352/813] integer and lfoat widget merged into one number widget --- .../config_setting/widgets/widgets.py | 24 ++++--------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/widgets.py b/pype/tools/config_setting/config_setting/widgets/widgets.py index 8e0dda42fd..d5b0f088de 100644 --- a/pype/tools/config_setting/config_setting/widgets/widgets.py +++ b/pype/tools/config_setting/config_setting/widgets/widgets.py @@ -1,28 +1,12 @@ from Qt import QtWidgets, QtCore, QtGui -class ModifiedIntSpinBox(QtWidgets.QSpinBox): +class NumberSpinBox(QtWidgets.QDoubleSpinBox): def __init__(self, *args, **kwargs): min_value = kwargs.pop("minimum", -99999) max_value = kwargs.pop("maximum", 99999) - super(ModifiedIntSpinBox, self).__init__(*args, **kwargs) - self.setFocusPolicy(QtCore.Qt.StrongFocus) - self.setMinimum(min_value) - self.setMaximum(max_value) - - def wheelEvent(self, event): - if self.hasFocus(): - super(ModifiedIntSpinBox, self).wheelEvent(event) - else: - event.ignore() - - -class ModifiedFloatSpinBox(QtWidgets.QDoubleSpinBox): - def __init__(self, *args, **kwargs): - min_value = kwargs.pop("minimum", -99999) - max_value = kwargs.pop("maximum", 99999) - decimals = kwargs.pop("decimal", 2) - super(ModifiedFloatSpinBox, self).__init__(*args, **kwargs) + decimals = kwargs.pop("decimal", 0) + super(NumberSpinBox, self).__init__(*args, **kwargs) self.setFocusPolicy(QtCore.Qt.StrongFocus) self.setDecimals(decimals) self.setMinimum(min_value) @@ -30,7 +14,7 @@ class ModifiedFloatSpinBox(QtWidgets.QDoubleSpinBox): def wheelEvent(self, event): if self.hasFocus(): - super(ModifiedFloatSpinBox, self).wheelEvent(event) + super(NumberSpinBox, self).wheelEvent(event) else: event.ignore() From d0a5d2800650ac5a1165dfa5abf40faf1e72a081 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 2 Sep 2020 11:40:52 +0200 Subject: [PATCH 353/813] integer and float input widgets also reduced to one input field with changeable "decimal" attribute --- .../projects_schema/1_plugins_gui_schema.json | 22 +-- .../studio_schema/0_studio_gui_schema.json | 2 +- .../studio_schema/1_tray_items.json | 10 +- .../config_setting/widgets/inputs.py | 154 ++---------------- 4 files changed, 33 insertions(+), 155 deletions(-) diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json index 19d5127a95..e43518989a 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json @@ -39,7 +39,7 @@ "label": "Deadline apartment", "default": "" }, { - "type": "int", + "type": "number", "key": "deadline_priority", "label": "Deadline priority", "default": 50 @@ -59,7 +59,7 @@ "label": "Deadline Group", "default": "" }, { - "type": "int", + "type": "number", "key": "deadline_chunk_size", "label": "Deadline Chunk size", "default": 10 @@ -208,32 +208,32 @@ "label": "Burnin formating options", "children": [ { - "type": "int", + "type": "number", "key": "font_size", "label": "Font size", "default": 42 }, { - "type": "int", + "type": "number", "key": "opacity", "label": "Font opacity", "default": 1 }, { - "type": "int", + "type": "number", "key": "bg_opacity", "label": "Background opacity", "default": 1 }, { - "type": "int", + "type": "number", "key": "x_offset", "label": "X Offset", "default": 5 }, { - "type": "int", + "type": "number", "key": "y_offset", "label": "Y Offset", "default": 5 }, { - "type": "int", + "type": "number", "key": "bg_padding", "label": "Padding aroung text", "default": 5 @@ -525,7 +525,7 @@ "is_group": true, "children": [ { - "type": "int", + "type": "number", "key": "deadline_priority", "label": "deadline_priority", "default": 50 @@ -540,7 +540,7 @@ "label": "deadline_pool_secondary", "default": "" }, { - "type": "int", + "type": "number", "key": "deadline_chunk_size", "label": "deadline_chunk_size", "default": 1 @@ -639,7 +639,7 @@ "label": "Folder", "default": "takes" }, { - "type": "int", + "type": "number", "key": "steps", "label": "Steps", "default": 20 diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/0_studio_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/0_studio_gui_schema.json index db465fb392..bde340250e 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/0_studio_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/0_studio_gui_schema.json @@ -21,7 +21,7 @@ "key": "muster", "children": [{ "type": "dict-modifiable", - "object_type": "int", + "object_type": "number", "input_modifiers": { "minimum": 0, "maximum": 300 diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_tray_items.json b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_tray_items.json index 7cea724f29..610430b25e 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_tray_items.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_tray_items.json @@ -72,14 +72,14 @@ "expandable": true, "children": [ { - "type": "int", + "type": "number", "key": "default_port", "label": "Default Port", "minimum": 1, "maximum": 65535 }, { "type": "list", - "object_type": "int", + "object_type": "number", "key": "exclude_ports", "label": "Exclude ports", "input_modifiers": { @@ -95,11 +95,13 @@ "expandable": true, "children": [ { - "type": "float", + "type": "number", + "decimal": 2, "key": "full_time", "label": "Max idle time" }, { - "type": "float", + "type": "number", + "decimal": 2, "key": "message_time", "label": "When dialog will show" } diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index dbc220d89a..3e86b864b4 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -3,8 +3,7 @@ import logging from Qt import QtWidgets, QtCore, QtGui from .widgets import ( ExpandingWidget, - ModifiedIntSpinBox, - ModifiedFloatSpinBox, + NumberSpinBox, PathInput ) from .lib import NOT_SET, AS_WIDGET, METADATA_KEY, TypeToKlass @@ -446,13 +445,15 @@ class BooleanWidget(QtWidgets.QWidget, InputObject): return self.checkbox.isChecked() -class IntegerWidget(QtWidgets.QWidget, InputObject): +class NumberWidget(QtWidgets.QWidget, InputObject): value_changed = QtCore.Signal(object) - input_modifiers = ("minimum", "maximum") + input_modifiers = ("minimum", "maximum", "decimal") def __init__( self, input_data, values, parent_keys, parent, label_widget=None ): + super(NumberWidget, self).__init__(parent) + self._parent = parent self._as_widget = values is AS_WIDGET @@ -462,8 +463,6 @@ class IntegerWidget(QtWidgets.QWidget, InputObject): self._state = None - super(IntegerWidget, self).__init__(parent) - layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(5) @@ -473,15 +472,15 @@ class IntegerWidget(QtWidgets.QWidget, InputObject): for modifier in self.input_modifiers if input_data.get(modifier) } - self.int_input = ModifiedIntSpinBox(self, **kwargs) + self.input_field = NumberSpinBox(self, **kwargs) - self.setFocusProxy(self.int_input) + self.setFocusProxy(self.input_field) if not self._as_widget and not label_widget: label = input_data["label"] label_widget = QtWidgets.QLabel(label) layout.addWidget(label_widget, 0) - layout.addWidget(self.int_input, 1) + layout.addWidget(self.input_field, 1) if not self._as_widget: self.label_widget = label_widget @@ -495,17 +494,17 @@ class IntegerWidget(QtWidgets.QWidget, InputObject): self.override_value = NOT_SET - self.int_input.valueChanged.connect(self._on_value_change) + self.input_field.valueChanged.connect(self._on_value_change) def update_global_values(self, values): value = NOT_SET if not self._as_widget: value = self.value_from_values(values) if value is not NOT_SET: - self.int_input.setValue(value) + self.input_field.setValue(value) elif self.default_value is not NOT_SET: - self.int_input.setValue(self.default_value) + self.input_field.setValue(self.default_value) self.global_value = value self.start_value = self.item_value() @@ -513,7 +512,7 @@ class IntegerWidget(QtWidgets.QWidget, InputObject): self._is_modified = self.global_value != self.start_value def set_value(self, value, *, global_value=False): - self.int_input.setValue(value) + self.input_field.setValue(value) if global_value: self.start_value = self.item_value() self.global_value = self.item_value() @@ -546,7 +545,7 @@ class IntegerWidget(QtWidgets.QWidget, InputObject): if self._as_widget: property_name = "input-state" - widget = self.int_input + widget = self.input_field else: property_name = "state" widget = self.label_widget @@ -555,129 +554,7 @@ class IntegerWidget(QtWidgets.QWidget, InputObject): widget.style().polish(widget) def item_value(self): - return self.int_input.value() - - -class FloatWidget(QtWidgets.QWidget, InputObject): - value_changed = QtCore.Signal(object) - input_modifiers = ("minimum", "maximum", "decimal") - - def __init__( - self, input_data, values, parent_keys, parent, label_widget=None - ): - self._parent = parent - self._as_widget = values is AS_WIDGET - - self._is_group = input_data.get("is_group", False) - self._is_nullable = input_data.get("is_nullable", False) - self.default_value = input_data.get("default", NOT_SET) - - self._state = None - - super(FloatWidget, self).__init__(parent) - - layout = QtWidgets.QHBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(5) - - kwargs = { - modifier: input_data.get(modifier) - for modifier in self.input_modifiers - if input_data.get(modifier) - } - self.float_input = ModifiedFloatSpinBox(self, **kwargs) - - self.setFocusProxy(self.float_input) - - decimals = input_data.get("decimals", 5) - maximum = input_data.get("maximum") - minimum = input_data.get("minimum") - - self.float_input.setDecimals(decimals) - if maximum is not None: - self.float_input.setMaximum(float(maximum)) - if minimum is not None: - self.float_input.setMinimum(float(minimum)) - - if not self._as_widget and not label_widget: - label = input_data["label"] - label_widget = QtWidgets.QLabel(label) - layout.addWidget(label_widget, 0) - layout.addWidget(self.float_input, 1) - - if not self._as_widget: - self.label_widget = label_widget - - self.key = input_data["key"] - keys = list(parent_keys) - keys.append(self.key) - self.keys = keys - - self.update_global_values(values) - - self.override_value = NOT_SET - - self.float_input.valueChanged.connect(self._on_value_change) - - def update_global_values(self, values): - value = NOT_SET - if not self._as_widget: - value = self.value_from_values(values) - if value is not NOT_SET: - self.float_input.setValue(value) - - elif self.default_value is not NOT_SET: - self.float_input.setValue(self.default_value) - - self.global_value = value - self.start_value = self.item_value() - - self._is_modified = self.global_value != self.start_value - - def set_value(self, value, *, global_value=False): - self.float_input.setValue(value) - if global_value: - self.start_value = self.item_value() - self.global_value = self.item_value() - self._on_value_change() - - def reset_value(self): - self.set_value(self.global_value) - - def clear_value(self): - self.set_value(0) - - def _on_value_change(self, item=None): - if self.ignore_value_changes: - return - - self._is_modified = self.item_value() != self.global_value - if self.is_overidable: - self._is_overriden = True - - self.update_style() - - self.value_changed.emit(self) - - def update_style(self): - state = self.style_state( - self.is_invalid, self.is_overriden, self.is_modified - ) - if self._state == state: - return - - if self._as_widget: - property_name = "input-state" - widget = self.float_input - else: - property_name = "state" - widget = self.label_widget - - widget.setProperty(property_name, state) - widget.style().polish(widget) - - def item_value(self): - return self.float_input.value() + return self.input_field.value() class TextSingleLineWidget(QtWidgets.QWidget, InputObject): @@ -2549,8 +2426,7 @@ TypeToKlass.types["path-input"] = PathInputWidget TypeToKlass.types["path-widget"] = PathWidget TypeToKlass.types["text-multiline"] = TextMultiLineWidget TypeToKlass.types["raw-json"] = RawJsonWidget -TypeToKlass.types["int"] = IntegerWidget -TypeToKlass.types["float"] = FloatWidget +TypeToKlass.types["number"] = NumberWidget TypeToKlass.types["dict-modifiable"] = ModifiableDict TypeToKlass.types["dict"] = DictWidget TypeToKlass.types["dict-form"] = DictFormWidget From d4a3a2ec17ed9fa87f8ddea823451d413432c513 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 2 Sep 2020 11:56:06 +0200 Subject: [PATCH 354/813] changed order in boolean input --- .../config_setting/widgets/inputs.py | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 3e86b864b4..0d2ff628ce 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -351,22 +351,20 @@ class BooleanWidget(QtWidgets.QWidget, InputObject): layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(5) - self.checkbox = QtWidgets.QCheckBox(self) - - self.setFocusProxy(self.checkbox) - - self.checkbox.setAttribute(QtCore.Qt.WA_StyledBackground) - if not self._as_widget and not label_widget: - label = input_data["label"] - label_widget = QtWidgets.QLabel(label) - label_widget.setAttribute(QtCore.Qt.WA_StyledBackground) - layout.addWidget(label_widget, 0) - - layout.addWidget(self.checkbox, 1) - if not self._as_widget: + if not label_widget: + label = input_data["label"] + label_widget = QtWidgets.QLabel(label) + label_widget.setAttribute(QtCore.Qt.WA_StyledBackground) + layout.addWidget(label_widget, 0) self.label_widget = label_widget + self.checkbox = QtWidgets.QCheckBox(self) + self.checkbox.setAttribute(QtCore.Qt.WA_StyledBackground) + layout.addWidget(self.checkbox, 1) + self.setFocusProxy(self.checkbox) + + if not self._as_widget: self.key = input_data["key"] keys = list(parent_keys) keys.append(self.key) From e79b051b59330027e1080cf648f10dfa55766d21 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 2 Sep 2020 12:43:24 +0200 Subject: [PATCH 355/813] cleanup anatomy input imports --- .../config_setting/widgets/anatomy_inputs.py | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index addd0d6666..feae91ba99 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -1,13 +1,6 @@ -import json -import logging -from Qt import QtWidgets, QtCore, QtGui -from .widgets import ( - ExpandingWidget, - ModifiedIntSpinBox, - ModifiedFloatSpinBox -) -from .inputs import ConfigObject, InputObject -from .lib import NOT_SET, AS_WIDGET, METADATA_KEY, TypeToKlass +from Qt import QtWidgets, QtCore +from .inputs import InputObject +from .lib import NOT_SET, AS_WIDGET, TypeToKlass class AnatomyWidget(QtWidgets.QWidget, InputObject): From 0ceb697c21be236711e179dcd15f90d8cea522a9 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 2 Sep 2020 12:45:52 +0200 Subject: [PATCH 356/813] changed way how global values a propagated to inputs --- .../config_setting/widgets/base.py | 8 +- .../config_setting/widgets/inputs.py | 361 ++++++++---------- 2 files changed, 164 insertions(+), 205 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/base.py b/pype/tools/config_setting/config_setting/widgets/base.py index 9c1fee5b6f..cd459aa674 100644 --- a/pype/tools/config_setting/config_setting/widgets/base.py +++ b/pype/tools/config_setting/config_setting/widgets/base.py @@ -92,10 +92,10 @@ class StudioWidget(QtWidgets.QWidget): widget.deleteLater() self.input_fields.clear() - values = {"studio": config.studio_configurations()} self.schema = lib.gui_schema("studio_schema", "0_studio_gui_schema") self.keys = self.schema.get("keys", []) - self.add_children_gui(self.schema, values) + self.add_children_gui(self.schema, lib.NOT_SET) + self._update_global_values() self.hierarchical_style_update() def _save(self): @@ -404,10 +404,10 @@ class ProjectWidget(QtWidgets.QWidget): input_field.hierarchical_style_update() def reset(self): - values = {"project": config.global_project_configurations()} self.schema = lib.gui_schema("projects_schema", "0_project_gui_schema") self.keys = self.schema.get("keys", []) - self.add_children_gui(self.schema, values) + self.add_children_gui(self.schema, lib.NOT_SET) + self._update_global_values() self.hierarchical_style_update() def add_children_gui(self, child_configuration, values): diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 0d2ff628ce..5f5747fd71 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -120,26 +120,6 @@ class ConfigObject: """Output for saving changes or overrides.""" return {self.key: self.item_value()} - def value_from_values(self, values, keys=None): - """Global getter of value based on loaded values.""" - if not values or values is AS_WIDGET: - return NOT_SET - - if keys is None: - keys = self.keys - - value = values - for key in keys: - if not isinstance(value, dict): - raise TypeError( - "Expected dictionary got {}.".format(str(type(value))) - ) - - if key not in value: - return NOT_SET - value = value[key] - return value - def style_state(self, is_invalid, is_overriden, is_modified): items = [] if is_invalid: @@ -336,22 +316,26 @@ class BooleanWidget(QtWidgets.QWidget, InputObject): def __init__( self, input_data, values, parent_keys, parent, label_widget=None ): + super(BooleanWidget, self).__init__(parent) + self._parent = parent self._as_widget = values is AS_WIDGET + self._state = None self._is_group = input_data.get("is_group", False) self._is_nullable = input_data.get("is_nullable", False) self.default_value = input_data.get("default", NOT_SET) - self._state = None - - super(BooleanWidget, self).__init__(parent) + self.override_value = NOT_SET + self.global_value = NOT_SET + self.start_value = NOT_SET layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(5) if not self._as_widget: + self.key = input_data["key"] if not label_widget: label = input_data["label"] label_widget = QtWidgets.QLabel(label) @@ -364,21 +348,14 @@ class BooleanWidget(QtWidgets.QWidget, InputObject): layout.addWidget(self.checkbox, 1) self.setFocusProxy(self.checkbox) - if not self._as_widget: - self.key = input_data["key"] - keys = list(parent_keys) - keys.append(self.key) - self.keys = keys - - self.update_global_values(values) - self.override_value = NOT_SET - self.checkbox.stateChanged.connect(self._on_value_change) - def update_global_values(self, values): + def update_global_values(self, parent_values): value = NOT_SET if not self._as_widget: - value = self.value_from_values(values) + if parent_values is not NOT_SET: + value = parent_values.get(self.key, NOT_SET) + if value is not NOT_SET: self.checkbox.setChecked(value) @@ -454,12 +431,15 @@ class NumberWidget(QtWidgets.QWidget, InputObject): self._parent = parent self._as_widget = values is AS_WIDGET + self._state = None self._is_group = input_data.get("is_group", False) self._is_nullable = input_data.get("is_nullable", False) self.default_value = input_data.get("default", NOT_SET) - self._state = None + self.override_value = NOT_SET + self.global_value = NOT_SET + self.start_value = NOT_SET layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) @@ -474,30 +454,24 @@ class NumberWidget(QtWidgets.QWidget, InputObject): self.setFocusProxy(self.input_field) - if not self._as_widget and not label_widget: - label = input_data["label"] - label_widget = QtWidgets.QLabel(label) - layout.addWidget(label_widget, 0) - layout.addWidget(self.input_field, 1) - if not self._as_widget: + self.key = input_data["key"] + if not label_widget: + label = input_data["label"] + label_widget = QtWidgets.QLabel(label) + layout.addWidget(label_widget, 0) self.label_widget = label_widget - self.key = input_data["key"] - keys = list(parent_keys) - keys.append(self.key) - self.keys = keys - - self.update_global_values(values) - - self.override_value = NOT_SET + layout.addWidget(self.input_field, 1) self.input_field.valueChanged.connect(self._on_value_change) - def update_global_values(self, values): + def update_global_values(self, parent_values): value = NOT_SET if not self._as_widget: - value = self.value_from_values(values) + if parent_values is not NOT_SET: + value = parent_values.get(self.key, NOT_SET) + if value is not NOT_SET: self.input_field.setValue(value) @@ -561,16 +535,19 @@ class TextSingleLineWidget(QtWidgets.QWidget, InputObject): def __init__( self, input_data, values, parent_keys, parent, label_widget=None ): + super(TextSingleLineWidget, self).__init__(parent) + self._parent = parent self._as_widget = values is AS_WIDGET + self._state = None self._is_group = input_data.get("is_group", False) self._is_nullable = input_data.get("is_nullable", False) self.default_value = input_data.get("default", NOT_SET) - self._state = None - - super(TextSingleLineWidget, self).__init__(parent) + self.override_value = NOT_SET + self.global_value = NOT_SET + self.start_value = NOT_SET layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) @@ -580,30 +557,24 @@ class TextSingleLineWidget(QtWidgets.QWidget, InputObject): self.setFocusProxy(self.text_input) - if not self._as_widget and not label_widget: - label = input_data["label"] - label_widget = QtWidgets.QLabel(label) - layout.addWidget(label_widget, 0) - layout.addWidget(self.text_input, 1) - if not self._as_widget: + self.key = input_data["key"] + if not label_widget: + label = input_data["label"] + label_widget = QtWidgets.QLabel(label) + layout.addWidget(label_widget, 0) self.label_widget = label_widget - self.key = input_data["key"] - keys = list(parent_keys) - keys.append(self.key) - self.keys = keys - - self.update_global_values(values) - - self.override_value = NOT_SET + layout.addWidget(self.text_input, 1) self.text_input.textChanged.connect(self._on_value_change) - def update_global_values(self, values): + def update_global_values(self, parent_values): value = NOT_SET if not self._as_widget: - value = self.value_from_values(values) + if parent_values is not NOT_SET: + value = parent_values.get(self.key, NOT_SET) + if value is not NOT_SET: self.text_input.setText(value) @@ -667,16 +638,19 @@ class TextMultiLineWidget(QtWidgets.QWidget, InputObject): def __init__( self, input_data, values, parent_keys, parent, label_widget=None ): + super(TextMultiLineWidget, self).__init__(parent) + self._parent = parent self._as_widget = values is AS_WIDGET + self._state = None self._is_group = input_data.get("is_group", False) self._is_nullable = input_data.get("is_nullable", False) self.default_value = input_data.get("default", NOT_SET) - self._state = None - - super(TextMultiLineWidget, self).__init__(parent) + self.override_value = NOT_SET + self.global_value = NOT_SET + self.start_value = NOT_SET layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) @@ -686,30 +660,23 @@ class TextMultiLineWidget(QtWidgets.QWidget, InputObject): self.setFocusProxy(self.text_input) - if not self._as_widget and not label_widget: - label = input_data["label"] - label_widget = QtWidgets.QLabel(label) - layout.addWidget(label_widget, 0) - layout.addWidget(self.text_input, 1) - if not self._as_widget: - self.label_widget = label_widget - self.key = input_data["key"] - keys = list(parent_keys) - keys.append(self.key) - self.keys = keys - - self.update_global_values(values) - - self.override_value = NOT_SET + if not label_widget: + label = input_data["label"] + label_widget = QtWidgets.QLabel(label) + layout.addWidget(label_widget, 0) + self.label_widget = label_widget + layout.addWidget(self.text_input, 1) self.text_input.textChanged.connect(self._on_value_change) - def update_global_values(self, values): + def update_global_values(self, parent_values): value = NOT_SET if not self._as_widget: - value = self.value_from_values(values) + if parent_values is not NOT_SET: + value = parent_values.get(self.key, NOT_SET) + if value is not NOT_SET: self.text_input.setPlainText(value) @@ -825,8 +792,11 @@ class RawJsonWidget(QtWidgets.QWidget, InputObject): def __init__( self, input_data, values, parent_keys, parent, label_widget=None ): + super(RawJsonWidget, self).__init__(parent) + self._parent = parent self._as_widget = values is AS_WIDGET + self._state = None any_parent_is_group = parent.is_group if not any_parent_is_group: @@ -838,9 +808,9 @@ class RawJsonWidget(QtWidgets.QWidget, InputObject): self._is_nullable = input_data.get("is_nullable", False) self.default_value = input_data.get("default", NOT_SET) - self._state = None - - super(RawJsonWidget, self).__init__(parent) + self.override_value = NOT_SET + self.global_value = NOT_SET + self.start_value = NOT_SET layout = QtWidgets.QVBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) @@ -854,30 +824,23 @@ class RawJsonWidget(QtWidgets.QWidget, InputObject): self.setFocusProxy(self.text_input) - if not self._as_widget and not label_widget: - label = input_data["label"] - label_widget = QtWidgets.QLabel(label) - layout.addWidget(label_widget, 0) - layout.addWidget(self.text_input, 1) - - self.override_value = NOT_SET - if not self._as_widget: - self.label_widget = label_widget - self.key = input_data["key"] - keys = list(parent_keys) - keys.append(self.key) - self.keys = keys - - self.update_global_values(values) + if not label_widget: + label = input_data["label"] + label_widget = QtWidgets.QLabel(label) + layout.addWidget(label_widget, 0) + self.label_widget = label_widget + layout.addWidget(self.text_input, 1) self.text_input.textChanged.connect(self._on_value_change) - def update_global_values(self, values): + def update_global_values(self, parent_values): value = NOT_SET if not self._as_widget: - value = self.value_from_values(values) + if parent_values is not NOT_SET: + value = parent_values.get(self.key, NOT_SET) + if value is not NOT_SET: self.text_input.set_value(value) @@ -1030,12 +993,12 @@ class ListWidget(QtWidgets.QWidget, InputObject): def __init__( self, input_data, values, parent_keys, parent, label_widget=None ): - self._parent = parent - super(ListWidget, self).__init__(parent) self.setObjectName("ListWidget") + self._parent = parent self._state = None + self._is_group = input_data.get("is_group", False) self._is_nullable = input_data.get("is_nullable", False) @@ -1043,18 +1006,14 @@ class ListWidget(QtWidgets.QWidget, InputObject): self.default_value = input_data.get("default", NOT_SET) self.input_modifiers = input_data.get("input_modifiers") or {} + self.override_value = NOT_SET + self.global_value = NOT_SET + self.start_value = NOT_SET + + self.key = input_data["key"] + self.input_fields = [] - inputs_widget = QtWidgets.QWidget(self) - inputs_widget.setAttribute(QtCore.Qt.WA_StyledBackground) - - inputs_layout = QtWidgets.QVBoxLayout(inputs_widget) - inputs_layout.setContentsMargins(0, 5, 0, 5) - inputs_layout.setSpacing(3) - - self.inputs_widget = inputs_widget - self.inputs_layout = inputs_layout - layout = QtWidgets.QVBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) @@ -1063,19 +1022,18 @@ class ListWidget(QtWidgets.QWidget, InputObject): label = input_data["label"] label_widget = QtWidgets.QLabel(label) layout.addWidget(label_widget) - self.label_widget = label_widget + inputs_widget = QtWidgets.QWidget(self) + inputs_widget.setAttribute(QtCore.Qt.WA_StyledBackground) layout.addWidget(inputs_widget) - self.key = input_data["key"] - keys = list(parent_keys) - keys.append(self.key) - self.keys = keys + inputs_layout = QtWidgets.QVBoxLayout(inputs_widget) + inputs_layout.setContentsMargins(0, 5, 0, 5) + inputs_layout.setSpacing(3) - self.update_global_values(values) - - self.override_value = NOT_SET + self.inputs_widget = inputs_widget + self.inputs_layout = inputs_layout def count(self): return len(self.input_fields) @@ -1086,10 +1044,12 @@ class ListWidget(QtWidgets.QWidget, InputObject): def clear_value(self): self.set_value([]) - def update_global_values(self, values): + def update_global_values(self, parent_values): old_inputs = tuple(self.input_fields) - value = self.value_from_values(values) + value = NOT_SET + if parent_values is not NOT_SET: + value = parent_values.get(self.key, NOT_SET) self.global_value = value @@ -1227,10 +1187,10 @@ class ModifiableDictItem(QtWidgets.QWidget, ConfigObject): value_changed = QtCore.Signal(object) def __init__(self, object_type, input_modifiers, config_parent, parent): - self._parent = config_parent - super(ModifiableDictItem, self).__init__(parent) + self._parent = config_parent + layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(3) @@ -1344,13 +1304,18 @@ class ModifiableDict(ExpandingWidget, InputObject): self, input_data, values, parent_keys, parent, label_widget=None ): - self._parent = parent - super(ModifiableDict, self).__init__(input_data["label"], parent) self.setObjectName("ModifiableDict") + self._parent = parent self._state = None + self.override_value = NOT_SET + self.global_value = NOT_SET + self.start_value = NOT_SET + + self.key = input_data["key"] + self.input_fields = [] any_parent_is_group = parent.is_group @@ -1378,22 +1343,15 @@ class ModifiableDict(ExpandingWidget, InputObject): self.default_value = input_data.get("default", NOT_SET) self.input_modifiers = input_data.get("input_modifiers") or {} - self.key = input_data["key"] - keys = list(parent_keys) - keys.append(self.key) - self.keys = keys - - self.override_value = NOT_SET - - self.update_global_values(values) - def count(self): return len(self.input_fields) - def update_global_values(self, values): + def update_global_values(self, parent_values): old_inputs = tuple(self.input_fields) - value = self.value_from_values(values) + value = NOT_SET + if parent_values is not NOT_SET: + value = parent_values.get(self.key, NOT_SET) self.global_value = value @@ -1566,9 +1524,6 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): self.input_fields = [] self.key = input_data["key"] - keys = list(parent_keys) - keys.append(self.key) - self.keys = keys main_layout = QtWidgets.QHBoxLayout(self) main_layout.setContentsMargins(5, 5, 0, 5) @@ -1596,7 +1551,7 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): self.checkbox_key = input_data.get("checkbox_key") for child_data in input_data.get("children", []): - self.add_children_gui(child_data, values) + self.add_children_gui(child_data) expandable = input_data.get("expandable", True) if len(self.input_fields) == 1 and self.checkbox_widget: @@ -1634,9 +1589,13 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): for item in self.input_fields: item.set_as_overriden() - def update_global_values(self, values): + def update_global_values(self, parent_values): + value = NOT_SET + if parent_values is not NOT_SET: + value = parent_values.get(self.key, NOT_SET) + for item in self.input_fields: - item.update_global_values(values) + item.update_global_values(value) def apply_overrides(self, parent_values): # Make sure this is set to False @@ -1749,16 +1708,16 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): output.update(input_field.config_value()) return output - def add_children_gui(self, child_configuration, values): + def add_children_gui(self, child_configuration): item_type = child_configuration["type"] klass = TypeToKlass.types.get(item_type) if self.checkbox_key and not self.checkbox_widget: key = child_configuration.get("key") if key == self.checkbox_key: - return self._add_checkbox_child(child_configuration, values) + return self._add_checkbox_child(child_configuration) item = klass( - child_configuration, values, self.keys, self + child_configuration, NOT_SET, self.keys, self ) item.value_changed.connect(self._on_value_change) self.content_layout.addWidget(item) @@ -1766,9 +1725,9 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): self.input_fields.append(item) return item - def _add_checkbox_child(self, child_configuration, values): + def _add_checkbox_child(self, child_configuration): item = BooleanWidget( - child_configuration, values, self.keys, self, self.label_widget + child_configuration, NOT_SET, self.keys, self, self.label_widget ) item.value_changed.connect(self._on_value_change) @@ -1823,11 +1782,9 @@ class DictInvisible(QtWidgets.QWidget, ConfigObject): self.input_fields = [] self.key = input_data["key"] - self.keys = list(parent_keys) - self.keys.append(self.key) for child_data in input_data.get("children", []): - self.add_children_gui(child_data, values) + self.add_children_gui(child_data) def update_style(self, *args, **kwargs): return @@ -1867,12 +1824,12 @@ class DictInvisible(QtWidgets.QWidget, ConfigObject): output.update(input_field.config_value()) return output - def add_children_gui(self, child_configuration, values): + def add_children_gui(self, child_configuration): item_type = child_configuration["type"] klass = TypeToKlass.types.get(item_type) item = klass( - child_configuration, values, self.keys, self + child_configuration, NOT_SET, self.keys, self ) self.layout().addWidget(item) @@ -1922,9 +1879,13 @@ class DictInvisible(QtWidgets.QWidget, ConfigObject): for item in self.input_fields: item.set_as_overriden() - def update_global_values(self, values): + def update_global_values(self, parent_values): + value = NOT_SET + if parent_values is not NOT_SET: + value = parent_values.get(self.key, NOT_SET) + for item in self.input_fields: - item.update_global_values(values) + item.update_global_values(value) def apply_overrides(self, parent_values): # Make sure this is set to False @@ -2001,7 +1962,7 @@ class DictFormWidget(QtWidgets.QWidget, ConfigObject): self.keys = list(parent_keys) for child_data in input_data.get("children", []): - self.add_children_gui(child_data, values) + self.add_children_gui(child_data) def mouseReleaseEvent(self, event): if event.button() == QtCore.Qt.RightButton: @@ -2042,9 +2003,9 @@ class DictFormWidget(QtWidgets.QWidget, ConfigObject): for item in self.input_fields: item.set_as_overriden() - def update_global_values(self, values): + def update_global_values(self, value): for item in self.input_fields: - item.update_global_values(values) + item.update_global_values(value) def _on_value_change(self, item=None): if self.ignore_value_changes: @@ -2080,7 +2041,7 @@ class DictFormWidget(QtWidgets.QWidget, ConfigObject): output.extend(input_field.get_invalid()) return output - def add_children_gui(self, child_configuration, values): + def add_children_gui(self, child_configuration): item_type = child_configuration["type"] # Pop label to not be set in child label = child_configuration["label"] @@ -2090,7 +2051,7 @@ class DictFormWidget(QtWidgets.QWidget, ConfigObject): label_widget = FormLabel(label, self) item = klass( - child_configuration, values, self.keys, self, label_widget + child_configuration, NOT_SET, self.keys, self, label_widget ) label_widget.item = item @@ -2137,16 +2098,19 @@ class PathInputWidget(QtWidgets.QWidget, InputObject): def __init__( self, input_data, values, parent_keys, parent, label_widget=None ): + super(PathInputWidget, self).__init__(parent) + self._parent = parent self._as_widget = values is AS_WIDGET + self._state = None self._is_group = input_data.get("is_group", False) self._is_nullable = input_data.get("is_nullable", False) self.default_value = input_data.get("default", NOT_SET) - self._state = None - - super(PathInputWidget, self).__init__(parent) + self.override_value = NOT_SET + self.global_value = NOT_SET + self.start_value = NOT_SET layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) @@ -2156,30 +2120,23 @@ class PathInputWidget(QtWidgets.QWidget, InputObject): self.setFocusProxy(self.path_input) - if not self._as_widget and not label_widget: - label = input_data["label"] - label_widget = QtWidgets.QLabel(label) - layout.addWidget(label_widget, 0) - layout.addWidget(self.path_input, 1) - if not self._as_widget: - self.label_widget = label_widget - self.key = input_data["key"] - keys = list(parent_keys) - keys.append(self.key) - self.keys = keys - - self.update_global_values(values) - - self.override_value = NOT_SET + if not label_widget: + label = input_data["label"] + label_widget = QtWidgets.QLabel(label) + layout.addWidget(label_widget, 0) + self.label_widget = label_widget + layout.addWidget(self.path_input, 1) self.path_input.textChanged.connect(self._on_value_change) - def update_global_values(self, values): + def update_global_values(self, parent_values): value = NOT_SET if not self._as_widget: - value = self.value_from_values(values) + if parent_values is not NOT_SET: + value = parent_values.get(self.key, NOT_SET) + if value is not NOT_SET: self.path_input.setText(value) @@ -2305,15 +2262,13 @@ class PathWidget(QtWidgets.QWidget, InputObject): layout.addWidget(self.content_widget) - self.create_gui(values) + self.create_gui() - self.update_global_values(values) - - def create_gui(self, values): + def create_gui(self): if not self.multiplatform and not self.multipath: input_data = {"key": self.key} path_input = PathInputWidget( - input_data, values, self.keys, self, self.label_widget + input_data, NOT_SET, self.keys, self, self.label_widget ) self.setFocusProxy(path_input) self.content_layout.addWidget(path_input) @@ -2327,7 +2282,7 @@ class PathWidget(QtWidgets.QWidget, InputObject): if not self.multiplatform: input_data_for_list["key"] = self.key input_widget = ListWidget( - input_data_for_list, values, self.keys, self, self.label_widget + input_data_for_list, NOT_SET, self.keys, self, self.label_widget ) self.setFocusProxy(input_widget) self.content_layout.addWidget(input_widget) @@ -2343,12 +2298,12 @@ class PathWidget(QtWidgets.QWidget, InputObject): if self.multipath: input_data_for_list["key"] = platform_key input_widget = ListWidget( - input_data_for_list, values, self.keys, self, label_widget + input_data_for_list, NOT_SET, self.keys, self, label_widget ) else: input_data = {"key": platform_key} input_widget = PathInputWidget( - input_data, values, self.keys, self, label_widget + input_data, NOT_SET, self.keys, self, label_widget ) proxy_layout.addRow(label_widget, input_widget) self.input_fields.append(input_widget) @@ -2357,11 +2312,15 @@ class PathWidget(QtWidgets.QWidget, InputObject): self.setFocusProxy(self.input_fields[0]) self.content_layout.addWidget(proxy_widget) - def update_global_values(self, values): - print(self.__class__.__name__, "* TODO implement `update_global_values`") + def update_global_values(self, parent_values): + print( + self.__class__.__name__, "* TODO implement `update_global_values`" + ) value = NOT_SET if not self._as_widget: - value = self.value_from_values(values) + if parent_values is not NOT_SET: + value = parent_values.get(self.key, NOT_SET) + if value is not NOT_SET: self.text_input.setText(value) From 370705a10a286770c1eb67994da67d2495762496 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 2 Sep 2020 13:07:13 +0200 Subject: [PATCH 357/813] removed AS_WIDGET constant and reduced input args --- .../config_setting/widgets/anatomy_inputs.py | 6 +- .../config_setting/widgets/base.py | 16 +- .../config_setting/widgets/inputs.py | 193 ++++++++---------- .../config_setting/widgets/lib.py | 1 - 4 files changed, 95 insertions(+), 121 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index feae91ba99..7c9ac0d8f4 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -1,16 +1,16 @@ from Qt import QtWidgets, QtCore from .inputs import InputObject -from .lib import NOT_SET, AS_WIDGET, TypeToKlass +from .lib import NOT_SET, TypeToKlass class AnatomyWidget(QtWidgets.QWidget, InputObject): value_changed = QtCore.Signal(object) def __init__( - self, input_data, values, parent_keys, parent, label_widget=None + self, input_data, parent, as_widget=False, label_widget=None ): self._parent = parent - self._as_widget = values is AS_WIDGET + self._as_widget = as_widget self._is_group = True self._state = None diff --git a/pype/tools/config_setting/config_setting/widgets/base.py b/pype/tools/config_setting/config_setting/widgets/base.py index cd459aa674..f057f5a0f6 100644 --- a/pype/tools/config_setting/config_setting/widgets/base.py +++ b/pype/tools/config_setting/config_setting/widgets/base.py @@ -94,7 +94,7 @@ class StudioWidget(QtWidgets.QWidget): self.schema = lib.gui_schema("studio_schema", "0_studio_gui_schema") self.keys = self.schema.get("keys", []) - self.add_children_gui(self.schema, lib.NOT_SET) + self.add_children_gui(self.schema) self._update_global_values() self.hierarchical_style_update() @@ -175,12 +175,10 @@ class StudioWidget(QtWidgets.QWidget): for input_field in self.input_fields: input_field.hierarchical_style_update() - def add_children_gui(self, child_configuration, values): + def add_children_gui(self, child_configuration): item_type = child_configuration["type"] klass = lib.TypeToKlass.types.get(item_type) - item = klass( - child_configuration, values, self.keys, self - ) + item = klass(child_configuration, self) self.input_fields.append(item) self.content_layout.addWidget(item) @@ -406,16 +404,14 @@ class ProjectWidget(QtWidgets.QWidget): def reset(self): self.schema = lib.gui_schema("projects_schema", "0_project_gui_schema") self.keys = self.schema.get("keys", []) - self.add_children_gui(self.schema, lib.NOT_SET) + self.add_children_gui(self.schema) self._update_global_values() self.hierarchical_style_update() - def add_children_gui(self, child_configuration, values): + def add_children_gui(self, child_configuration): item_type = child_configuration["type"] klass = lib.TypeToKlass.types.get(item_type) - item = klass( - child_configuration, values, self.keys, self - ) + item = klass(child_configuration, self) self.input_fields.append(item) self.content_layout.addWidget(item) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 5f5747fd71..336a8ef93e 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -6,7 +6,7 @@ from .widgets import ( NumberSpinBox, PathInput ) -from .lib import NOT_SET, AS_WIDGET, METADATA_KEY, TypeToKlass +from .lib import NOT_SET, METADATA_KEY, TypeToKlass class ConfigObject: @@ -314,12 +314,12 @@ class BooleanWidget(QtWidgets.QWidget, InputObject): value_changed = QtCore.Signal(object) def __init__( - self, input_data, values, parent_keys, parent, label_widget=None + self, input_data, parent, as_widget=False, label_widget=None ): super(BooleanWidget, self).__init__(parent) self._parent = parent - self._as_widget = values is AS_WIDGET + self._as_widget = as_widget self._state = None self._is_group = input_data.get("is_group", False) @@ -334,7 +334,7 @@ class BooleanWidget(QtWidgets.QWidget, InputObject): layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(5) - if not self._as_widget: + if not as_widget: self.key = input_data["key"] if not label_widget: label = input_data["label"] @@ -425,12 +425,12 @@ class NumberWidget(QtWidgets.QWidget, InputObject): input_modifiers = ("minimum", "maximum", "decimal") def __init__( - self, input_data, values, parent_keys, parent, label_widget=None + self, input_data, parent, as_widget=False, label_widget=None ): super(NumberWidget, self).__init__(parent) self._parent = parent - self._as_widget = values is AS_WIDGET + self._as_widget = as_widget self._state = None self._is_group = input_data.get("is_group", False) @@ -533,12 +533,12 @@ class TextSingleLineWidget(QtWidgets.QWidget, InputObject): value_changed = QtCore.Signal(object) def __init__( - self, input_data, values, parent_keys, parent, label_widget=None + self, input_data, parent, as_widget=False, label_widget=None ): super(TextSingleLineWidget, self).__init__(parent) self._parent = parent - self._as_widget = values is AS_WIDGET + self._as_widget = as_widget self._state = None self._is_group = input_data.get("is_group", False) @@ -636,12 +636,12 @@ class TextMultiLineWidget(QtWidgets.QWidget, InputObject): value_changed = QtCore.Signal(object) def __init__( - self, input_data, values, parent_keys, parent, label_widget=None + self, input_data, parent, as_widget=False, label_widget=None ): super(TextMultiLineWidget, self).__init__(parent) self._parent = parent - self._as_widget = values is AS_WIDGET + self._as_widget = as_widget self._state = None self._is_group = input_data.get("is_group", False) @@ -790,12 +790,12 @@ class RawJsonWidget(QtWidgets.QWidget, InputObject): value_changed = QtCore.Signal(object) def __init__( - self, input_data, values, parent_keys, parent, label_widget=None + self, input_data, parent, as_widget=False, label_widget=None ): super(RawJsonWidget, self).__init__(parent) self._parent = parent - self._as_widget = values is AS_WIDGET + self._as_widget = as_widget self._state = None any_parent_is_group = parent.is_group @@ -944,10 +944,9 @@ class ListItem(QtWidgets.QWidget, ConfigObject): ItemKlass = TypeToKlass.types[object_type] self.value_input = ItemKlass( input_modifiers, - AS_WIDGET, - [], self, - None + as_widget=True, + label_widget=None ) layout.addWidget(self.value_input, 1) @@ -991,7 +990,7 @@ class ListWidget(QtWidgets.QWidget, InputObject): value_changed = QtCore.Signal(object) def __init__( - self, input_data, values, parent_keys, parent, label_widget=None + self, input_data, parent, as_widget=False, label_widget=None ): super(ListWidget, self).__init__(parent) self.setObjectName("ListWidget") @@ -1202,10 +1201,9 @@ class ModifiableDictItem(QtWidgets.QWidget, ConfigObject): self.value_input = ItemKlass( input_modifiers, - AS_WIDGET, - [], self, - None + as_widget=True, + label_widget=None ) self.add_btn = QtWidgets.QPushButton("+") self.remove_btn = QtWidgets.QPushButton("-") @@ -1301,8 +1299,7 @@ class ModifiableDict(ExpandingWidget, InputObject): value_changed = QtCore.Signal(object) def __init__( - self, input_data, values, parent_keys, parent, - label_widget=None + self, input_data, parent, as_widget=False, label_widget=None ): super(ModifiableDict, self).__init__(input_data["label"], parent) self.setObjectName("ModifiableDict") @@ -1498,9 +1495,9 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): value_changed = QtCore.Signal(object) def __init__( - self, input_data, values, parent_keys, parent, label_widget=None + self, input_data, parent, as_widget=False, label_widget=None ): - if values is AS_WIDGET: + if as_widget: raise TypeError("Can't use \"{}\" as widget item.".format( self.__class__.__name__ )) @@ -1564,6 +1561,32 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): else: body_widget.hide_toolbox(hide_content=False) + def add_children_gui(self, child_configuration): + item_type = child_configuration["type"] + klass = TypeToKlass.types.get(item_type) + if self.checkbox_key and not self.checkbox_widget: + key = child_configuration.get("key") + if key == self.checkbox_key: + return self._add_checkbox_child(child_configuration) + + item = klass(child_configuration, self) + item.value_changed.connect(self._on_value_change) + self.content_layout.addWidget(item) + + self.input_fields.append(item) + return item + + def _add_checkbox_child(self, child_configuration): + item = BooleanWidget( + child_configuration, self, label_widget=self.label_widget + ) + item.value_changed.connect(self._on_value_change) + + self.body_widget.top_part.layout().addWidget(item) + self.checkbox_widget = item + self.input_fields.append(item) + return item + def remove_overrides(self): self._is_overriden = False self._is_modified = False @@ -1708,34 +1731,6 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): output.update(input_field.config_value()) return output - def add_children_gui(self, child_configuration): - item_type = child_configuration["type"] - klass = TypeToKlass.types.get(item_type) - if self.checkbox_key and not self.checkbox_widget: - key = child_configuration.get("key") - if key == self.checkbox_key: - return self._add_checkbox_child(child_configuration) - - item = klass( - child_configuration, NOT_SET, self.keys, self - ) - item.value_changed.connect(self._on_value_change) - self.content_layout.addWidget(item) - - self.input_fields.append(item) - return item - - def _add_checkbox_child(self, child_configuration): - item = BooleanWidget( - child_configuration, NOT_SET, self.keys, self, self.label_widget - ) - item.value_changed.connect(self._on_value_change) - - self.body_widget.top_part.layout().addWidget(item) - self.checkbox_widget = item - self.input_fields.append(item) - return item - def overrides(self): if not self.is_overriden and not self.child_overriden: return NOT_SET, False @@ -1759,7 +1754,7 @@ class DictInvisible(QtWidgets.QWidget, ConfigObject): allow_actions = False def __init__( - self, input_data, values, parent_keys, parent, label_widget=None + self, input_data, parent, as_widget=False, label_widget=None ): self._parent = parent @@ -1786,6 +1781,18 @@ class DictInvisible(QtWidgets.QWidget, ConfigObject): for child_data in input_data.get("children", []): self.add_children_gui(child_data) + def add_children_gui(self, child_configuration): + item_type = child_configuration["type"] + klass = TypeToKlass.types.get(item_type) + + item = klass(child_configuration, self) + self.layout().addWidget(item) + + item.value_changed.connect(self._on_value_change) + + self.input_fields.append(item) + return item + def update_style(self, *args, **kwargs): return @@ -1824,20 +1831,6 @@ class DictInvisible(QtWidgets.QWidget, ConfigObject): output.update(input_field.config_value()) return output - def add_children_gui(self, child_configuration): - item_type = child_configuration["type"] - klass = TypeToKlass.types.get(item_type) - - item = klass( - child_configuration, NOT_SET, self.keys, self - ) - self.layout().addWidget(item) - - item.value_changed.connect(self._on_value_change) - - self.input_fields.append(item) - return item - def _on_value_change(self, item=None): if self.ignore_value_changes: return @@ -1942,7 +1935,7 @@ class DictFormWidget(QtWidgets.QWidget, ConfigObject): allow_actions = False def __init__( - self, input_data, values, parent_keys, parent, label_widget=None + self, input_data, parent, as_widget=False, label_widget=None ): self._parent = parent @@ -1959,11 +1952,26 @@ class DictFormWidget(QtWidgets.QWidget, ConfigObject): self.input_fields = [] self.content_layout = QtWidgets.QFormLayout(self) - self.keys = list(parent_keys) - for child_data in input_data.get("children", []): self.add_children_gui(child_data) + def add_children_gui(self, child_configuration): + item_type = child_configuration["type"] + # Pop label to not be set in child + label = child_configuration["label"] + + klass = TypeToKlass.types.get(item_type) + + label_widget = FormLabel(label, self) + + item = klass(child_configuration, self, label_widget=label_widget) + label_widget.item = item + + item.value_changed.connect(self._on_value_change) + self.content_layout.addRow(label_widget, item) + self.input_fields.append(item) + return item + def mouseReleaseEvent(self, event): if event.button() == QtCore.Qt.RightButton: position = self.mapFromGlobal(QtGui.QCursor().pos()) @@ -2041,25 +2049,6 @@ class DictFormWidget(QtWidgets.QWidget, ConfigObject): output.extend(input_field.get_invalid()) return output - def add_children_gui(self, child_configuration): - item_type = child_configuration["type"] - # Pop label to not be set in child - label = child_configuration["label"] - - klass = TypeToKlass.types.get(item_type) - - label_widget = FormLabel(label, self) - - item = klass( - child_configuration, NOT_SET, self.keys, self, label_widget - ) - label_widget.item = item - - item.value_changed.connect(self._on_value_change) - self.content_layout.addRow(label_widget, item) - self.input_fields.append(item) - return item - def hierarchical_style_update(self): for input_field in self.input_fields: input_field.hierarchical_style_update() @@ -2096,12 +2085,12 @@ class PathInputWidget(QtWidgets.QWidget, InputObject): value_changed = QtCore.Signal(object) def __init__( - self, input_data, values, parent_keys, parent, label_widget=None + self, input_data, parent, as_widget=False, label_widget=None ): super(PathInputWidget, self).__init__(parent) self._parent = parent - self._as_widget = values is AS_WIDGET + self._as_widget = as_widget self._state = None self._is_group = input_data.get("is_group", False) @@ -2214,12 +2203,11 @@ class PathWidget(QtWidgets.QWidget, InputObject): } def __init__( - self, input_data, values, parent_keys, parent, label_widget=None + self, input_data, parent, as_widget=False, label_widget=None ): super(PathWidget, self).__init__(parent) self._parent = parent - self._as_widget = values is AS_WIDGET self._is_group = input_data.get("is_group", False) self._is_nullable = input_data.get("is_nullable", False) @@ -2234,12 +2222,6 @@ class PathWidget(QtWidgets.QWidget, InputObject): self.input_fields = [] - if not self._as_widget: - self.key = input_data["key"] - keys = list(parent_keys) - keys.append(self.key) - self.keys = keys - if not self.multiplatform and not self.multipath: layout = QtWidgets.QHBoxLayout(self) else: @@ -2247,13 +2229,12 @@ class PathWidget(QtWidgets.QWidget, InputObject): layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(5) - if not self._as_widget and not label_widget: + self.key = input_data["key"] + if not label_widget: label = input_data["label"] label_widget = QtWidgets.QLabel(label) layout.addWidget(label_widget, 0) - self.label_widget = label_widget - layout.addWidget(label_widget) self.content_widget = QtWidgets.QWidget(self) self.content_layout = QtWidgets.QVBoxLayout(self.content_widget) @@ -2267,9 +2248,7 @@ class PathWidget(QtWidgets.QWidget, InputObject): def create_gui(self): if not self.multiplatform and not self.multipath: input_data = {"key": self.key} - path_input = PathInputWidget( - input_data, NOT_SET, self.keys, self, self.label_widget - ) + path_input = PathInputWidget(input_data, self, self.label_widget) self.setFocusProxy(path_input) self.content_layout.addWidget(path_input) self.input_fields.append(path_input) @@ -2282,7 +2261,7 @@ class PathWidget(QtWidgets.QWidget, InputObject): if not self.multiplatform: input_data_for_list["key"] = self.key input_widget = ListWidget( - input_data_for_list, NOT_SET, self.keys, self, self.label_widget + input_data_for_list, self, self.label_widget ) self.setFocusProxy(input_widget) self.content_layout.addWidget(input_widget) @@ -2298,12 +2277,12 @@ class PathWidget(QtWidgets.QWidget, InputObject): if self.multipath: input_data_for_list["key"] = platform_key input_widget = ListWidget( - input_data_for_list, NOT_SET, self.keys, self, label_widget + input_data_for_list, self, label_widget ) else: input_data = {"key": platform_key} input_widget = PathInputWidget( - input_data, NOT_SET, self.keys, self, label_widget + input_data, self, label_widget ) proxy_layout.addRow(label_widget, input_widget) self.input_fields.append(input_widget) diff --git a/pype/tools/config_setting/config_setting/widgets/lib.py b/pype/tools/config_setting/config_setting/widgets/lib.py index c416f7a5b0..4669004b53 100644 --- a/pype/tools/config_setting/config_setting/widgets/lib.py +++ b/pype/tools/config_setting/config_setting/widgets/lib.py @@ -12,7 +12,6 @@ class TypeToKlass: NOT_SET = type("NOT_SET", (), {}) -AS_WIDGET = type("AS_WIDGET", (), {}) METADATA_KEY = type("METADATA_KEY", (), {}) OVERRIDE_VERSION = 1 From 9c34ee32abd971a80ff4c4fb44f26d23d96bda65 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 2 Sep 2020 16:37:03 +0200 Subject: [PATCH 358/813] few updates and fixes for path input --- .../config_setting/widgets/inputs.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 336a8ef93e..53054b27a6 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -2105,10 +2105,6 @@ class PathInputWidget(QtWidgets.QWidget, InputObject): layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(5) - self.path_input = PathInput(self) - - self.setFocusProxy(self.path_input) - if not self._as_widget: self.key = input_data["key"] if not label_widget: @@ -2116,6 +2112,9 @@ class PathInputWidget(QtWidgets.QWidget, InputObject): label_widget = QtWidgets.QLabel(label) layout.addWidget(label_widget, 0) self.label_widget = label_widget + + self.path_input = PathInput(self) + self.setFocusProxy(self.path_input) layout.addWidget(self.path_input, 1) self.path_input.textChanged.connect(self._on_value_change) @@ -2217,6 +2216,8 @@ class PathWidget(QtWidgets.QWidget, InputObject): self.multipath = input_data.get("multipath", False) self.override_value = NOT_SET + self.global_value = NOT_SET + self.start_value = NOT_SET self._state = None @@ -2248,7 +2249,9 @@ class PathWidget(QtWidgets.QWidget, InputObject): def create_gui(self): if not self.multiplatform and not self.multipath: input_data = {"key": self.key} - path_input = PathInputWidget(input_data, self, self.label_widget) + path_input = PathInputWidget( + input_data, self, label_widget=self.label_widget + ) self.setFocusProxy(path_input) self.content_layout.addWidget(path_input) self.input_fields.append(path_input) @@ -2261,7 +2264,7 @@ class PathWidget(QtWidgets.QWidget, InputObject): if not self.multiplatform: input_data_for_list["key"] = self.key input_widget = ListWidget( - input_data_for_list, self, self.label_widget + input_data_for_list, self, label_widget=self.label_widget ) self.setFocusProxy(input_widget) self.content_layout.addWidget(input_widget) @@ -2277,12 +2280,12 @@ class PathWidget(QtWidgets.QWidget, InputObject): if self.multipath: input_data_for_list["key"] = platform_key input_widget = ListWidget( - input_data_for_list, self, label_widget + input_data_for_list, self, label_widget=label_widget ) else: input_data = {"key": platform_key} input_widget = PathInputWidget( - input_data, self, label_widget + input_data, self, label_widget=label_widget ) proxy_layout.addRow(label_widget, input_widget) self.input_fields.append(input_widget) From e3a62eae3fd2cd17fa186eb1982f56017c7a24be Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 2 Sep 2020 16:37:15 +0200 Subject: [PATCH 359/813] path widget can update global values --- .../config_setting/widgets/inputs.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 53054b27a6..23068ce236 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -2295,19 +2295,16 @@ class PathWidget(QtWidgets.QWidget, InputObject): self.content_layout.addWidget(proxy_widget) def update_global_values(self, parent_values): - print( - self.__class__.__name__, "* TODO implement `update_global_values`" - ) value = NOT_SET - if not self._as_widget: - if parent_values is not NOT_SET: - value = parent_values.get(self.key, NOT_SET) + if parent_values is not NOT_SET: + value = parent_values.get(self.key, NOT_SET) - if value is not NOT_SET: - self.text_input.setText(value) + if not self.multiplatform: + self.input_fields[0].update_global_values(parent_values) - elif self.default_value is not NOT_SET: - self.text_input.setText(self.default_value) + elif self.multiplatform: + for input_field in self.input_fields: + input_field.update_global_values(value) self.global_value = value self.start_value = self.item_value() From 4c0f0a6bddcd688f255d6475db8e196be50d559d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 2 Sep 2020 16:45:18 +0200 Subject: [PATCH 360/813] reorganization --- .../config_setting/widgets/inputs.py | 572 +++++++++--------- 1 file changed, 289 insertions(+), 283 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 23068ce236..ac08a9ae4f 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -734,6 +734,111 @@ class TextMultiLineWidget(QtWidgets.QWidget, InputObject): return self.text_input.toPlainText() +class PathInputWidget(QtWidgets.QWidget, InputObject): + value_changed = QtCore.Signal(object) + + def __init__( + self, input_data, parent, as_widget=False, label_widget=None + ): + super(PathInputWidget, self).__init__(parent) + + self._parent = parent + self._as_widget = as_widget + self._state = None + + self._is_group = input_data.get("is_group", False) + self._is_nullable = input_data.get("is_nullable", False) + self.default_value = input_data.get("default", NOT_SET) + + self.override_value = NOT_SET + self.global_value = NOT_SET + self.start_value = NOT_SET + + layout = QtWidgets.QHBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(5) + + if not self._as_widget: + self.key = input_data["key"] + if not label_widget: + label = input_data["label"] + label_widget = QtWidgets.QLabel(label) + layout.addWidget(label_widget, 0) + self.label_widget = label_widget + + self.path_input = PathInput(self) + self.setFocusProxy(self.path_input) + layout.addWidget(self.path_input, 1) + + self.path_input.textChanged.connect(self._on_value_change) + + def update_global_values(self, parent_values): + value = NOT_SET + if not self._as_widget: + if parent_values is not NOT_SET: + value = parent_values.get(self.key, NOT_SET) + + if value is not NOT_SET: + self.path_input.setText(value) + + elif self.default_value is not NOT_SET: + self.path_input.setText(self.default_value) + + self.global_value = value + self.start_value = self.item_value() + + self._is_modified = self.global_value != self.start_value + + def set_value(self, value, *, global_value=False): + self.path_input.setText(value) + if global_value: + self.start_value = self.item_value() + self.global_value = self.item_value() + self._on_value_change() + + def reset_value(self): + self.set_value(self.start_value) + + def clear_value(self): + self.set_value("") + + def focusOutEvent(self, event): + self.path_input.clear_end_path() + super(PathInput, self).focusOutEvent(event) + + def _on_value_change(self, item=None): + if self.ignore_value_changes: + return + + self._is_modified = self.item_value() != self.global_value + if self.is_overidable: + self._is_overriden = True + + self.update_style() + + self.value_changed.emit(self) + + def update_style(self): + state = self.style_state( + self.is_invalid, self.is_overriden, self.is_modified + ) + if self._state == state: + return + + if self._as_widget: + property_name = "input-state" + widget = self.path_input + else: + property_name = "state" + widget = self.label_widget + + widget.setProperty(property_name, state) + widget.style().polish(widget) + + def item_value(self): + return self.path_input.text() + + class RawJsonInput(QtWidgets.QPlainTextEdit): tab_length = 4 @@ -1923,13 +2028,189 @@ class DictInvisible(QtWidgets.QWidget, ConfigObject): return {self.key: values}, self.is_group +class PathWidget(QtWidgets.QWidget, InputObject): + value_changed = QtCore.Signal(object) + + platforms = ("windows", "darwin", "linux") + platform_labels_mapping = { + "windows": "Windows", + "darwin": "MacOS", + "linux": "Linux" + } + platform_separators = { + "windows": ";", + "darwin": ":", + "linux": ":" + } + + def __init__( + self, input_data, parent, as_widget=False, label_widget=None + ): + super(PathWidget, self).__init__(parent) + + self._parent = parent + + self._is_group = input_data.get("is_group", False) + self._is_nullable = input_data.get("is_nullable", False) + + self.default_value = input_data.get("default", NOT_SET) + self.multiplatform = input_data.get("multiplatform", False) + self.multipath = input_data.get("multipath", False) + + self.override_value = NOT_SET + self.global_value = NOT_SET + self.start_value = NOT_SET + + self._state = None + + self.input_fields = [] + + if not self.multiplatform and not self.multipath: + layout = QtWidgets.QHBoxLayout(self) + else: + layout = QtWidgets.QVBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(5) + + self.key = input_data["key"] + if not label_widget: + label = input_data["label"] + label_widget = QtWidgets.QLabel(label) + layout.addWidget(label_widget, 0) + self.label_widget = label_widget + + self.content_widget = QtWidgets.QWidget(self) + self.content_layout = QtWidgets.QVBoxLayout(self.content_widget) + self.content_layout.setSpacing(0) + self.content_layout.setContentsMargins(0, 0, 0, 0) + + layout.addWidget(self.content_widget) + + self.create_gui() + + def create_gui(self): + if not self.multiplatform and not self.multipath: + input_data = {"key": self.key} + path_input = PathInputWidget( + input_data, self, label_widget=self.label_widget + ) + self.setFocusProxy(path_input) + self.content_layout.addWidget(path_input) + self.input_fields.append(path_input) + path_input.value_changed.connect(self._on_value_change) + return + + input_data_for_list = { + "object_type": "path-input" + } + if not self.multiplatform: + input_data_for_list["key"] = self.key + input_widget = ListWidget( + input_data_for_list, self, label_widget=self.label_widget + ) + self.setFocusProxy(input_widget) + self.content_layout.addWidget(input_widget) + self.input_fields.append(input_widget) + input_widget.value_changed.connect(self._on_value_change) + return + + proxy_widget = QtWidgets.QWidget(self.content_widget) + proxy_layout = QtWidgets.QFormLayout(proxy_widget) + for platform_key in self.platforms: + platform_label = self.platform_labels_mapping[platform_key] + label_widget = QtWidgets.QLabel(platform_label, proxy_widget) + if self.multipath: + input_data_for_list["key"] = platform_key + input_widget = ListWidget( + input_data_for_list, self, label_widget=label_widget + ) + else: + input_data = {"key": platform_key} + input_widget = PathInputWidget( + input_data, self, label_widget=label_widget + ) + proxy_layout.addRow(label_widget, input_widget) + self.input_fields.append(input_widget) + input_widget.value_changed.connect(self._on_value_change) + + self.setFocusProxy(self.input_fields[0]) + self.content_layout.addWidget(proxy_widget) + + def update_global_values(self, parent_values): + value = NOT_SET + if parent_values is not NOT_SET: + value = parent_values.get(self.key, NOT_SET) + + if not self.multiplatform: + self.input_fields[0].update_global_values(parent_values) + + elif self.multiplatform: + for input_field in self.input_fields: + input_field.update_global_values(value) + + self.global_value = value + self.start_value = self.item_value() + + self._is_modified = self.global_value != self.start_value + + def set_value(self, value, *, global_value=False): + if not self.multiplatform: + self.input_fields[0].set_value(value, global_value=global_value) + + else: + for input_field in self.input_fields: + _value = value[input_field.key] + input_field.set_value(_value) + + if global_value: + self.global_value = value + self.start_value = self.item_value() + self._on_value_change() + + def reset_value(self): + for input_field in self.input_fields: + input_field.reset_value() + + def clear_value(self): + for input_field in self.input_fields: + input_field.clear_value() + + def _on_value_change(self, item=None): + if self.ignore_value_changes: + return + + self._is_modified = self.item_value() != self.global_value + if self.is_overidable: + self._is_overriden = True + + self.update_style() + + self.value_changed.emit(self) + + def update_style(self): + for input_field in self.input_fields: + input_field.update_style() + + def item_value(self): + if not self.multiplatform and not self.multipath: + return self.input_fields[0].item_value() + + if not self.multiplatform: + return self.input_fields[0].item_value() + + output = {} + for input_field in self.input_fields: + output.update(input_field.config_value()) + return output + + +# Proxy for form layout class FormLabel(QtWidgets.QLabel): def __init__(self, *args, **kwargs): super(FormLabel, self).__init__(*args, **kwargs) self.item = None -# Proxy for form layout class DictFormWidget(QtWidgets.QWidget, ConfigObject): value_changed = QtCore.Signal(object) allow_actions = False @@ -2081,290 +2362,15 @@ class DictFormWidget(QtWidgets.QWidget, ConfigObject): return values, self.is_group -class PathInputWidget(QtWidgets.QWidget, InputObject): - value_changed = QtCore.Signal(object) - - def __init__( - self, input_data, parent, as_widget=False, label_widget=None - ): - super(PathInputWidget, self).__init__(parent) - - self._parent = parent - self._as_widget = as_widget - self._state = None - - self._is_group = input_data.get("is_group", False) - self._is_nullable = input_data.get("is_nullable", False) - self.default_value = input_data.get("default", NOT_SET) - - self.override_value = NOT_SET - self.global_value = NOT_SET - self.start_value = NOT_SET - - layout = QtWidgets.QHBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(5) - - if not self._as_widget: - self.key = input_data["key"] - if not label_widget: - label = input_data["label"] - label_widget = QtWidgets.QLabel(label) - layout.addWidget(label_widget, 0) - self.label_widget = label_widget - - self.path_input = PathInput(self) - self.setFocusProxy(self.path_input) - layout.addWidget(self.path_input, 1) - - self.path_input.textChanged.connect(self._on_value_change) - - def update_global_values(self, parent_values): - value = NOT_SET - if not self._as_widget: - if parent_values is not NOT_SET: - value = parent_values.get(self.key, NOT_SET) - - if value is not NOT_SET: - self.path_input.setText(value) - - elif self.default_value is not NOT_SET: - self.path_input.setText(self.default_value) - - self.global_value = value - self.start_value = self.item_value() - - self._is_modified = self.global_value != self.start_value - - def set_value(self, value, *, global_value=False): - self.path_input.setText(value) - if global_value: - self.start_value = self.item_value() - self.global_value = self.item_value() - self._on_value_change() - - def reset_value(self): - self.set_value(self.start_value) - - def clear_value(self): - self.set_value("") - - def focusOutEvent(self, event): - self.path_input.clear_end_path() - super(PathInput, self).focusOutEvent(event) - - def _on_value_change(self, item=None): - if self.ignore_value_changes: - return - - self._is_modified = self.item_value() != self.global_value - if self.is_overidable: - self._is_overriden = True - - self.update_style() - - self.value_changed.emit(self) - - def update_style(self): - state = self.style_state( - self.is_invalid, self.is_overriden, self.is_modified - ) - if self._state == state: - return - - if self._as_widget: - property_name = "input-state" - widget = self.path_input - else: - property_name = "state" - widget = self.label_widget - - widget.setProperty(property_name, state) - widget.style().polish(widget) - - def item_value(self): - return self.path_input.text() - - -class PathWidget(QtWidgets.QWidget, InputObject): - value_changed = QtCore.Signal(object) - - platforms = ("windows", "darwin", "linux") - platform_labels_mapping = { - "windows": "Windows", - "darwin": "MacOS", - "linux": "Linux" - } - platform_separators = { - "windows": ";", - "darwin": ":", - "linux": ":" - } - - def __init__( - self, input_data, parent, as_widget=False, label_widget=None - ): - super(PathWidget, self).__init__(parent) - - self._parent = parent - - self._is_group = input_data.get("is_group", False) - self._is_nullable = input_data.get("is_nullable", False) - - self.default_value = input_data.get("default", NOT_SET) - self.multiplatform = input_data.get("multiplatform", False) - self.multipath = input_data.get("multipath", False) - - self.override_value = NOT_SET - self.global_value = NOT_SET - self.start_value = NOT_SET - - self._state = None - - self.input_fields = [] - - if not self.multiplatform and not self.multipath: - layout = QtWidgets.QHBoxLayout(self) - else: - layout = QtWidgets.QVBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(5) - - self.key = input_data["key"] - if not label_widget: - label = input_data["label"] - label_widget = QtWidgets.QLabel(label) - layout.addWidget(label_widget, 0) - self.label_widget = label_widget - - self.content_widget = QtWidgets.QWidget(self) - self.content_layout = QtWidgets.QVBoxLayout(self.content_widget) - self.content_layout.setSpacing(0) - self.content_layout.setContentsMargins(0, 0, 0, 0) - - layout.addWidget(self.content_widget) - - self.create_gui() - - def create_gui(self): - if not self.multiplatform and not self.multipath: - input_data = {"key": self.key} - path_input = PathInputWidget( - input_data, self, label_widget=self.label_widget - ) - self.setFocusProxy(path_input) - self.content_layout.addWidget(path_input) - self.input_fields.append(path_input) - path_input.value_changed.connect(self._on_value_change) - return - - input_data_for_list = { - "object_type": "path-input" - } - if not self.multiplatform: - input_data_for_list["key"] = self.key - input_widget = ListWidget( - input_data_for_list, self, label_widget=self.label_widget - ) - self.setFocusProxy(input_widget) - self.content_layout.addWidget(input_widget) - self.input_fields.append(input_widget) - input_widget.value_changed.connect(self._on_value_change) - return - - proxy_widget = QtWidgets.QWidget(self.content_widget) - proxy_layout = QtWidgets.QFormLayout(proxy_widget) - for platform_key in self.platforms: - platform_label = self.platform_labels_mapping[platform_key] - label_widget = QtWidgets.QLabel(platform_label, proxy_widget) - if self.multipath: - input_data_for_list["key"] = platform_key - input_widget = ListWidget( - input_data_for_list, self, label_widget=label_widget - ) - else: - input_data = {"key": platform_key} - input_widget = PathInputWidget( - input_data, self, label_widget=label_widget - ) - proxy_layout.addRow(label_widget, input_widget) - self.input_fields.append(input_widget) - input_widget.value_changed.connect(self._on_value_change) - - self.setFocusProxy(self.input_fields[0]) - self.content_layout.addWidget(proxy_widget) - - def update_global_values(self, parent_values): - value = NOT_SET - if parent_values is not NOT_SET: - value = parent_values.get(self.key, NOT_SET) - - if not self.multiplatform: - self.input_fields[0].update_global_values(parent_values) - - elif self.multiplatform: - for input_field in self.input_fields: - input_field.update_global_values(value) - - self.global_value = value - self.start_value = self.item_value() - - self._is_modified = self.global_value != self.start_value - - def set_value(self, value, *, global_value=False): - print(self.__class__.__name__, "* TODO implement `set_value`") - self.text_input.setText(value) - if global_value: - self.start_value = self.item_value() - self.global_value = self.item_value() - self._on_value_change() - - def reset_value(self): - print(self.__class__.__name__, "* TODO implement `reset_value`") - self.set_value(self.start_value) - - def clear_value(self): - print(self.__class__.__name__, "* TODO implement `clear_value`") - self.set_value("") - - def _on_value_change(self, item=None): - print(self.__class__.__name__, "* TODO implement `_on_value_change`") - if self.ignore_value_changes: - return - - self._is_modified = self.item_value() != self.global_value - if self.is_overidable: - self._is_overriden = True - - self.update_style() - - self.value_changed.emit(self) - - def update_style(self): - print(self.__class__.__name__, "* TODO implement `update_style`") - - def item_value(self): - if not self.multiplatform and not self.multipath: - return self.input_fields[0].item_value() - - if not self.multiplatform: - return self.input_fields[0].item_value() - - output = {} - for input_field in self.input_fields: - output.update(input_field.config_value()) - return output - - TypeToKlass.types["boolean"] = BooleanWidget -TypeToKlass.types["text-singleline"] = TextSingleLineWidget -TypeToKlass.types["path-input"] = PathInputWidget -TypeToKlass.types["path-widget"] = PathWidget -TypeToKlass.types["text-multiline"] = TextMultiLineWidget -TypeToKlass.types["raw-json"] = RawJsonWidget TypeToKlass.types["number"] = NumberWidget +TypeToKlass.types["text-singleline"] = TextSingleLineWidget +TypeToKlass.types["text-multiline"] = TextMultiLineWidget +TypeToKlass.types["path-input"] = PathInputWidget +TypeToKlass.types["raw-json"] = RawJsonWidget +TypeToKlass.types["list"] = ListWidget TypeToKlass.types["dict-modifiable"] = ModifiableDict TypeToKlass.types["dict"] = DictWidget -TypeToKlass.types["dict-form"] = DictFormWidget TypeToKlass.types["dict-invisible"] = DictInvisible -TypeToKlass.types["list"] = ListWidget +TypeToKlass.types["path-widget"] = PathWidget +TypeToKlass.types["dict-form"] = DictFormWidget From b183564954bd29735a85630ef2246fec0f1f3282 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 2 Sep 2020 16:52:13 +0200 Subject: [PATCH 361/813] text input has one input widget with settable attribute "multiline" --- .../1_ftrack_projects_gui_schema.json | 6 +- .../projects_schema/1_plugins_gui_schema.json | 42 +++--- .../studio_schema/1_intents_gui_schema.json | 4 +- .../studio_schema/1_tray_items.json | 2 +- .../config_setting/widgets/inputs.py | 128 +++--------------- 5 files changed, 45 insertions(+), 137 deletions(-) diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_ftrack_projects_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_ftrack_projects_gui_schema.json index 51c05aeb3a..6608463100 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_ftrack_projects_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_ftrack_projects_gui_schema.json @@ -14,11 +14,11 @@ "children": [ { "key": "Ready", - "type": "text-singleline", + "type": "text", "label": "Ready" }, { "key": "Ready2", - "type": "text-singleline", + "type": "text", "label": "Ready2" } ] @@ -32,7 +32,7 @@ "children": [ { "key": "in progress", - "type": "text-singleline", + "type": "text", "label": "In Progress" } ] diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json index e43518989a..98fbfb206d 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json @@ -34,7 +34,7 @@ "type": "dict-form", "children": [ { - "type": "text-singleline", + "type": "text", "key": "deadline_department", "label": "Deadline apartment", "default": "" @@ -44,17 +44,17 @@ "label": "Deadline priority", "default": 50 }, { - "type": "text-singleline", + "type": "text", "key": "deadline_pool", "label": "Deadline pool", "default": "" }, { - "type": "text-singleline", + "type": "text", "key": "deadline_pool_secondary", "label": "Deadline pool (secondary)", "default": "" }, { - "type": "text-singleline", + "type": "text", "key": "deadline_group", "label": "Deadline Group", "default": "" @@ -98,13 +98,13 @@ "label": "Enabled", "default": false }, { - "type": "text-singleline", + "type": "text", "key": "note_with_intent_template", "label": "Note with intent template", "default": "{intent}: {comment}" }, { "type": "list", - "object_type": "text-singleline", + "object_type": "text", "key": "note_labels", "label": "Note labels", "default": [] @@ -155,13 +155,13 @@ "children": [ { "type": "list", - "object_type": "text-singleline", + "object_type": "text", "key": "input", "label": "FFmpeg input arguments", "default": [] }, { "type": "list", - "object_type": "text-singleline", + "object_type": "text", "key": "output", "label": "FFmpeg output arguments", "default": [] @@ -271,15 +271,15 @@ "key": "enabled", "label": "Enabled" }, { - "type": "text-singleline", + "type": "text", "key": "deadline_department", "label": "Deadline department" }, { - "type": "text-singleline", + "type": "text", "key": "deadline_pool", "label": "Deadline Pool" }, { - "type": "text-singleline", + "type": "text", "key": "deadline_group", "label": "Deadline Group" } @@ -314,11 +314,11 @@ "key": "enabled", "label": "Enabled" }, { - "type": "text-singleline", + "type": "text", "key": "material_file", "label": "Material File" }, { - "type": "text-singleline", + "type": "text", "key": "regex", "label": "Validation regex", "default": "(.*)_(\\d)*_(?P.*)_(GEO)" @@ -351,7 +351,7 @@ "key": "enabled", "label": "Enabled" }, { - "type": "text-singleline", + "type": "text", "key": "regex", "label": "Validation regex", "default": "(?P.*)_(.*)_SHD" @@ -401,7 +401,7 @@ "is_group": true, "children": [ { - "type": "text-singleline", + "type": "text", "key": "fpath_template", "label": "Path template", "default": "{work}/renders/nuke/{subset}/{subset}.{frame}.{ext}" @@ -415,7 +415,7 @@ "is_group": true, "children": [ { - "type": "text-singleline", + "type": "text", "key": "fpath_template", "label": "Path template", "default": "{work}/prerenders/nuke/{subset}/{subset}.{frame}.{ext}" @@ -530,12 +530,12 @@ "label": "deadline_priority", "default": 50 }, { - "type": "text-singleline", + "type": "text", "key": "deadline_pool", "label": "deadline_pool", "default": "" }, { - "type": "text-singleline", + "type": "text", "key": "deadline_pool_secondary", "label": "deadline_pool_secondary", "default": "" @@ -598,7 +598,7 @@ "default": true }, { "type": "list", - "object_type": "text-singleline", + "object_type": "text", "key": "tags_addition", "label": "Tags addition", "default": [] @@ -629,12 +629,12 @@ "is_group": true, "children": [ { - "type": "text-singleline", + "type": "text", "key": "clipName", "label": "Clip name template", "default": "{track}{sequence}{shot}" }, { - "type": "text-singleline", + "type": "text", "key": "folder", "label": "Folder", "default": "takes" diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_intents_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_intents_gui_schema.json index 18f3d42ba7..a4b5e16fa1 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_intents_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_intents_gui_schema.json @@ -8,11 +8,11 @@ "children": [ { "type": "dict-modifiable", - "object_type": "text-singleline", + "object_type": "text", "key": "items", "label": "Intent Key/Label" }, { - "type": "text-singleline", + "type": "text", "key": "default", "label": "Default intent" } diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_tray_items.json b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_tray_items.json index 610430b25e..13ab0293de 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_tray_items.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_tray_items.json @@ -113,7 +113,7 @@ "expandable": true, "children": [ { - "type": "text-singleline", + "type": "text", "key": "workspace_name", "label": "Workspace name" } diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index ac08a9ae4f..ce14e575cf 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -529,13 +529,13 @@ class NumberWidget(QtWidgets.QWidget, InputObject): return self.input_field.value() -class TextSingleLineWidget(QtWidgets.QWidget, InputObject): +class TextWidget(QtWidgets.QWidget, InputObject): value_changed = QtCore.Signal(object) def __init__( self, input_data, parent, as_widget=False, label_widget=None ): - super(TextSingleLineWidget, self).__init__(parent) + super(TextWidget, self).__init__(parent) self._parent = parent self._as_widget = as_widget @@ -545,6 +545,8 @@ class TextSingleLineWidget(QtWidgets.QWidget, InputObject): self._is_nullable = input_data.get("is_nullable", False) self.default_value = input_data.get("default", NOT_SET) + self.multiline = input_data.get("multiline", False) + self.override_value = NOT_SET self.global_value = NOT_SET self.start_value = NOT_SET @@ -553,7 +555,10 @@ class TextSingleLineWidget(QtWidgets.QWidget, InputObject): layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(5) - self.text_input = QtWidgets.QLineEdit(self) + if self.multiline: + self.text_input = QtWidgets.QPlainTextEdit(self) + else: + self.text_input = QtWidgets.QLineEdit(self) self.setFocusProxy(self.text_input) @@ -576,10 +581,10 @@ class TextSingleLineWidget(QtWidgets.QWidget, InputObject): value = parent_values.get(self.key, NOT_SET) if value is not NOT_SET: - self.text_input.setText(value) + self.set_value(value) elif self.default_value is not NOT_SET: - self.text_input.setText(self.default_value) + self.set_value(self.default_value) self.global_value = value self.start_value = self.item_value() @@ -587,7 +592,10 @@ class TextSingleLineWidget(QtWidgets.QWidget, InputObject): self._is_modified = self.global_value != self.start_value def set_value(self, value, *, global_value=False): - self.text_input.setText(value) + if self.multiline: + self.text_input.setPlainText(value) + else: + self.text_input.setText(value) if global_value: self.start_value = self.item_value() self.global_value = self.item_value() @@ -629,109 +637,10 @@ class TextSingleLineWidget(QtWidgets.QWidget, InputObject): widget.style().polish(widget) def item_value(self): - return self.text_input.text() - - -class TextMultiLineWidget(QtWidgets.QWidget, InputObject): - value_changed = QtCore.Signal(object) - - def __init__( - self, input_data, parent, as_widget=False, label_widget=None - ): - super(TextMultiLineWidget, self).__init__(parent) - - self._parent = parent - self._as_widget = as_widget - self._state = None - - self._is_group = input_data.get("is_group", False) - self._is_nullable = input_data.get("is_nullable", False) - self.default_value = input_data.get("default", NOT_SET) - - self.override_value = NOT_SET - self.global_value = NOT_SET - self.start_value = NOT_SET - - layout = QtWidgets.QHBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(5) - - self.text_input = QtWidgets.QPlainTextEdit(self) - - self.setFocusProxy(self.text_input) - - if not self._as_widget: - self.key = input_data["key"] - if not label_widget: - label = input_data["label"] - label_widget = QtWidgets.QLabel(label) - layout.addWidget(label_widget, 0) - self.label_widget = label_widget - layout.addWidget(self.text_input, 1) - - self.text_input.textChanged.connect(self._on_value_change) - - def update_global_values(self, parent_values): - value = NOT_SET - if not self._as_widget: - if parent_values is not NOT_SET: - value = parent_values.get(self.key, NOT_SET) - - if value is not NOT_SET: - self.text_input.setPlainText(value) - - elif self.default_value is not NOT_SET: - self.text_input.setPlainText(self.default_value) - - self.global_value = value - self.start_value = self.item_value() - - self._is_modified = self.global_value != self.start_value - - def set_value(self, value, *, global_value=False): - self.text_input.setPlainText(value) - if global_value: - self.start_value = self.item_value() - self.global_value = self.item_value() - self._on_value_change() - - def reset_value(self): - self.set_value(self.start_value) - - def clear_value(self): - self.set_value("") - - def _on_value_change(self, item=None): - if self.ignore_value_changes: - return - - self._is_modified = self.item_value() != self.global_value - if self.is_overidable: - self._is_overriden = True - - self.update_style() - - self.value_changed.emit(self) - - def update_style(self): - state = self.style_state( - self.is_invalid, self.is_overriden, self.is_modified - ) - if self._state == state: - return - - if self._as_widget: - property_name = "input-state" - widget = self.text_input + if self.multiline: + return self.text_input.toPlainText() else: - property_name = "state" - widget = self.label_widget - - widget.setProperty(property_name, state) - widget.style().polish(widget) - - def item_value(self): - return self.text_input.toPlainText() + return self.text_input.text() class PathInputWidget(QtWidgets.QWidget, InputObject): @@ -2364,8 +2273,7 @@ class DictFormWidget(QtWidgets.QWidget, ConfigObject): TypeToKlass.types["boolean"] = BooleanWidget TypeToKlass.types["number"] = NumberWidget -TypeToKlass.types["text-singleline"] = TextSingleLineWidget -TypeToKlass.types["text-multiline"] = TextMultiLineWidget +TypeToKlass.types["text"] = TextWidget TypeToKlass.types["path-input"] = PathInputWidget TypeToKlass.types["raw-json"] = RawJsonWidget TypeToKlass.types["list"] = ListWidget From 5cba39b998bbb563ebbeb59cac415b87f9498ce0 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 2 Sep 2020 17:16:08 +0200 Subject: [PATCH 362/813] path widget works right with overrides --- .../config_setting/widgets/inputs.py | 125 ++++++++++++++++-- 1 file changed, 115 insertions(+), 10 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index ce14e575cf..09e4629a45 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1937,7 +1937,7 @@ class DictInvisible(QtWidgets.QWidget, ConfigObject): return {self.key: values}, self.is_group -class PathWidget(QtWidgets.QWidget, InputObject): +class PathWidget(QtWidgets.QWidget, ConfigObject): value_changed = QtCore.Signal(object) platforms = ("windows", "darwin", "linux") @@ -1958,8 +1958,19 @@ class PathWidget(QtWidgets.QWidget, InputObject): super(PathWidget, self).__init__(parent) self._parent = parent + self._state = None + self._child_state = None - self._is_group = input_data.get("is_group", False) + any_parent_is_group = parent.is_group + if not any_parent_is_group: + any_parent_is_group = parent.any_parent_is_group + self.any_parent_is_group = any_parent_is_group + + # This is partial input and dictionary input + if not any_parent_is_group: + self._is_group = True + else: + self._is_group = False self._is_nullable = input_data.get("is_nullable", False) self.default_value = input_data.get("default", NOT_SET) @@ -1970,8 +1981,6 @@ class PathWidget(QtWidgets.QWidget, InputObject): self.global_value = NOT_SET self.start_value = NOT_SET - self._state = None - self.input_fields = [] if not self.multiplatform and not self.multipath: @@ -2062,6 +2071,30 @@ class PathWidget(QtWidgets.QWidget, InputObject): self._is_modified = self.global_value != self.start_value + def apply_overrides(self, parent_values): + # Make sure this is set to False + self._state = None + self._child_state = None + self._is_modified = False + override_values = NOT_SET + if parent_values is not NOT_SET: + override_values = parent_values.get(self.key, override_values) + + self._is_overriden = override_values is not NOT_SET + if not self.multiplatform: + self.input_fields[0].apply_overrides(parent_values) + else: + for item in self.input_fields: + item.apply_overrides(override_values) + + if not self._is_overriden: + self._is_overriden = ( + self.is_group + and self.is_overidable + and self.child_overriden + ) + self._was_overriden = bool(self._is_overriden) + def set_value(self, value, *, global_value=False): if not self.multiplatform: self.input_fields[0].set_value(value, global_value=global_value) @@ -2089,16 +2122,79 @@ class PathWidget(QtWidgets.QWidget, InputObject): return self._is_modified = self.item_value() != self.global_value - if self.is_overidable: - self._is_overriden = True - - self.update_style() + if self.is_group: + if self.is_overidable: + self._is_overriden = True + self.hierarchical_style_update() self.value_changed.emit(self) - def update_style(self): + self.update_style() + + def update_style(self, is_overriden=None): + child_modified = self.child_modified + child_invalid = self.child_invalid + child_state = self.style_state( + child_invalid, self.child_overriden, child_modified + ) + if child_state: + child_state = "child-{}".format(child_state) + + if child_state != self._child_state: + self.setProperty("state", child_state) + self.style().polish(self) + self._child_state = child_state + + state = self.style_state( + child_invalid, self.is_overriden, self.is_modified + ) + if self._state == state: + return + + self.label_widget.setProperty("state", state) + self.label_widget.style().polish(self.label_widget) + + self._state = state + + def remove_overrides(self): + self._is_overriden = False + self._is_modified = False + self._was_overriden = False + for item in self.input_fields: + item.remove_overrides() + + def discard_changes(self): for input_field in self.input_fields: - input_field.update_style() + input_field.discard_changes() + + self._is_modified = self.child_modified + self._is_overriden = self._was_overriden + + @property + def child_modified(self): + for input_field in self.input_fields: + if input_field.child_modified: + return True + return False + + @property + def child_overriden(self): + for input_field in self.input_fields: + if input_field.child_overriden: + return True + return False + + @property + def child_invalid(self): + for input_field in self.input_fields: + if input_field.child_invalid: + return True + return False + + def hierarchical_style_update(self): + for input_field in self.input_fields: + input_field.hierarchical_style_update() + self.update_style() def item_value(self): if not self.multiplatform and not self.multipath: @@ -2112,6 +2208,15 @@ class PathWidget(QtWidgets.QWidget, InputObject): output.update(input_field.config_value()) return output + def overrides(self): + if not self.is_overriden and not self.child_overriden: + return NOT_SET, False + + value = self.item_value() + if not self.multiplatform: + value = {self.key: value} + return value, self.is_group + # Proxy for form layout class FormLabel(QtWidgets.QLabel): From cbf1519645ff0e200af6e464314a8dbe276fea14 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 2 Sep 2020 19:10:43 +0200 Subject: [PATCH 363/813] first introducing enhancement of anatomy widget --- .../config_setting/widgets/anatomy_inputs.py | 74 +++++++++++++++++-- 1 file changed, 66 insertions(+), 8 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index 7c9ac0d8f4..d07f69e7ca 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -5,28 +5,59 @@ from .lib import NOT_SET, TypeToKlass class AnatomyWidget(QtWidgets.QWidget, InputObject): value_changed = QtCore.Signal(object) + template_keys = ( + "project[name]", + "project[code]", + "asset", + "task", + "subset", + "family", + "version", + "ext", + "representation" + ) + default_exmaple_data = { + "project": { + "name": "ProjectPype", + "code": "pp", + }, + "asset": "sq01sh0010", + "task": "compositing", + "subset": "renderMain", + "family": "render", + "version": 1, + "ext": ".png", + "representation": "png" + } def __init__( self, input_data, parent, as_widget=False, label_widget=None ): + super(AnatomyWidget, self).__init__(parent) + self._parent = parent self._as_widget = as_widget self._is_group = True - self._state = None self.key = "anatomy" - self.start_value = None - super(AnatomyWidget, self).__init__(parent) + self.override_value = NOT_SET + self.start_value = NOT_SET + self.global_value = NOT_SET + + self.root_keys = None + self.root_widget = RootsWidget(self) + self.templates_widget = TemplatesWidget(self) layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(5) - label = QtWidgets.QLabel("Test") - layout.addWidget(label) - self.override_value = NOT_SET + label = QtWidgets.QLabel("Anatomy", self) + layout.addWidget(label) + layout.addWidget(self.root_widget) + layout.addWidget(self.templates_widget) def update_global_values(self, values): print("* update_global_values") @@ -47,10 +78,37 @@ class AnatomyWidget(QtWidgets.QWidget, InputObject): print("* item_value") -class TemplatesWidget: - pass +class RootsWidget(QtWidgets.QWidget): + multiroot_changed = QtCore.QSignal() + + def __init__(self, parent=None): + super(RootsWidget, self).__init__(parent) + + self.root_keys = None + + layout = QtWidgets.QHBoxLayout(self) + multiroot_checkbox = QtWidgets.QCheckBox(self) + layout.addWidget(multiroot_checkbox) + + self.multiroot_checkbox = multiroot_checkbox + multiroot_checkbox.stateChanged.connect(self._on_multiroot_checkbox) + + def _on_multiroot_checkbox(self): + self.set_multiroot(self.multiroot_checkbox.isChecked()) + + def set_multiroot(self, is_multiroot=None): + if is_multiroot is None: + is_multiroot = not self.multiroot_checkbox.isChecked() + + if is_multiroot != self.multiroot_checkbox.isChecked(): + self.multiroot_checkbox.setChecked(is_multiroot) + + self.multiroot_changed.emit() +class TemplatesWidget(QtWidgets.QWidget): + def __init__(self, parent=None): + super(TemplatesWidget, self).__init__(parent) TypeToKlass.types["anatomy"] = AnatomyWidget From 059152188d191240791e9b53f862bb17c452e7d8 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 2 Sep 2020 19:25:34 +0200 Subject: [PATCH 364/813] fix numer value when decimals are set to 0 --- .../tools/config_setting/config_setting/widgets/widgets.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/widgets.py b/pype/tools/config_setting/config_setting/widgets/widgets.py index d5b0f088de..35b515d641 100644 --- a/pype/tools/config_setting/config_setting/widgets/widgets.py +++ b/pype/tools/config_setting/config_setting/widgets/widgets.py @@ -18,6 +18,13 @@ class NumberSpinBox(QtWidgets.QDoubleSpinBox): else: event.ignore() + def value(self): + output = super(NumberSpinBox, self).value() + print(self.decimals()) + if self.decimals() == 0: + output = int(output) + return output + class PathInput(QtWidgets.QLineEdit): def clear_end_path(self): From 043aec3fef57a430bbed0cd9bede58f466758518 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 2 Sep 2020 19:25:47 +0200 Subject: [PATCH 365/813] fix class name in anatomy --- .../config_setting/config_setting/widgets/anatomy_inputs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index d07f69e7ca..46b69aaffe 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -79,7 +79,7 @@ class AnatomyWidget(QtWidgets.QWidget, InputObject): class RootsWidget(QtWidgets.QWidget): - multiroot_changed = QtCore.QSignal() + multiroot_changed = QtCore.Signal() def __init__(self, parent=None): super(RootsWidget, self).__init__(parent) From f71aad40e04ac934522f25edad1d358bf4a8f5f5 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 3 Sep 2020 11:01:09 +0200 Subject: [PATCH 366/813] removed icon attribute --- .../ftrack/ftrack_server/sub_event_status.py | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/pype/modules/ftrack/ftrack_server/sub_event_status.py b/pype/modules/ftrack/ftrack_server/sub_event_status.py index c2e7b7477f..00a6687de3 100644 --- a/pype/modules/ftrack/ftrack_server/sub_event_status.py +++ b/pype/modules/ftrack/ftrack_server/sub_event_status.py @@ -12,7 +12,7 @@ from pype.modules.ftrack.ftrack_server.lib import ( SocketSession, StatusEventHub, TOPIC_STATUS_SERVER, TOPIC_STATUS_SERVER_RESULT ) -from pype.api import Logger, config +from pype.api import Logger log = Logger().get_logger("Event storer") action_identifier = ( @@ -23,17 +23,7 @@ action_data = { "label": "Pype Admin", "variant": "- Event server Status ({})".format(host_ip), "description": "Get Infromation about event server", - "actionIdentifier": action_identifier, - "icon": "{}/ftrack/action_icons/PypeAdmin.svg".format( - os.environ.get( - "PYPE_STATICS_SERVER", - "http://localhost:{}".format( - config.get_presets().get("services", {}).get( - "rest_api", {} - ).get("default_port", 8021) - ) - ) - ) + "actionIdentifier": action_identifier } From a88fcfe9fa3de9e2b52478b70f1ddb0b8a72f7c2 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 3 Sep 2020 11:19:57 +0200 Subject: [PATCH 367/813] removed debug print --- pype/tools/config_setting/config_setting/widgets/widgets.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pype/tools/config_setting/config_setting/widgets/widgets.py b/pype/tools/config_setting/config_setting/widgets/widgets.py index 35b515d641..e9ba1b798c 100644 --- a/pype/tools/config_setting/config_setting/widgets/widgets.py +++ b/pype/tools/config_setting/config_setting/widgets/widgets.py @@ -20,7 +20,6 @@ class NumberSpinBox(QtWidgets.QDoubleSpinBox): def value(self): output = super(NumberSpinBox, self).value() - print(self.decimals()) if self.decimals() == 0: output = int(output) return output From bccaec878ff084bafe8c04cb604b34f2d377f08b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 3 Sep 2020 11:46:11 +0200 Subject: [PATCH 368/813] PathWidget can be as widget --- .../config_setting/widgets/inputs.py | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 09e4629a45..c0c0ba3406 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1960,6 +1960,7 @@ class PathWidget(QtWidgets.QWidget, ConfigObject): self._parent = parent self._state = None self._child_state = None + self._as_widget = as_widget any_parent_is_group = parent.is_group if not any_parent_is_group: @@ -1967,7 +1968,7 @@ class PathWidget(QtWidgets.QWidget, ConfigObject): self.any_parent_is_group = any_parent_is_group # This is partial input and dictionary input - if not any_parent_is_group: + if not any_parent_is_group and not as_widget: self._is_group = True else: self._is_group = False @@ -1990,12 +1991,14 @@ class PathWidget(QtWidgets.QWidget, ConfigObject): layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(5) - self.key = input_data["key"] - if not label_widget: - label = input_data["label"] - label_widget = QtWidgets.QLabel(label) - layout.addWidget(label_widget, 0) - self.label_widget = label_widget + if not as_widget: + self.key = input_data["key"] + if not label_widget: + label = input_data["label"] + label_widget = QtWidgets.QLabel(label) + label_widget.setAttribute(QtCore.Qt.WA_StyledBackground) + layout.addWidget(label_widget, 0) + self.label_widget = label_widget self.content_widget = QtWidgets.QWidget(self) self.content_layout = QtWidgets.QVBoxLayout(self.content_widget) @@ -2056,15 +2059,16 @@ class PathWidget(QtWidgets.QWidget, ConfigObject): def update_global_values(self, parent_values): value = NOT_SET - if parent_values is not NOT_SET: - value = parent_values.get(self.key, NOT_SET) + if not self._as_widget: + if parent_values is not NOT_SET: + value = parent_values.get(self.key, NOT_SET) - if not self.multiplatform: - self.input_fields[0].update_global_values(parent_values) + if not self.multiplatform: + self.input_fields[0].update_global_values(parent_values) - elif self.multiplatform: - for input_field in self.input_fields: - input_field.update_global_values(value) + elif self.multiplatform: + for input_field in self.input_fields: + input_field.update_global_values(value) self.global_value = value self.start_value = self.item_value() From e515895cf38e1c4f73a740dfa4f3c49ff8c80efe Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 3 Sep 2020 11:46:40 +0200 Subject: [PATCH 369/813] roots widget has multiroot and singleroot widget --- .../config_setting/widgets/anatomy_inputs.py | 57 ++++++++++++++++--- 1 file changed, 50 insertions(+), 7 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index 46b69aaffe..3148b8f32d 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -1,5 +1,5 @@ from Qt import QtWidgets, QtCore -from .inputs import InputObject +from .inputs import ConfigObject, InputObject, ModifiableDict, PathWidget from .lib import NOT_SET, TypeToKlass @@ -50,7 +50,7 @@ class AnatomyWidget(QtWidgets.QWidget, InputObject): self.root_widget = RootsWidget(self) self.templates_widget = TemplatesWidget(self) - layout = QtWidgets.QHBoxLayout(self) + layout = QtWidgets.QVBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(5) @@ -61,6 +61,8 @@ class AnatomyWidget(QtWidgets.QWidget, InputObject): def update_global_values(self, values): print("* update_global_values") + self.root_widget.update_global_values(values) + self.templates_widget.update_global_values(values) def set_value(self, value, *, global_value=False): print("* set_value") @@ -78,21 +80,59 @@ class AnatomyWidget(QtWidgets.QWidget, InputObject): print("* item_value") -class RootsWidget(QtWidgets.QWidget): +class RootsWidget(QtWidgets.QWidget, ConfigObject): multiroot_changed = QtCore.Signal() - def __init__(self, parent=None): + def __init__(self, parent): super(RootsWidget, self).__init__(parent) + self._parent = parent + self._is_group = True self.root_keys = None - layout = QtWidgets.QHBoxLayout(self) - multiroot_checkbox = QtWidgets.QCheckBox(self) - layout.addWidget(multiroot_checkbox) + checkbox_widget = QtWidgets.QWidget(self) + + multiroot_label = QtWidgets.QLabel( + "Use multiple roots", checkbox_widget + ) + multiroot_checkbox = QtWidgets.QCheckBox(checkbox_widget) + + checkbox_layout = QtWidgets.QHBoxLayout(checkbox_widget) + checkbox_layout.addWidget(multiroot_label, 0) + checkbox_layout.addWidget(multiroot_checkbox, 1) + + path_widget_data = { + "key": "roots", + "multiplatform": True, + "label": "Roots" + } + singleroot_widget = PathWidget(path_widget_data, self) + multiroot_data = { + "key": "roots", + "label": "Roots", + "object_type": "path-widget", + "input_modifiers": { + "multiplatform": True + } + } + multiroot_widget = ModifiableDict(multiroot_data, self) + + main_layout = QtWidgets.QVBoxLayout(self) + main_layout.addWidget(checkbox_widget) + main_layout.addWidget(singleroot_widget) + main_layout.addWidget(multiroot_widget) self.multiroot_checkbox = multiroot_checkbox + self.singleroot_widget = singleroot_widget + self.multiroot_widget = multiroot_widget + multiroot_checkbox.stateChanged.connect(self._on_multiroot_checkbox) + def update_global_values(self, values): + print(values) + self.singleroot_widget.update_global_values(NOT_SET) + self.multiroot_widget.update_global_values(NOT_SET) + def _on_multiroot_checkbox(self): self.set_multiroot(self.multiroot_checkbox.isChecked()) @@ -110,5 +150,8 @@ class TemplatesWidget(QtWidgets.QWidget): def __init__(self, parent=None): super(TemplatesWidget, self).__init__(parent) + def update_global_values(self, values): + pass + TypeToKlass.types["anatomy"] = AnatomyWidget From f147579ad943baf56c4da157aea2b8cada39fbf0 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 3 Sep 2020 11:56:00 +0200 Subject: [PATCH 370/813] pass global values to subwidgets --- .../config_setting/config_setting/widgets/anatomy_inputs.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index 3148b8f32d..a0c94fb846 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -129,9 +129,8 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): multiroot_checkbox.stateChanged.connect(self._on_multiroot_checkbox) def update_global_values(self, values): - print(values) - self.singleroot_widget.update_global_values(NOT_SET) - self.multiroot_widget.update_global_values(NOT_SET) + self.singleroot_widget.update_global_values(values) + self.multiroot_widget.update_global_values(values) def _on_multiroot_checkbox(self): self.set_multiroot(self.multiroot_checkbox.isChecked()) From 462c350c0bdc25dff4700780a3d289f0c12f2e80 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 3 Sep 2020 11:57:05 +0200 Subject: [PATCH 371/813] modifiable dict has buttons at first place --- pype/tools/config_setting/config_setting/widgets/inputs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index c0c0ba3406..36bb8c9902 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1228,10 +1228,10 @@ class ModifiableDictItem(QtWidgets.QWidget, ConfigObject): self.add_btn.setProperty("btn-type", "tool-item") self.remove_btn.setProperty("btn-type", "tool-item") - layout.addWidget(self.key_input, 0) - layout.addWidget(self.value_input, 1) layout.addWidget(self.add_btn, 0) layout.addWidget(self.remove_btn, 0) + layout.addWidget(self.key_input, 0) + layout.addWidget(self.value_input, 1) self.setFocusProxy(self.value_input) From 93a0905b6f8be0f4607a8169a0c2ddf717a40d6d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 3 Sep 2020 12:11:58 +0200 Subject: [PATCH 372/813] ModifiedDictionary Item looks same as ListItem --- .../config_setting/widgets/inputs.py | 51 +++++++++---------- 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 36bb8c9902..441bc6c9a5 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1263,6 +1263,21 @@ class ModifiableDictItem(QtWidgets.QWidget, ConfigObject): def is_group(self): return self._parent.is_group + def on_add_clicked(self): + if self.value_input.isEnabled(): + self._parent.add_row(row=self.row() + 1) + else: + self.set_as_empty(False) + + def on_remove_clicked(self): + self._parent.remove_row(self) + + def set_as_empty(self, is_empty=True): + self.key_input.setEnabled(not is_empty) + self.value_input.setEnabled(not is_empty) + self.remove_btn.setEnabled(not is_empty) + self._on_value_change() + @property def any_parent_is_group(self): return self._parent.any_parent_is_group @@ -1289,16 +1304,6 @@ class ModifiableDictItem(QtWidgets.QWidget, ConfigObject): def row(self): return self._parent.input_fields.index(self) - def on_add_clicked(self): - self._parent.add_row(row=self.row() + 1) - - def on_remove_clicked(self): - if self.is_single: - self.value_input.clear_value() - self.key_input.setText("") - else: - self._parent.remove_row(self) - def config_value(self): key = self.key_input.text() value = self.value_input.item_value() @@ -1374,12 +1379,12 @@ class ModifiableDict(ExpandingWidget, InputObject): for item_key, item_value in self.default_value.items(): self.add_row(key=item_key, value=item_value) - if self.count() == 0: - self.add_row() - for old_input in old_inputs: self.remove_row(old_input) + if self.count() == 0: + self.add_row(is_empty=True) + self.start_value = self.item_value() self._is_modified = self.global_value != self.start_value @@ -1441,19 +1446,13 @@ class ModifiableDict(ExpandingWidget, InputObject): output.update(item_value) return output - def add_row(self, row=None, key=None, value=None): + def add_row(self, row=None, key=None, value=None, is_empty=False): # Create new item item_widget = ModifiableDictItem( self.object_type, self.input_modifiers, self, self.inputs_widget ) - - # Set/unset if new item is single item - current_count = self.count() - if current_count == 0: - item_widget.is_single = True - elif current_count == 1: - for _input_field in self.input_fields: - _input_field.is_single = False + if is_empty: + item_widget.set_as_empty() item_widget.value_changed.connect(self._on_value_change) @@ -1493,12 +1492,8 @@ class ModifiableDict(ExpandingWidget, InputObject): item_widget.setParent(None) item_widget.deleteLater() - current_count = self.count() - if current_count == 0: - self.add_row() - elif current_count == 1: - for _input_field in self.input_fields: - _input_field.is_single = True + if self.count() == 0: + self.add_row(is_empty=True) self._on_value_change() self.parent().updateGeometry() From 787d8516cbaa288b3c8c14c83c0f2fd53cfa52da Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 3 Sep 2020 13:21:11 +0200 Subject: [PATCH 373/813] hide inputs based on checkbox --- .../config_setting/config_setting/widgets/anatomy_inputs.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index a0c94fb846..943b8b0973 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -128,6 +128,8 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): multiroot_checkbox.stateChanged.connect(self._on_multiroot_checkbox) + self._on_multiroot_checkbox() + def update_global_values(self, values): self.singleroot_widget.update_global_values(values) self.multiroot_widget.update_global_values(values) @@ -142,6 +144,9 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): if is_multiroot != self.multiroot_checkbox.isChecked(): self.multiroot_checkbox.setChecked(is_multiroot) + self.singleroot_widget.setVisible(not is_multiroot) + self.multiroot_widget.setVisible(is_multiroot) + self.multiroot_changed.emit() From f505f3099ac79d9bdde4fd9c952576eb5672afc0 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 3 Sep 2020 13:53:00 +0200 Subject: [PATCH 374/813] modifiable dict is not based on expanding widget but using it inside --- .../config_setting/widgets/inputs.py | 56 ++++++++++++------- .../config_setting/widgets/widgets.py | 2 +- 2 files changed, 38 insertions(+), 20 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 441bc6c9a5..394a0f69ca 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1312,7 +1312,7 @@ class ModifiableDictItem(QtWidgets.QWidget, ConfigObject): return {key: value} -class ModifiableDict(ExpandingWidget, InputObject): +class ModifiableDict(QtWidgets.QWidget, InputObject): # Should be used only for dictionary with one datatype as value # TODO this is actually input field (do not care if is group or not) value_changed = QtCore.Signal(object) @@ -1320,7 +1320,7 @@ class ModifiableDict(ExpandingWidget, InputObject): def __init__( self, input_data, parent, as_widget=False, label_widget=None ): - super(ModifiableDict, self).__init__(input_data["label"], parent) + super(ModifiableDict, self).__init__(parent) self.setObjectName("ModifiableDict") self._parent = parent @@ -1330,10 +1330,6 @@ class ModifiableDict(ExpandingWidget, InputObject): self.global_value = NOT_SET self.start_value = NOT_SET - self.key = input_data["key"] - - self.input_fields = [] - any_parent_is_group = parent.is_group if not any_parent_is_group: any_parent_is_group = parent.any_parent_is_group @@ -1343,17 +1339,39 @@ class ModifiableDict(ExpandingWidget, InputObject): self._is_group = input_data.get("is_group", False) self._is_nullable = input_data.get("is_nullable", False) - inputs_widget = QtWidgets.QWidget(self) - inputs_widget.setAttribute(QtCore.Qt.WA_StyledBackground) + self.input_fields = [] - inputs_layout = QtWidgets.QVBoxLayout(inputs_widget) - inputs_layout.setContentsMargins(5, 5, 0, 5) - inputs_layout.setSpacing(5) + self.key = input_data["key"] - self.set_content_widget(inputs_widget) + main_layout = QtWidgets.QHBoxLayout(self) + main_layout.setContentsMargins(5, 5, 0, 5) + main_layout.setSpacing(0) - self.inputs_widget = inputs_widget - self.inputs_layout = inputs_layout + body_widget = ExpandingWidget(input_data["label"], self) + + main_layout.addWidget(body_widget) + + content_widget = QtWidgets.QWidget(self) + content_layout = QtWidgets.QVBoxLayout(content_widget) + content_layout.setContentsMargins(3, 3, 0, 3) + + body_widget.set_content_widget(content_widget) + + self.body_widget = body_widget + self.content_widget = content_widget + self.content_layout = content_layout + + self.label_widget = body_widget.label_widget + + self.setAttribute(QtCore.Qt.WA_StyledBackground) + + expandable = input_data.get("expandable", True) + if not expandable: + body_widget.hide_toolbox(hide_content=False) + else: + expanded = input_data.get("expanded", False) + if expanded: + body_widget.toggle_content() self.object_type = input_data["object_type"] self.default_value = input_data.get("default", NOT_SET) @@ -1449,7 +1467,7 @@ class ModifiableDict(ExpandingWidget, InputObject): def add_row(self, row=None, key=None, value=None, is_empty=False): # Create new item item_widget = ModifiableDictItem( - self.object_type, self.input_modifiers, self, self.inputs_widget + self.object_type, self.input_modifiers, self, self.content_widget ) if is_empty: item_widget.set_as_empty() @@ -1457,10 +1475,10 @@ class ModifiableDict(ExpandingWidget, InputObject): item_widget.value_changed.connect(self._on_value_change) if row is None: - self.inputs_layout.addWidget(item_widget) + self.content_layout.addWidget(item_widget) self.input_fields.append(item_widget) else: - self.inputs_layout.insertWidget(row, item_widget) + self.content_layout.insertWidget(row, item_widget) self.input_fields.insert(row, item_widget) previous_input = None @@ -1487,7 +1505,7 @@ class ModifiableDict(ExpandingWidget, InputObject): def remove_row(self, item_widget): item_widget.value_changed.disconnect() - self.inputs_layout.removeWidget(item_widget) + self.content_layout.removeWidget(item_widget) self.input_fields.remove(item_widget) item_widget.setParent(None) item_widget.deleteLater() @@ -1543,7 +1561,7 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): content_layout = QtWidgets.QVBoxLayout(content_widget) content_layout.setContentsMargins(3, 3, 0, 3) - body_widget.set_content_widget(content_widget, (4, 4, 0, 4)) + body_widget.set_content_widget(content_widget) self.body_widget = body_widget self.content_widget = content_widget diff --git a/pype/tools/config_setting/config_setting/widgets/widgets.py b/pype/tools/config_setting/config_setting/widgets/widgets.py index e9ba1b798c..fbbc3f26df 100644 --- a/pype/tools/config_setting/config_setting/widgets/widgets.py +++ b/pype/tools/config_setting/config_setting/widgets/widgets.py @@ -111,7 +111,7 @@ class ExpandingWidget(QtWidgets.QWidget): def set_content_widget(self, content_widget, margins=None): main_layout = QtWidgets.QVBoxLayout(self) if margins is None: - margins = (9, 9, 0, 9) + margins = (4, 4, 0, 4) main_layout.setContentsMargins(*margins) content_widget.setVisible(False) From 88e2a794d4d79eb6c4a57d922c7b6e1614e7e7e6 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 3 Sep 2020 16:23:46 +0200 Subject: [PATCH 375/813] added abstract class for objects --- .../config_setting/widgets/inputs.py | 76 +------- .../config_setting/widgets/widgets.py | 180 ++++++++++++++++++ 2 files changed, 185 insertions(+), 71 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 394a0f69ca..379b255359 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -2,6 +2,7 @@ import json import logging from Qt import QtWidgets, QtCore, QtGui from .widgets import ( + AbstractConfigObject, ExpandingWidget, NumberSpinBox, PathInput @@ -9,7 +10,7 @@ from .widgets import ( from .lib import NOT_SET, METADATA_KEY, TypeToKlass -class ConfigObject: +class ConfigObject(AbstractConfigObject): allow_actions = True default_state = "" @@ -72,10 +73,6 @@ class ConfigObject: @property def ignore_value_changes(self): """Most of attribute changes are ignored on value change when True.""" - if not hasattr(self, "_parent"): - raise NotImplementedError( - "Object {} does not have `_parent` attribute".format(self) - ) return self._parent.ignore_value_changes @ignore_value_changes.setter @@ -83,44 +80,12 @@ class ConfigObject: """Setter for global parent item to apply changes for all inputs.""" self._parent.ignore_value_changes = value - @property - def child_modified(self): - """Any children item is modified.""" - raise NotImplementedError( - "{} does not have implemented `child_modified`".format(self) - ) - - @property - def child_overriden(self): - """Any children item is overriden.""" - raise NotImplementedError( - "{} does not have implemented `child_overriden`".format(self) - ) - - @property - def child_invalid(self): - """Any children item does not have valid value.""" - raise NotImplementedError( - "{} does not have implemented `child_invalid`".format(self) - ) - - def get_invalid(self): - """Returns invalid items all down the hierarchy.""" - raise NotImplementedError( - "{} does not have implemented `get_invalid`".format(self) - ) - - def item_value(self): - """Value of an item without key.""" - raise NotImplementedError( - "Method `item_value` not implemented!" - ) - def config_value(self): """Output for saving changes or overrides.""" return {self.key: self.item_value()} - def style_state(self, is_invalid, is_overriden, is_modified): + @classmethod + def style_state(cls, is_invalid, is_overriden, is_modified): items = [] if is_invalid: items.append("invalid") @@ -129,54 +94,23 @@ class ConfigObject: items.append("overriden") if is_modified: items.append("modified") - return "-".join(items) or self.default_state - - def add_children_gui(self, child_configuration, values): - raise NotImplementedError( - "{} Method `add_children_gui` is not implemented!.".format( - repr(self) - ) - ) + return "-".join(items) or cls.default_state def _discard_changes(self): self.ignore_value_changes = True self.discard_changes() self.ignore_value_changes = False - def discard_changes(self): - raise NotImplementedError( - "{} Method `discard_changes` not implemented!".format( - repr(self) - ) - ) - def _remove_overrides(self): self.ignore_value_changes = True self.remove_overrides() self.ignore_value_changes = False - def remove_overrides(self): - raise NotImplementedError( - "{} Method `remove_overrides` not implemented!".format( - repr(self) - ) - ) - def _set_as_overriden(self): self.ignore_value_changes = True self.set_as_overriden() self.ignore_value_changes = False - def set_as_overriden(self): - raise NotImplementedError( - "Method `set_as_overriden` not implemented!" - ) - - def hierarchical_style_update(self): - raise NotImplementedError( - "Method `hierarchical_style_update` not implemented!" - ) - def mouseReleaseEvent(self, event): if self.allow_actions and event.button() == QtCore.Qt.RightButton: menu = QtWidgets.QMenu() diff --git a/pype/tools/config_setting/config_setting/widgets/widgets.py b/pype/tools/config_setting/config_setting/widgets/widgets.py index fbbc3f26df..42ca49d600 100644 --- a/pype/tools/config_setting/config_setting/widgets/widgets.py +++ b/pype/tools/config_setting/config_setting/widgets/widgets.py @@ -180,3 +180,183 @@ class UnsavedChangesDialog(QtWidgets.QDialog): def on_discard_pressed(self): self.done(2) + + +class AbstractConfigObject: + abstract_attributes = ("_parent", ) + + def __getattr__(self, name): + if name in self.abstract_attributes: + raise NotImplementedError( + "Attribute `{}` is not implemented. {}".format(name, self) + ) + return super(AbstractConfigObject, self).__getattribute__(name) + + @property + def log(self): + raise NotImplementedError( + "{} does not have implemented `log`".format(self) + ) + + @property + def is_modified(self): + """Has object any changes that require saving.""" + raise NotImplementedError( + "{} does not have implemented `is_modified`".format(self) + ) + + @property + def is_overriden(self): + """Is object overriden so should be saved to overrides.""" + raise NotImplementedError( + "{} does not have implemented `is_overriden`".format(self) + ) + + @property + def was_overriden(self): + """Initial state after applying overrides.""" + raise NotImplementedError( + "{} does not have implemented `was_overriden`".format(self) + ) + + @property + def is_invalid(self): + """Value set in is not valid.""" + raise NotImplementedError( + "{} does not have implemented `is_invalid`".format(self) + ) + + @property + def is_group(self): + """Value set in is not valid.""" + raise NotImplementedError( + "{} does not have implemented `is_group`".format(self) + ) + + @property + def is_nullable(self): + raise NotImplementedError( + "{} does not have implemented `is_nullable`".format(self) + ) + + @property + def is_overidable(self): + """Should care about overrides.""" + raise NotImplementedError( + "{} does not have implemented `is_overidable`".format(self) + ) + + def any_parent_overriden(self): + """Any of parent object up to top hiearchy is overriden.""" + raise NotImplementedError( + "{} does not have implemented `any_parent_overriden`".format(self) + ) + + @property + def ignore_value_changes(self): + """Most of attribute changes are ignored on value change when True.""" + raise NotImplementedError( + "{} does not have implemented `ignore_value_changes`".format(self) + ) + + @ignore_value_changes.setter + def ignore_value_changes(self, value): + """Setter for global parent item to apply changes for all inputs.""" + raise NotImplementedError(( + "{} does not have implemented setter method `ignore_value_changes`" + ).format(self)) + + @property + def child_modified(self): + """Any children item is modified.""" + raise NotImplementedError( + "{} does not have implemented `child_modified`".format(self) + ) + + @property + def child_overriden(self): + """Any children item is overriden.""" + raise NotImplementedError( + "{} does not have implemented `child_overriden`".format(self) + ) + + @property + def child_invalid(self): + """Any children item does not have valid value.""" + raise NotImplementedError( + "{} does not have implemented `child_invalid`".format(self) + ) + + def get_invalid(self): + """Returns invalid items all down the hierarchy.""" + raise NotImplementedError( + "{} does not have implemented `get_invalid`".format(self) + ) + + def item_value(self): + """Value of an item without key.""" + raise NotImplementedError( + "Method `item_value` not implemented!" + ) + + def config_value(self): + """Output for saving changes or overrides.""" + return {self.key: self.item_value()} + + @classmethod + def style_state(cls, is_invalid, is_overriden, is_modified): + items = [] + if is_invalid: + items.append("invalid") + else: + if is_overriden: + items.append("overriden") + if is_modified: + items.append("modified") + return "-".join(items) or cls.default_state + + def add_children_gui(self, child_configuration, values): + raise NotImplementedError( + "{} Method `add_children_gui` is not implemented!.".format( + repr(self) + ) + ) + + def _discard_changes(self): + self.ignore_value_changes = True + self.discard_changes() + self.ignore_value_changes = False + + def discard_changes(self): + raise NotImplementedError( + "{} Method `discard_changes` not implemented!".format( + repr(self) + ) + ) + + def _remove_overrides(self): + self.ignore_value_changes = True + self.remove_overrides() + self.ignore_value_changes = False + + def remove_overrides(self): + raise NotImplementedError( + "{} Method `remove_overrides` not implemented!".format( + repr(self) + ) + ) + + def _set_as_overriden(self): + self.ignore_value_changes = True + self.set_as_overriden() + self.ignore_value_changes = False + + def set_as_overriden(self): + raise NotImplementedError( + "Method `set_as_overriden` not implemented!" + ) + + def hierarchical_style_update(self): + raise NotImplementedError( + "Method `hierarchical_style_update` not implemented!" + ) From 3ef141c8cadcd77caba894517764ff9730750716 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 3 Sep 2020 16:24:08 +0200 Subject: [PATCH 376/813] Anatmy widget updated style changes --- .../config_setting/style/style.css | 21 +-- .../config_setting/widgets/anatomy_inputs.py | 172 ++++++++++++++++-- 2 files changed, 167 insertions(+), 26 deletions(-) diff --git a/pype/tools/config_setting/config_setting/style/style.css b/pype/tools/config_setting/config_setting/style/style.css index 937259ba7b..638bf1c6fb 100644 --- a/pype/tools/config_setting/config_setting/style/style.css +++ b/pype/tools/config_setting/config_setting/style/style.css @@ -123,8 +123,7 @@ QPushButton[btn-type="expand-toggle"] { #DictLabel { font-weight: bold; } - -#ModifiableDict, #DictWidget { +#ModifiableDict, #DictWidget, #AnatomyWidget { border-style: solid; border-color: #455c6e; border-left-width: 3px; @@ -132,36 +131,36 @@ QPushButton[btn-type="expand-toggle"] { border-right-width: 0px; border-top-width: 0px; } -#ModifiableDict:hover, #DictWidget:hover { +#ModifiableDict:hover, #DictWidget:hover, #AnatomyWidget:hover { border-color: #62839d; } -#ModifiableDict[state="child-modified"], #DictWidget[state="child-modified"] { +#ModifiableDict[state="child-modified"], #DictWidget[state="child-modified"], #AnatomyWidget[state="child-modified"] { border-color: #106aa2; } -#ModifiableDict[state="child-modified"]:hover, #DictWidget[state="child-modified"]:hover { +#ModifiableDict[state="child-modified"]:hover, #DictWidget[state="child-modified"]:hover, #AnatomyWidget[state="child-modified"]:hover { border-color: #137cbd; } -#ModifiableDict[state="child-invalid"], #DictWidget[state="child-invalid"] { +#ModifiableDict[state="child-invalid"], #DictWidget[state="child-invalid"], #AnatomyWidget[state="child-invalid"] { border-color: #ad2e2e; } -#ModifiableDict[state="child-invalid"]:hover, #DictWidget[state="child-invalid"]:hover { +#ModifiableDict[state="child-invalid"]:hover, #DictWidget[state="child-invalid"]:hover, #AnatomyWidget[state="child-invalid"]:hover { border-color: #c93636; } -#ExpandingWidget[state="child-overriden"], #ModifiableDict[state="child-overriden"], #DictWidget[state="child-overriden"] { +#ModifiableDict[state="child-overriden"], #DictWidget[state="child-overriden"], #AnatomyWidget[state="child-overriden"] { border-color: #e67300; } -#ExpandingWidget[state="child-overriden"]:hover, #ModifiableDict[state="child-overriden"]:hover, #DictWidget[state="child-overriden"]:hover { +#ModifiableDict[state="child-overriden"]:hover, #DictWidget[state="child-overriden"]:hover, #AnatomyWidget[state="child-overriden"]:hover { border-color: #ff8c1a; } -#ExpandingWidget[state="child-overriden-modified"], #ModifiableDict[state="child-overriden-modified"], #DictWidget[state="child-overriden-modified"] { +#ModifiableDict[state="child-overriden-modified"], #DictWidget[state="child-overriden-modified"], #AnatomyWidget[state="child-modified"] { border-color: #106aa2; } -#ExpandingWidget[state="child-overriden-modified"]:hover, #ModifiableDict[state="child-overriden-modified"]:hover, #DictWidget[state="child-overriden-modified"]:hover { +#ModifiableDict[state="child-overriden-modified"]:hover, #DictWidget[state="child-overriden-modified"]:hover, #AnatomyWidget[state="child-modified"]:hover { border-color: #137cbd; } diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index 943b8b0973..11d94319e6 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -1,9 +1,10 @@ from Qt import QtWidgets, QtCore -from .inputs import ConfigObject, InputObject, ModifiableDict, PathWidget +from .widgets import ExpandingWidget +from .inputs import ConfigObject, ModifiableDict, PathWidget from .lib import NOT_SET, TypeToKlass -class AnatomyWidget(QtWidgets.QWidget, InputObject): +class AnatomyWidget(QtWidgets.QWidget, ConfigObject): value_changed = QtCore.Signal(object) template_keys = ( "project[name]", @@ -33,10 +34,16 @@ class AnatomyWidget(QtWidgets.QWidget, InputObject): def __init__( self, input_data, parent, as_widget=False, label_widget=None ): + if as_widget: + raise TypeError( + "`AnatomyWidget` does not allow to be used as widget." + ) super(AnatomyWidget, self).__init__(parent) - + self.setObjectName("AnatomyWidget") self._parent = parent - self._as_widget = as_widget + + self._child_state = None + self._state = None self._is_group = True @@ -50,14 +57,26 @@ class AnatomyWidget(QtWidgets.QWidget, InputObject): self.root_widget = RootsWidget(self) self.templates_widget = TemplatesWidget(self) - layout = QtWidgets.QVBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(5) + self.setAttribute(QtCore.Qt.WA_StyledBackground) - label = QtWidgets.QLabel("Anatomy", self) - layout.addWidget(label) - layout.addWidget(self.root_widget) - layout.addWidget(self.templates_widget) + body_widget = ExpandingWidget("Anatomy", self) + + layout = QtWidgets.QVBoxLayout(self) + layout.setContentsMargins(5, 5, 0, 5) + layout.setSpacing(0) + layout.addWidget(body_widget) + + content_widget = QtWidgets.QWidget(body_widget) + content_layout = QtWidgets.QVBoxLayout(content_widget) + content_layout.setContentsMargins(0, 0, 0, 0) + content_layout.setSpacing(5) + + content_layout.addWidget(self.root_widget) + content_layout.addWidget(self.templates_widget) + + body_widget.set_content_widget(content_widget) + + self.label_widget = body_widget.label_widget def update_global_values(self, values): print("* update_global_values") @@ -73,8 +92,63 @@ class AnatomyWidget(QtWidgets.QWidget, InputObject): def _on_value_change(self, item=None): print("* _on_value_change") - def update_style(self): + def update_style(self, is_overriden=None): print("* update_style") + child_modified = self.child_modified + child_invalid = self.child_invalid + child_state = self.style_state( + child_invalid, self.child_overriden, child_modified + ) + if child_state: + child_state = "child-{}".format(child_state) + + if child_state != self._child_state: + self.setProperty("state", child_state) + self.style().polish(self) + self._child_state = child_state + + state = self.style_state( + child_invalid, self.is_overriden, self.is_modified + ) + if self._state == state: + return + + self.label_widget.setProperty("state", state) + self.label_widget.style().polish(self.label_widget) + + self._state = state + + def hierarchical_style_update(self): + self.root_widget.hierarchical_style_update() + self.templates_widget.hierarchical_style_update() + self.update_style() + + @property + def is_modified(self): + return self._is_modified or self.child_modified + + @property + def child_modified(self): + return ( + self.root_widget.child_modified + or self.templates_widget.child_modified + ) + + @property + def child_overriden(self): + return ( + self.root_widget.is_overriden + or self.root_widget.child_overriden + or self.templates_widget.is_overriden + or self.templates_widget.child_overriden + ) + + @property + def child_invalid(self): + return ( + self.root_widget.child_invalid + or self.templates_widget.child_invalid + ) def item_value(self): print("* item_value") @@ -85,6 +159,7 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): def __init__(self, parent): super(RootsWidget, self).__init__(parent) + self.setObjectName("RootsWidget") self._parent = parent self._is_group = True @@ -111,6 +186,7 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): "key": "roots", "label": "Roots", "object_type": "path-widget", + "expandable": False, "input_modifiers": { "multiplatform": True } @@ -130,18 +206,26 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): self._on_multiroot_checkbox() + @property + def is_multiroot(self): + return self.multiroot_checkbox.isChecked() + def update_global_values(self, values): self.singleroot_widget.update_global_values(values) self.multiroot_widget.update_global_values(values) + def hierarchical_style_update(self): + self.singleroot_widget.hierarchical_style_update() + self.multiroot_widget.hierarchical_style_update() + def _on_multiroot_checkbox(self): - self.set_multiroot(self.multiroot_checkbox.isChecked()) + self.set_multiroot(self.is_multiroot) def set_multiroot(self, is_multiroot=None): if is_multiroot is None: - is_multiroot = not self.multiroot_checkbox.isChecked() + is_multiroot = not self.is_multiroot - if is_multiroot != self.multiroot_checkbox.isChecked(): + if is_multiroot != self.is_multiroot: self.multiroot_checkbox.setChecked(is_multiroot) self.singleroot_widget.setVisible(not is_multiroot) @@ -149,6 +233,41 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): self.multiroot_changed.emit() + @property + def is_modified(self): + return self._is_modified or self.child_modified + + @property + def is_overriden(self): + return self._is_overriden + + @property + def child_modified(self): + if self.is_multiroot: + return self.multiroot_widget.child_modified + else: + return self.singleroot_widget.child_modified + + @property + def child_overriden(self): + if self.is_multiroot: + return ( + self.multiroot_widget.is_overriden + or self.multiroot_widget.child_overriden + ) + else: + return ( + self.singleroot_widget.is_overriden + or self.singleroot_widget.child_overriden + ) + + @property + def child_invalid(self): + if self.is_multiroot: + return self.multiroot_widget.child_invalid + else: + return self.singleroot_widget.child_invalid + class TemplatesWidget(QtWidgets.QWidget): def __init__(self, parent=None): @@ -157,5 +276,28 @@ class TemplatesWidget(QtWidgets.QWidget): def update_global_values(self, values): pass + def hierarchical_style_update(self): + pass + + @property + def is_modified(self): + return False + + @property + def is_overriden(self): + return False + + @property + def child_modified(self): + return False + + @property + def child_overriden(self): + return False + + @property + def child_invalid(self): + return False + TypeToKlass.types["anatomy"] = AnatomyWidget From 821a9c2dacae9952830ad7412b717a7885e766eb Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 3 Sep 2020 18:07:11 +0200 Subject: [PATCH 377/813] fixed maya icon --- pype/resources/app_icons/maya.png | Bin 41557 -> 122413 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/pype/resources/app_icons/maya.png b/pype/resources/app_icons/maya.png index e84a6a3742f325769d6cf619045d17107d925f3c..95c605f50d6d4459a2c1028922a44affe0d11161 100644 GIT binary patch literal 122413 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 z
JB1!+ZugKmP2j3T~beu)-KC%*OFtdDPgP(^<(=vkx8Q!s4PP~o=W!s!}jg6kQ z4nWrE4o;vbEp!#6)A`qx7_hCvzW(LKw(-(vvLzPuG@=|6Vg1?wBV%)6Y>912*UxES zA`;iUa~4C(iNf#BoXhHdR->}?{jmxs8;8mQi@Q3$f^e?9pl4+^J>dM|lv&BBK z`F6(oAU6Qhd^Yb#d9)AkpRnwHKTblU&HWXNTt?Fu`T`aRA6^U zc(}!->!PlS$;kzSkeuIW0}HzMpLf6Qv+2E*@WOA|XzFiRm~FeB>mU-BdD^=}1kU^W zp%K5YH7S$Qe7^DS!kWGq@j#^6<-c|kizan9MDCO23J06rFYM+TO9=^KB(kc1M0E?_ zi!*=RSayXR8Y6O4S=pw(az&N(JI~kQiyb++QZqvNBjLx(;k?vhU12pAwlwFGpCncC zBCN5Ljy;=XzT+Gztt-kVi}j6_#4C^iF0p;TpZe9&bf`0^uxElkE)l(u8PfXi?G|5C z;Fc)bPY@Tl3Z7(R7&XgsZ6Pwg@M1}?Em2&*y6^MJM~g?c!OM1}%vNFR)B%oK7-uKh z`K&6IU^*IA>y1D;+u#FLmkoR&gEsdSfF=x%%CC}&5MOJO`~vG8>85r|Av1h7vo^PF zQbEAgopLW#_GeuBBP+zWV!y<2L%oyp{j5SdM8#cFza0reYXyJDBTmfL`{A3C3p_qy z<5v}4Tgme)G-!_vm-g%L|G(w$y<;U+rd>R(ca({+r8DugK`` z$J0-PPg`O7x_YC4WWwasTcSTLHbi)7#4a|ZymqrVpMi-<%uFs)myQlneYf6G2Jvb`#)jm{F>>I&Q63$g`m7tRaG`m_}!wI`a zRnZTpgxemibj-i}t2;{R1fjjFbNE=V8bMq?y`e4^?ZB4jto`Jy-Lzgo6bbrUa8RU1 zTru~Ps@gW9dsOAPno)*yHJJn&q<5b;9o4`)dc((A$XpS5-5D6X=kn5#5gwC6g5H1S z$~Rkt=6J7Vp5eVMri7d}(NEDa#*w`?yLEBBXHokHGi}x><%EvtiE_+m1c5o>@fw`m zcg@cZmgDK-tvgdD-F~}ahE(Gt3q023MjS8CKLKTKu9){?1kRsuQABR>a^aiD8j9Cw zfC0eiQZ3=L*Dd`8A-C-iJrnrpN`wWqmicCwqK($ue&Uy%qQMPh+I|Rn6QR`QkN%-N zPI{0D=WsAkZfkom=npUkP7*wp&IVLYgiH%f%P#68=93-hiXe)HHzgUD+J#KM_Jg11 z+X5*XY3HN9*76aJK7;KaNq}K7X zb92eV;v(4c4GD=zy2qd7uQc$&`{dg-RKw|UJ`>--IPwmufw46U_j&huj>HplVxg~g zI{SW=A$B|`f6{bsULPp?v!cGee)AC9n3Oq#9J#Xn#m;iBwetm1&hBQJRWH3{9L>?X z&k3w*4L&^k?Y6r*J0Ib@cHG%giBJkRV|AgIsHoi5nH?&tw|-fpXIhC0$*V=(D@5<0PyWrM8HQQ`=;v&x^aOnYhGp1D~ zKY)W4(lJf`5y|x`co$nJOH^HOZAEmayf;e`~sr(^?E zLy$7|^CeQ=j$1l(uRoT87(aq4Ui3-gy}4iY+l}Gt4%_OL!9Q6>MJge9>G|+B_&jip zBsu$HFGSZY=!l|W>9T5Oe>HGk!L=vs8G8Lb0n+Ev#qeJ=%J*9}8~XWP(@gh8EqFO(el&cvYi_?TX%?Y+_{NE- zY#R;yxnW|L)ucK*YVkeXo8%+r|DoxuqT1@ZH5^=vyB8=@tU!@s!70T{vEo)7id%4} zxO zV2&cDy0V_z+DD<}Jh3tM9j%y8+l{{`EVtiZoSaXoEl#gsTx5y7i8Q6*MzN|L6BB=t z;?x?3nW}#M`rUgg1dHqYUz5H>rSmBN{|+)L@x1vUhti>GD|7|FJ0}m3h3`1Y*xE_g5F3$U@*|fT2=GcDb7E|i*voSdT z6L||VL_j|d>;jg{2Z}#7T!*w(88Y#OW{-vCy{IzK)2C=?NU_sM0T{)MRU~V#nyZe* ztU?y~zy%%u0CkMbRM&4|Wfw0CPw_hgHNk4C3BS!jpFL-aHC{e~=Tdr;`1kfUwszz= z6s}tbWF55(NjqwNDTvT4z69q4ZR;#^rJ#>q>FeltK0h6Rfv!i>g`LhTNLcW89~qht zWM!k{5DWa;lNgaN&Eo>*!D0d=O`vD>UD!P2h*pz=7h8M5yVjsW&@Yj_mEp!=U6&#d z$?(C#lT`WrUgVVols(WPxpB6S{;{;JoikH;C%`DVCa~vg0mDzDlOL;Xr^JUMtH?`= zvLZIn$r+pes`PAdBM2RwqOkmtUnJ@*I(l+#84;dgrKBVWk=`gmt{ZCbAbTgBJNK_p z=!IqWjJWI2iXZdwYr{kA98+B5YYqH|$b}leiqp2$`o8ZM#VtRO-o5hu_#DQa29K+O zpPa{OK-&?_aOpn^{O>3FO#3n@R}8!9gxy?>jEtzLC4Nh z?2W$ciW!RwVfXhJ56}WIQE}K`_xRHX7h!Y!>Xa+&Xo#{Viv6I0${JjOp+>Hd@;4)d z0~;L^*?KT2u$n#|VH2<9drDOp2@yI{NNY(0J11(%$L*4fv1{0WQ^#0!h7>ZWl? zcV}K(k0*1%I#bHua&mL1!TmzkkfD$z8YRUge1D3GM-qF#Sk7|>NFXKd5p`R zZwxmv^5l)!=>ZwfW;}%cRy(8KUKKA(^KaZbtT<&pTJf)IkgcZPThUMj&&R*j*4D!1 zz9f3NqgD?$i%43IDub)Zu`i=|C)(N?8r;s{bcX5?dptRQ_%()T*&T@T&VB8!c0)0f z-|j6EuY$owt@R6qra0C4?p_;QSntBVtRb{YJ=(qMChhhS){$nreTeGqEs^;0Kwd8FGS=|X@Cv3R%@G(N#&6xX*&_f|1F z+PJ0^k)ZG0XL!oJGqBi~f0w>rCi{v7`Q9IP{!0(nhD2noFHv{hpkwJ(R@OU52P(wQ z`7!`g-ntHSSlKfr?~L>bYPsbuxCs9njZykbjEMpb^4Hy#h$~H`&x@$JqX6UcQ*(+o z+|x3VDR7a|vA%wwSz)k-5pxZ<^$x-zE+bd}xhS@pZ}vABEzY5gK%tXVd4G|wza!X0 zcz!tH;AXZ6Z36kL0{_}-jyJ?lOulsPndyzkC4WY*vlM4sQq7*NIW(0(_}zD#5%~z@!L@2%)B#LrvA2rj8}U7!r;8@ z^YUnT%<*IG6SGg$NYH%$VJx{C!YAJ(N7-F-btN>j?*t4Qac;?z8YShBVk*z$w7a`S zfk`y0ej@`{4%Kf#YU+;2K{uZ>L-ZfpH`6`n)!e$D4!D-89d~vh_h*zSa`EtHuIKc` z1?-7=kQG9%3WdUnrqX{#NH=T;tg;#Rl^$Emhom=aw zJNx!g64=^C6Ttg{Fw{0e|8_b2i=~di*0m7D=E-ka>U_?2C0U(jAc^T>@NHE!xLXTC zO7wDio$i83>25oqG-432FY3*mNFy~^6Yle@D0`^s`;(eXD5+|&HVz%QONAh{-cRaf zVr@pYjLTqLqH5olm7`uB6D+H%wiydB7*M_-dE}$1JUj^{JynUd&VgRRc zgwD1QMkbubB|=y*Uigk*K=-CL1zb3S^6||*gcu`OW!ui0 zCqD3P!Y&079)(JH!cXdeHwayego?Ww;aqW8xsudMM0Rgu2@6Boay&w&{qKEN86)EN z7t?ue-9I1a_2m67OpzAL#VZ7!<_)y)Qm->|9~_ z(_iz;tmQi2uPwYB*uo-AplaEJk5iPNcaNOX&yaLvdpYF8r@20xRBnakQoEKLoXCeC zr_=tXIi7je{x*kpTy~3a(i++p=3sVLF%E_qTupboSu7k}4?N@GXWOBMEmNVb{er<}M!s(N?Q1KU*Hkx@z1h$K5|0qbg!;_6N7_Yv^Rqxj z@i2hGMVA}xey=d*HTi%octrYixbA)N0&&-OdjdKg++|0PW;YicKLGsKNkM-1kq;NE z*j0_xsObAsUqkiKT>qkX(9>#k)|X##;4jiON$!3!oQe=rSkB4ny_t(BG481(68!l1?FDDmax1v%m8 zKllS8d6>BY?eEESfUTPTO-qfd7WJP*p z#o*xLEgo5ZIcea_h|?)yn`$JX2j0ILfXt1HHaOm5t4uq-*YJ z@xIF6O42!!{&@5%D@2vF#2EHG(_*O03$R_+#ZTB0Wg#FhTkH(CGyky>gOGh6Z#vN; zW@g@8rTE`2x$>h;lgoBn4Bu%i<`s5|z81Mr-&+^**ODke7K;CFz5TCUkkg>aa({yq zI7~KWc(1XjHvi=n{lTbbm0ma-SnL9%*7|0?rumOB`KnkSi~Jc;?L~Sa8ItYwt*)a$ zxn0y|11gtFtMiHq(A=KXY__G zh}#D{gi}p&u3HaGN~NMU<5{(#w0_X5g|2uBdqkr=o66>vXW@;f^V;O(}P z0SuFBu?0mgEbNhTB%v_YEj$}W>85nSYVO#fS=5co3Qi^d?x$K=q4+&jiP^D&OYM?} z9Ov#{65a|FoLoa18veqr%!H3W_O3LS7SH*#w$Uto1=_n`W(;E(nXtgH=!8Gd{e#|AcmJT__t|^0(=((&v#|zreM@0Ab8?Wr;!YVt(U6u0#YQRS6=9I-FJulGDRiKs zUqz)Y(;N7>X0pv)0Q>GvO2?ItD4B@x^{Y=p&U>uhv=F@+do2iEkZOG9xE_)jPF4=dbh|D(ymsWXxggslp#3hj=?l9)?6$#2$!7 zdd@Nzw(r;N0f3;Y)!M;Ybl)VAJvMWhcL+Mm#VM}43A;{LSn@n3n*Xz(#MRh>y!>1| z$Z&0!urpKI5znI0xD&r%SZq-vNDnqOteN72zS~z`>mUQm6)Az`SL%+p^K@brjfPnW+z_Z4E!|!Eb zC-LNX|B8_gGQjdpP{DT;jqzA!*SFT4w9Vr&w-ZS`=yA{dQOx2*3`9SX^qPk#a2(;V z&DSN)skXa(zVi#WV^~2M=Tu@CdwrptVql9E!d5M358TrMBF$ZZmR2b#7 zFE>p5O2WruQ7p?;VU=$Hw<59U?ii^dSVy{)w)ENY)0lLd5+FB=m#!4K=GI)^>5gmY zaekfEC<=J<0$7H;d(3N$0x~XK!+tWoX@z)g*09dJd?Ps1AGLfvzAyuHkmXXpJKZ|1 z9dCh7=Jgxetv3C^rE10i6o=9HKBl;LI8PniIfSjmfNw1-p)BJc(Br$|q?|Sf$Pc4# z<|zc|63f8|<0oRx#08cFeML_K392!l$8Y^#Q}WeWg3~Vu5EkG1qVzC=d}wP;6x4H4 zxacI&b?E^%1cYAUOSU8{xcoi)dsUmTu}mYEpLhYCtOfT+Hz!>>bRVP-YXhb=JLHBj zl5zilQ*CBgxjcIsIq)a%Wrhy6dB=);(+6vG$c|5X;diNVNfqwMd$zwNb+eazD`|Nh zT`z;T8a=o=h&9aJe>$wM@2)JKML2#PiB)jD{XpneY)*c`?gM4H3ma1C(37OvP#bzC zP+Ei#19EL#kRj>?Kf8C;CvQ$Kh9@7mln5B@-hUx_cQ7V=kp+ZZg)qZGcs&Wu$`o)u z4B+uttJ7eo;IT2S-CFGDR$<41Vd83=F>82OmKgl8hpR(pruXYw$KE%o?JG$q z4lD1*HF>*$#7kN&3W?D2SmL?lwBS$>VaH7m1P61 z%^q}P!;;U$FCp$`Kk?kEbdhuobWzthICd3$Nb+00V*280AXz%y180j6IcO_n0;ZKI zGjGA{Nwel0ANBkSDn)KvRdnvwWOPkb!uekF_Mz-x;>!K6q5qjp0Q40b&m!Bi ze-EBG04 zUwasPW(9F5 z%(zTVaI4w9O{L=jG!-;brYhR`Z)@w#UU*WR;y%X&TvpZRhh4!Tn9l18TshOJoO6c%7}ei}b(bWz~BA0a(#4Ae!rh zm->DY{hE{UroJTjNF%;PK~xwaL_|i@L+9ulX2!#LKt&ZtSpS)>v8{);T`OM?+L!@F z$kJC0Ru+XuK1awgVWP_Dq1PI60I+&XFR-l5h+4S<^p^k=QIjG#*k9RYQh=8sd za zeV$QJHt`=julA4e54xeDn_-!nTiX0m^vJIBw&%SQ)LV#gpxmpwd|e)1DV7s&f&G|G(KVP$zL&qe?mAETu?k%-t9IW(r8+-B z0u1sZ{@77Kth)+nD^&E&)(WQ8>~NqfQ}!Gp?1=H&UFiTVJ18KIsssDU?%iD_MqurN zLOg+ca~g3hoN6GBg-47V`E$1WM0AXe$Vn@CTO+x{NA2Cc#a+?a{#Yhjsat%1D*TRV zF$JAr;qFNpO6UGoWjnt|h)jLN(8JNDIB+&KWovZUdhdlkF?fs@;TASG5heI?JHvH! zbo6xgpN@Eok@3QV?PuHJ{fa|pz47;6;W6}8W|)}T_!GTuPa01zT;^o9IbLy+3+%&9 z-<+|2FEy<7<1YW}b=Z)_X94P~=iXh8Xv9p;I-iCb+xU`QHUt_tcf>_vM{*2B9EKbH z&1s~p!Gy$9hFm5Kj_g~RLO3yerq#ZJY)C)?f(ZKXHFSD)p8Y-96#tpL4)WN=elHy@ z)BAys&A}#@KFm>Pu&-ZK@8e1O`>BQfBcBi^gx}s@AVmyGShFHHzFVR-YR=*h=f1W{ zR7<{3c?gS*Nb#3UI2$3Gi{8o`Wnal>PmFYH+E$&y57g;XvKeFITd?3QvmM>FJosbh0*1F7O%-i6ydU`^`no7KCX%`G*VQ5ND&#)CfH87GSw?6 zg>#C<-m7e2=(&GNQS~lPd(O)T3#Hs{DNRtR`q1kEYXFw=J>OCQ_3j}Y7`(}2UF$Hh z$Jf(#tF#{j8cdHlnL0KW_P3s-V(M-w{k$hJAsp9>2Z!~|{@eLih>65?tjIj`L;)HvAX%)HBnSe%*Hmu*e! z#!ndNh$11$1&@3Xe{82AwIqr@^tZTH^&hG*W&Rk|3o_b~K;#x4U`N5*u}GX8<82*T zCX=jRV!7)0fWlHiRi|C^VX%nMQygC+_0hsSYs?PL@XF7uPDrE?QQ zZ{`L4sCNjTxw8$6X?L_<2%qY3XvKNe)m>aJ45Da&$|Dd+bz2oUGG8v6v*vDc4lMXN zP9~ekm4o!(iVL-G3D<64ojx1nQF~@FMr6T>?(Fen zcr6b3|Fr-a-;LTF$G+2J?uFDDvMf-MJhJM&KRhn0@?C-XKwvjF6P+#m=w5S52%vLZ z*ye`bUB*J&3YRm6T*!U(n9SOum33u^w*gdaKOIT8vWoT1KZYzp1R0tx2G1a#<9xck zSxu&;B5ldn$9Q>xXKU=#QkCa7(RVWid;($J)WVnT-kmWOxPGMSrWzBVepQV)edY$l zQP=v4{-TUmd)uDa(Ap;(OM$L$TCB&n^^{J4G94uQ#t(IVv^miJm&N+3Y@?uY`~)=ZGI}%yzgb z<=4V=IzUe{o!3Kt4;KptE%zFC${-DH04;1XOaOGP1$#PuL@Uc+2 zdWwDVYzKOqQ+D`mlOY?LpL4ufTrhi@DBcXJ{$!hm9P+L*REPlkNbNLEooN2UlWfST z7(n93TabiW4mO8H9;;gaTYc+`#N+&#?IKy^B1@ryZ={}>eR;IOzMp-(6wmlgJ$~Y? z(0UWKHj0I*6YltD9aQ};#pjQ^HZMxiNqd#1Ir#Hc1>FhH6hvc5W9Y!Vw*;;%(D9*K>HaBUs~(yKhnS*Te{*kiv;I!raf1gA!SV zR?MX2tn4H-rnbBN!*U{)w3msUiSR+K+Q~$IWzd--ED5tqx7nhoKuiGG z>arX&4?&0CQ!h};mc3~c@glfb_4wrVN6yH2hF$gNm;^zG%c~NF4VV7IGE4($LiDl= ziEBs0sjXB9VEDLA3cG~UQoM?b)82P5_4%?*JZ2R3=Lq^Hb+nwk>-u1`8J@3@uZlk% zfq{~uV_{f}1AM4wPnHx28wIzhAn5v3G58qIU|xr%DMwk!*?~i#LQuhBfs}zqNnKxR zZlsUgP6=)zcgO}_*WMq+K!Muln{vNZ4-128oV*P*GeXs#&5P6{TlzbCe=i=Bfa{45 zr5_2JoyOx|gd#hXJKw)@&o_ZS>b`jq1_~=M4u{2oR-^W6f~>i2b2<>2Q1wD)I7 z{?y}{)zmHRIU3wP~ZsrA{pw_}p0z@y%GV!;|R;yh~0 zy`(ZRR3g2|-3V4KE9o8`Q%sy$2x-((Iyv^)$}iI}9FwDIb8Yw_AUra6e5EA|dC3aF zJ0J$GIymVbrsp%N2X#nkJ)sGw5s!#6Z$C90*zZ!0b5oE*2Q!JN!Tuv*wl|F0!qYe| zyDE`($?2F6r|36Y+iWw-yxgHSsGL2a$!u?Wd-e?+cvjVs1H136mTg8-I9eY|7xY5@ zyS!hCs{(g4rVyH*_A^so*Dqk}S`e*@7J7qX%Gl$bsfIRG_ctyRBf3|^heOgdF`6^@ zO=sP1JczM!^*kK1M2lLC+H7*0<{7)*i)!M^C0ueWSgUAVdMZeiQo6|--lT%R{SYQs zSF!Iqo3af&c8#gl8e_j#e3GQhj?OKU?J6H?ei8%RXT_5vV1c5^QTsZ6@G>vh&TTDR zXWglw!Rob574>5-XP_F7q`B#TnPLyZx%7g11+o zzRb?OoQ;CXsEpcVe>9~-PF5B)>_6NuDU`pvD$+mfKPnCFipx@wK$)6}Jno{Nn`rO1 zy{o+5MW;t2*3<$V$K%$_?!JG%Zj(%2z1E009h?WD&pcE9#vcH^5{B>PWR;Wlb?e_< zsP9fI6TUn(f1+AzB~|g+@r6H1_=;InBo9A8c}0fKf_G74MGbXA3@EN{x5tGmqqBYc z4Zn4`CH2EA^j@x|261j}Vw8u$>$6n)s+m+d{W!Y{Z<}3&%;GxQPlmLaVj-QY&H4_*$xI3c^Db~U0RQF9>-~nW zvV=!BHK_>F&jqye?~oxl<+sW^m0d?3d4m6MaSQTavqjF%zN+69>Hb$s#-`97-eazD2TBQPIk2+KFRvK+fg?>BNLt7U`&^P^V zHZ;s(G24e7?EtcuGR*>up#-|_-<&HJ0ievll8Vd2x`Rv*EdM|ly(HQBLk$P-9lhlZ z^bJOgS%+#JaYq%k*hX_#C*!7;%YGuRABuX^vultfjzi!dxewJ2CR7WCF8K2t7tYr; zgD-Uzx)82oTr`Qm(|@FfpesH&>8W)S36bj&rwa@_0>fZ%M8(T_nq(qTRzZoNfn0{K z!}G7IHgT?~tPezAMMLz9{-~NM(_);Fo($MLP!Vp++9d~5HTZ)X5wQEu*Em$(>e zkVO)UoP^V$h+nxPcX(Z0g;S98{e;EbKxjT)vt=YV zt(omz5cKUEZ5)@R2Kp(jWjS8rFT(xG+xnUOGt)T+_`6zseYz|Q&~`2TyNt{RThH4G zTpAJWtD}hR&{jf{%H~oozwZyLt?bfg)!Xa%|sN7mDXWlvEJsRhNGTV`VlvnW%Q`9;9$4;e8uKf7bqs%u zMosMuRNv)YY#%v@`kO-T^Z9@qqadXpHg89|Yu%iSSj%{dE|@0~m_$AA-n$xAG~Bb? zU0lvG!a7B({AE6TV0|A^G3t0!n7V!m-}P{OK)LF*PD9F$G`X>%0)B=1J#S|yaQWED zpDemI-S=%Tv_k4%4SxI)faEGQ?q8uz03+g$$dPCDYU1NSZ{>8iFk7bV+4S5@meOxs zi0E72QXll{`N;L-=$W*{GYC&t+W?3nd4|-@ld1nng?4AcgK1^#arXawR5NxyeRrfI@1O^oP4%lgaLs zNu#%w%cWY~)OEP?DU$)39e8%sARTQ z3|d4>_rG2Yx60Q{3!0u1T${|EoCg2sEzGxi@30!h`%f|a(ChHL;ShE|hyFTSX;j%n z6yaRN>AHAXf4$m(4#u`U2%wvh=P;UdLr*kdEFWComQVRL-X=h%fKP>F%GH2~0FJDLiAL&d&;fUVLSOw4fJpN3?QRTOq zidW`f!%ryRiDKSY%@?25ZX9NwiVYI zuys(aMlh*!w6sQib%fNWIn)HD*lgT@qRdp2wK7{5GX!L zhy%Gct`cgUNu6JDlMj9T?D8T-jWfRX`7N0QSvu9wXX-6}Jb!*uvqKgG=sL+MXx~aP zIwlYM@)vFlFAerGfzEHQ{)H_$iMV8|xh?gZLKgR09K$}+R6~%IMp4{D6;CPKnERF? z3oQBRgwbev3oyz5oUyuSJ2qxwW{Ez4OC>IWqFHVbos-kkh}2z+LmOwYf>7^s>G@@i zv;ip~273?-#56H_{^V+uv>@iq|eiXBm5W<9a#N^~64_Njw$BAdcGqKSCd^&kcO^?`$5I z)q#_ehxXJ*yKaT_*lFOAf}^O+*sddhxFFj#jILWtdy<%6RWUOcsBQM0#!%LNd?p*O zfQDa=A*m^BTx{ZwA<1&g7yse8mrf4R(#Zw8wc{1L{qd26;*${aK7aFim>u=!sLRs% ztyGlBpUB#XXhhUjY-QkX(pYg*#po-Y=0RtBx#gs0pl0^C{}xNXk@XfG<(IXcDDT>ai~}WvuL(AALIq+uAoqCnPSf{hj+V zvB3|PwMlgZFOm0bwy)BVq@`lIr7)Szov77nf;v+d@Z=kBNheCqKskYEnJC&6SP&dl;?vM(j)Ig$8CG|1zCp5CPuf?j*j5vk8mR z;9Uc8=JdH$^2n`zW9#=y6p~@uq{IO?9xD#*97z*^Gw;me-72pK^u#Y#e?s)v($Nui z7X_rbZg$6IxyFrgKLffTK8?~NzR-|FS<4}KWi-v?*a#2C~VBJ??wm#%~o^z<{ej+3YcH6i+Vw2Y|D z(onGx)paH<@qcXD{UchC3`xU<-hxU^!N4s!=h3mTt-;Fd|LSzX+u@P4%=7@S;M_JY zlX8kPYG@oR+D;zLOx#6^%{{QHGALy+%6^=`wEWOasCTK39x38Wh~Jsa@pR`hmJrKp zl$x?Kk&dP2K=GqMg7YtKZlOR1Z?~P-o-^+qx4+F>2>c zs$Qf*YUR(f%hxgk9o6ZDWE^KC>Pe1xagtY9VCxwU&CWIJsfca~+l_1VGf!VOrPyc&g_D*W z<%Q;(p7|}bhVnKP7}w_ae+N;!gbsG@H7k;OB1S#^}2K~_O%Uw^3nFhr?isQ`Dis$N{ ze_-&dYt`|~Q+QapAmdVhN>kz;j5jm1+u&@=_Jsz~LPyP0_1(I$v$)ctK$qvZm9PJ} ze78s1+uPaL>q#b`Q{L9NB)%}Pf)mPC#saRpr*a!0V&vpgkN$+nxkiQF4>47xGD=W@^G@);G3)OQh-_s3CDWf< z`6~u3RXc_LalKHEWIk&p{-GXZPdohjdn4MfjGy3mwsXjMcQj3uFT@n`VETATm%?cn zLtKUmh~uW^=P&=ds`Dxrz<%d50qda~?UYAxg4So1sS)dg6fi!GYO zG5YC;Az?DA^jL~PJ&1+Ny>isf8YSmILQYQgix%`NtnLDLgSC3$83GZw$Grvmz4|S-%&V|_@EJdkuD%0T3nm6T>U%ZmaB-PCGE#FZa zJk_%~=ja#a4ic7UtWw$j4B~}g10wytwoF&bPTmq?R)fMV_h%WYJ+5SIy>H)AEO+8o zJbenZ^C=1Kadon9P+mPICGvTLr)rlo&T>s}s=i#>TeXy=T?@m1R$}#yIps2=?#Sq* zcy;>*V;sUHJ`VJ3D!7n(O~Fgdx=8j$72e+S2 z`@%Qn>~$wu48eZ~3#ReWdq_BaRF% zWqilUUF}=~14-)OQLYsnr zb{&ZlTBizcQ|}2y``VaJ-dD2&R8z(KDy}Re0{ynCs|Z2cAowdbh@TePx#IdLpG``} zva^9KmkNvCdw-AVSqW9@XjTmZ0M+Se-wJ@}H7 zi;%+#?rD7c8fE<@w9P++a!2r-x?Ee17H#R9pCZHDgXpQ`YVo%E{1o#^FnMl?*2xWc zV+bowf}cXHS=}V0Bf%n~5R|*@VseRR=Y1$~y3~;Tqo`ctk81VtJnM^Eph3UXqa-vT%Ikd^B#>iv9DU@u7rv~sWx5vNWVVGmwHYftIYUY*;txdMSR31|FcWNJQ1oIb z;K}~qv#GGF+s2N7oglQ4yUAv$-qhNv;{|=jySiqMJ=9X}Z#HsNSLhJ5cbiu9^F z2BmqU^TCw+9qc6XpA^Gq*h#?UGz!W-g;-!HGffk1nA^FyjWWox#3~bU;{jdvw)+j= zV`*lX7rXvPbJu6;p=1b+5C{*#JRn>H?rnopnFOH6Rj^|;BDRA$$8}zS->sbA{W8`FW7{Nzl@{ZRDufH9(EH=Cc_kgtb5ozn(q4SkV{C+y7T^8i}jqJaVKItQ+P6&%yP zEe8!{LeLDljPR7ircYHDi9uT{51_1(Uv?cJ~j~>zB;Gek3(Gz|_tnSp7lr zo*}$=IK^zE#Gd^Pt-H3$od76BW&N8HZME_;34G>kL03rS(#n~r9%BJdw6OY* z3cax8DrctLUWe+&TLh*FzmR=3Da)mJnT2isk@uP@Jh2+P-jjb`CJJ#ITc*&iw^q05 zsNd7wfX#De6%TldqFiDDUvFp)o1ONAAi}v_tnG(lbO?0oXq&od4_k+SSh=bww#0Aw zZ8{{B-?JDO%NjJ|08ek<_vURTR2RlXxf&j6_qcq&xq-{wNNGARY!>earP9bgZoDjY8eQuaOm+aJ{T_j)_wU1g>(<}1**r0O)#-UnNyXF%BM_i}E zgmnK`1fBY9d5YZQ`o&VK07KLX3C|HuTfD60SvzOl@=bL=z>5f;83R2Ahf}e=2fwAe z1S3`J<$sv1$dmd&--TlMg-iPU5nQ{|W)4`tPHDXLIaUWhU&F_;T3FxvXFert2V~(~ z5~UP+!T;;WM7s$SYu|>C1 zZ#y_I_6scp7bv0nQn=p~rx95sWb;?Yv+J;ytB zK{*i7_oE%(oH9=3{gBpP_!Z0*P=%&Oir+#EI5)zA-mvlYe9_=nz z$|IdsB=T6U5C_9h=IORNQC7|>wda1xT_kuZ_YKZOt4`m?k3N`HogQ!IId!ep$ze7D z{AkEdY1+H+rPN>A12oDw9qPYtcwTm+;cknFjIY3(GqCdu-`f;{_9F|jq)E+$an`Dk z_}aH2{~r6h+iw>qxBPod7Fk|0SOM`&cvX^?vW^-R1uFTM^1qXJaWV8sb|a)P%btYv zSRbA;smKdcI~q~AL}6snG(g!sG?k36UE-HuB22Ai`SZEky2wq}0pxLQo;FZ9Fe zld#Nt;m;fORxY;BMC&wEddV$-@J< z_a5F-kLV3D_owZF+y<37OqmhLX$6?~_zN#gM`p>AH#_@*f>>^f9sS~_=ji^a z8nVytn^db-t`WB9HqVH_HC(TuIeiW;0T~=n_LVIziw)BO$fdVHBH!CTOVyO$KC9KR< zGq|?hv6oIK)tSgvI>Hh2krD7!x^w_}X^C%bE}_=EnVnH>nfM(ehRiTEQ2dyo)T@p8 ze1u;u?5L+RJH<~Q7FqiZ_IE7iTI+1%9!k}(oR!Pq{V)k#rFzz^D1x_Vnj89I%!$5t zLeHy0pb+ETThmVg6?Ev_Ra~x!y6JR%k0Jw;DGium49UEq7DY$dl$w#SG8+=?mWPzrkm;IVsT)Fi1Hb|Y@+OB?|+rvPyjMmvuP<$2tb z88$Eat|XdZu1I|>Ed1%+@u)Hik%PhCjrxjRX)SA8W(Gk9bJSVHR-<7w@0LL~bHCL^PcbHjA&@Zle=pDQ6luoj*D&J*<&ZNUb}On%xZL&X_NIh6p_zZ>4W; zdEEe;@O?$lKe#@JQj`?d92q-prU0>*Bb5!O1^5c@&O8<`WUd`@mXLf5lY>)3Cg{$l zm#A>C-7kyx%=sMx;Ka{ZJp(;DlxKJ_$c-c6fP(j~`@lMzaRS}11iqH8T?iy;&sNAZ zvHy!WTkvm|AFu3A3{nKxRo;rUHu^`1U*@Lnp%9c;1IAeq3L8^qV~kF)FF8i3Oj=sK z{TOAgq*?j>UF2fpJNdXOOqB-OB2L)_Rx9A*N34blBI41*;#eTo-}bk* zv@$28dJD_apX%`iw5Idy>bQA?!`#a|vBfSYAZsmQ`Zvv7o!E zj^_{K1^*q@`fI?$tDcI(#aBsr4up4h8t5&hA6%50W7Lr`0B7T!bAm-0mVhYz?lj&1QfblR|Roun-+x#@|zhQ0$Ae9sbC@Qp5kzbd^y}zi)pu(nv`oAYFoVDHNOyO4m*nW~?nYWh4;b6C-*e9Y&0g=^=X+oGbzPqd!x-@xkYS`GVhg$^YAg$J zN;$9S9%5eA2_oZKmCHz$z+$BRumen6ypN$D$PRnUQOXdDeZ?e=p)WGMf8KBzYzxHY zIBFK&y4(6MS&{P_9qpkO);2Q4))+A;ym> zB44jYh`7;MlH9^i3XYn=m%3)Xex| zGlM3(1uf}&S7}dUM?xQCBw^{UA?Nvl+W2WzJjjuiTfYB}dNNS0eo4duPk$T=yF28Q z5O8#Mv;Ad%MSRPE{z{3sjZF`Stv*)hJ>*N0uOWf^aE^Febfik=*zxb}3cYw{+!I&0 zeTau(KkSU_&Uq_@qFExaCrg}9TAW18WH+W}jmc^JCGA8?;$~&>z$5Npn5csz1Jx@% z7A>fj&uk&rSfoI?ezBo-1F@7m9g;O6iWr0z!jCr(bE$nstCFCn5J4W~dtQXG9q~-< z$>1_4X_Nw%cAlCt+h2bBAFl2yAaDeRe!by27mWRdNNVn{(hi6G;r`9uKc#QHn}jB` zw%a8VE?vn{|I9OWp7JzbwYpG##yhI@QZBLp2!!U{X4Kk^yu8L9^98_`Pi(>Ci+ z4R2=Y?!tvMK9VtNX=II}`lFVB#I(_2L!im6v@@&~N=J z@@U#+bzTRrUr4$HzCei@@!8zgcT#URZzRJNrzEHMk7xM7yuH0-LH)I(QS#T!EeV8~!r1+T`52Nl4Sv9@t}o%?o1l>QiZzGVoqaB%J-jWEs;XRXFIg=s#|ISqXqEwfT_x_P|iH>5zcBnVlQ@cAK;Qk>m|S-{ot*pIA0l)REJL zezOAui(m}l6KWBjiph_a;E;+vl z#-vS$NuT>;h^6pBtau-9mJgpfIX?wmO{IQ%u4oh&h0eH&x}K1R5S$GS&lDgzI)E{q zyI}k>U>KF|OtQ$|*~XwvU)v`V!8DY=wZ8bL{AeTper@_B9x^~`PRy+r{ROpm#oq}= z)3?F|bw9~Ns@b-lr1-=Mu-#(En*zhLy8^lvQ@WrQ2cV2*uBm zitpx(E6!fqcW&yM+>OfSmR-JGy}UtI>}7H)Ng;HnmKS##`RT-JK2880n_mT^qO?1< z&32dUj~K+O9ks;OCpp6usxXSfvN>W462v~tAPM0#Card=k_;-rTfk^1l*dQghlpnU z@}NeN?%aOdJE=rRPlpGf=y!cBn&iU?;eWNG!R>sQ(`g2(QcF_)!~&&88^Fn zUB3(QKI=U@+wZ?=WuTFl9shQfBmLN|v9(k8vW%1LF=pP_^Rzx5mt;TAAx#(6&Ex0~ z<%zbZVB=GU-;rS+o$AJ}v`5<{on5#pGro7LXjmR8OE+wU&dPD8p4#-848Nz`L`g+C zuk=-_nZ{GoGW)9!vj_G}5L9;#8{w_Tw!ZT`_+xROzS(=QC1Is1eFDqYt;KQUN4&}+ zdboBX2$g?zWRd0hl~t_Km!_?*C|M7!eHE>c%J}SnS1~qxqI%3^1f=86kM`GXp(wqR z^{=({H%L;-WxwzJ*C&z~a~HQp8d-a<+?Qj&CW_kOc~QOVi)K->!~`kz}pVKxD0{QJ7iF=xvl;YTj_0 zijgKnd-N&TDJEM7>nip(oY&d_lkVQGw_{*Oa68Z|+0zHZ_t$Bv&kTY&AF=uw1s>Gu z5a&O8*21>rDU94^+IW4Cjj!QZkAFlL+P6M@VXo&Ulc%Ps``geGsV&BKzz_>S&f#flbdrIN zF%oh=v$sCYktA474c^&t1eNNh7=*{o$j9c^vDXM(k$$gwPG$W2JG!a>ddl{#fdKSc z)?o#<0Bs2ay`&ugD`lrY-utj2hKsE#Cc!IL*3$B`TCsF6DQwlTl8P%G2M+`II1=H@ zGshyF4*P&r=QHq0Uz9J`>5Z`z-9uG>l%9OG3i14r&Y1ku=yB-JGhd6V5)8`GJ1+~T zk^G>P1{J!^luor_tKZkJyPMnw%``oXQsmF1-HOu4`pet(W8xMNXxwl< zQPxPXO$l(VT5#GE$GU$p*37DWSp*3sN>CWHEM|BoDyzgP@Dk6vbW|g_Xv4{T|A;92 zsRYzda=#U7jzy5WVfT8}6rH)Na+z?poO~$;lCi3!${`(DMG(0VmCi1D+#M|#!f;rYb@tFU@o>FkHe7RY=HILsVN z^@;e87+sQeEsmG!b?M&Nu zQTlH^WNkf+Ki-}?qrStz?8+XPLt#)vc`J}D`SzDH%{s2aS}Ad_&0~C!3s(~I{V@0* zn+vejW%gC&@RD@Ts%m_5#C4U*+;zpHcR&#yD7H~`Nm_I{)~E8Ogdfe z&VJe(dk$8`70BKH{xDngzqP32uopK~`cXy7DQSn(PL;2ufyGS?^U@EG@`K>&Ti4rP z!B3*~Z^Vf5eIsxC*wJUS{}%7+HWuXj)y=fJpJO^JuAkl~Uj9T9E>ZUlQHU1}JN}M3 z`K;jQp#74{$y|EXK)z#&%w9HCk|K3EP@TCrZF;&4 z(VL5AH#{bBwD8|~3YVcSpY|8Y8GTN~7T>3ooLbB^n);8csn5aBc}>@t2j%@SS=Jyvf$`T4&Eh5QOA-l=YSrZ8<(H z9qT;m^t8oZ@iJzVgW9-3PF>;5haereyc9y~v99Cvs%Fu-W$m5!*(=(Wc4|i_AYy<5 zsP0_DFQ2Y(6Y8iv?Fb$9|?1IySFc530^^ zTo77u8FV>lwJ-Qx1z^$rpp3ZDou~ERdaObciByF*V8E`>!zHucb2Ns)cbUcKRVmNS zdB^OqfoMmYraq%A07IVgp1;hMq|%e&$7jCZvFoA(V)X)J*l4ImEkb9Gj3;KHvcKLy z77cMjJ-YrYD8E0{bnK(S9P~G$zp;^9D~QO_F+P{ag@-du=H%z}IiJXBhr9wsexPK; zNa>H6+u4$n+}l!vXc)5KbVZg4Xu`FiB+{0U0rNt`xVU6QUR6PX z;nXMJSfi%Ok6d%TTXuxQ+S%O&^Y5Y6cb{)WLj*K{+q+!Qf*q-(Bte1WX`td;8~OpZ zm1N~&U)&&U)r^n%T-+H%Uz}I`UeP86B*@NoO*D69W;>hrjY=8Lfk)48D8Tl^WNDo& z-G6+IUp+sgDPOc6UW2N<+~3kfh+%F|NZ%xbR3Y?F-z8P%TzP`t^$+=tPI9hL{~jfj zkod9BFwvX_5Rz=X@?NQ6TH<7$p|)K6An%oPihLR4^%xi^o7U)ZjTBE90@hm}oFaRO zF$u`?4KbG^gOv;9x7=m#EV4>09!`R2{dB$a|eJ6Uv=KNZCQn zjx{a4Kvb59+8UiV(bV&t^z6|zQMZbdI$8bJ1p6AtZ=t5~a0iny>+Ftrn^T7;hgB2m zX`MuH?h(`MJzn6<>n*Pac;yji(wuChUt^AY1iq^dZ>X&bDM`0L?g`pC`0`DSuc zLpM&>$1$*^a7{qTS$y0V}YVyBD(QgMBQIuvxLx#UA_V^~N;VgFmUYdKmycEyzrTR|>(iarOQg0&4 zOv@6J+oQFQ5=`~Ii%UuOlFo&pQ-)i zEH{ZvHsbbMF-=+yYKq8I%q+X#ctzvq3&2*qOSkP$wzlz!D90Xk%31>S*q2UfkUdVK zvPitO^O!^m&reF25qf1avfKHJyl#a56fb5ij|@6-e!X|VJ#&0aG8k4TM!y|AO%a1j zmF4(!t3p{TXLo#8Y%zeJl}oXl_1k%DPY<-O@AB%s;$DCEd{-vz>Sx&_)_LJH5ZV4aF`F_8j*!z=~!$Vty^4c--eF#B5e9B1eREY8L zbVvx5@&Ol{l)Tz*$K6lJn{P=%9^N>Pw1rv}``U#<>_O7P8?k*;r35Za6Ci$WCc-P7 zoh=sV7ZXd!%5iLi2C_^E>Kp8iB_lb-oF|@32UE82FNpACOtPHP7Mdlmyqwz_Mna!A zr@jY}WccR{;RP3;$X)#StYRG&2V$@+lf=4(D$m z#|>@8B>s)){oT?vj>bG6KM#U6Ixp-gX&4bpNT@)VRuR47{G8_P>C0&a|8UJJTHAU| zSHb}vLV;QiLQSsv4obHI8gTbYS)e#&MnoBpprxgnjwBLWzCtc4_Kr3s zf1G68o1gY=4M0aqJ5O@Gw7%QS`R?Y}aq37kxeSi1P&!C@t(PEu?Tbsg(FDA`y;Iho z^V%s67P}){aJF0gPFvui$b8m+oymPK?&l~>P~MT^`fuxd5$?sF3af4jcIUak+ zRC2$ekF#Yb@BS<&n`kvsBI&mOnO(TCEoS+~O{46B*SifTI^V@W7L!!c)IU+|>wv$} zmi{Y!Ws^4mX0gHK3Hnr9=#d%VbAD>8*r%0j`zr;FJ3L@SFv0PixxQ#E=b7s--cV@@ zjLDD3J`ny-H%KvhQd3JUYeZcbc1oaKEaUKz|J*S%^2(eqNA`90Tbp1 zrPVk*xN=NA>V(x>zb0JVv4?WxpXtT19+=d9l0n`kaC5XtGMKTYYZk(Kz4*_6j zQ}8oFBx|c(`h)z;9vZ@-24B{Bir~}OWa@K>0KkeSO)41Cv4=6tyN+cTcow~?A-Zz< zwNcguz~T+s*Hm>Mw_#b&;urz+YAAdCGfUDX?7ZSHR#0Sms>QTm$&K;1G*0^G>%sg1 zwS;jhinLt8MytbKm}$GdXa1a3H*Y@A@nK_jbxg~AcE{ly4lW@6Q&do}#^fb{&84rJh=!Ue>4 zR@I#n@a16Z;lZo&zokkEU>kn>6`(hErFP}6|-l2lS|Y3|zSBRx9yT+q8aT>3wZ zK~NQ}%q!Vn@3D%7^rW35?lR;Kleq>$UU@|6{?k2lbhP2+H`SZG3^4l(l z2OT~V^c>;(cm7zbM?Wj1zxeGVqOxKOpKf|f(!3p3u5?ZDV=*@2K zKw7GT$y@3yZTL8OZh2E8(@&E*Q|{>EUcHDX29)9LndWy%b#gDq@;mbu-Ty{NZ#BD@tQUR$(b zXauj$k?B5zzG5N74_rIn@2Y%m^Q99T&h~RI_WO+#PO0rK-y~)q7(R}6>KOVxoO>d7 zP^(ml*0;J}fB;Y5?dm4_r{y6y(XaeMZ68G$Ol=Xn0pkcZSPycC%-e1(XK?l!!0@M~ zBYGRZk`n{9=}?mh+Axke$joHpm;dKOHPYUr<0*IhDcEiB{)Op)6@FJP>=ah9lGxmK zS#$Jyw5p9{L8X(hR*{VDBj+cPo>vP$1@pqgXWV71i;AR}B-m(?Zy# z_`plqYMuHlb=%z70SZ5@2&&TbxQqme*u&V@|GLgiSj31f@MD+qK1*liYpwbK zkkZ?}(WE~f3skA(RQ>#^pB>1!@ToI|__D(Dt(~l_)}(#T?G1p3*%{^IQ;_G~ z%?(#w4{d8V4rQ#_JsO)=PCTmjSDRSuJralhegEnP46B ze7l2}^jc%IeLz-T#5po@ENlf>_PkCS(d?G+A_JLD_VnMffGT2N_Z<)*4eRr)ghPj< z;jNo*l(pO3Z-@s7We=hD*>sGU(V6M}Cg55?xi4SsV13x)`-Q|K~3KYfI?#k7V zM?rr7jn-Cm7P*GBL@Fr5IQgn5M1hB2{=Go+ivF>h5(K<|A1ovPKQ2J(s8G&Mu}9UP z2}cLRnV5m~^-|3Z0SJz`1f*j5UU+0!^)y1H)|W9r?qb|14^wh0mOj%-*1dYxL;I#|@I& z_^YoTe&*33aFdH0y%BhDJ~6=uuj3>q-iY^5?8meh40Apk#32m(Jp-k~KdO%+e`3bA zgB)beMn2lVopIy=BIFt%?Jw1vIP@PQNh4pDwE6%Hcv(d6Yppo3DNc4IkY2&++g z2BlL9fBXB4x^W@jW%*2$Gp_F{kB0S$9)+)}X$Z<6n=rz0k|W%oxfCvV51 zMtu9t<*UaxlV7m-Yy*1EXWyZ-e1wfRP9?%6A86~gLkMcNpZCVLMwYM&niYxw5 zk@`COs1s-4tG&Ni+^%L!=a6K%PoS9Cl@#GZaFDoLf(a$@nZ87J7B@BQ0$vjjM)PS5 zs$snGtZ+dWJu6wgrq)P389^yko>S{H5F+rauh(MIJE{o@i{W3T4C*EbYD-qukbDCvz6EiCLHNOm&M8kH) zR9b3t8uUk=HJepWoK=0WbdDEAs)b4cKq$)lL5=NN4szb*Tk@+J(CmyeBUNXkd~P4C z`uxLopIL2E?Rr2tIi8KDFXubvT1lg|)A3u==ju>xdYP38KFOPLF`I(<88KHzX(|i(>S%we z$`4D+{v1BcibSpM@XQ{QpcpdIksHxO9lwjNV=<@6?Fa>}Fi^#!mq5IRktUqdhafa? zs(3)-abtI^$oaOxA!L`{URd025~{OnEnn^Xrp?sN|FY~r-8K#VHbFB(_5mpM z@sEW1sn2J4;W0K$)Q2BLc1-PKQBAyZx7J*PM~X2Kt)AK~)1pGJ8LDE8^IdI_(C-&5 zhGD`A?c(uAgW?UIh|sf5Zq2PdHbv;%_4SXP{TB!`VS!%HfMX)vNMHLoW6siCL3^Hu zfE?&Z&L5nJZCz4Yf6#3WV&MRpf_0~$%F_Ok^f~-uS+IvN_DMf)Qj8fw1mve*$PZC5dc3~n;$@7o)TW?j%_H22@r&8<+KhejG^@J!oC|Kzci4!>EW^g*Fj z7n8yVD=YU*eR}L6{iSM>*MR1OljV3&jLPEn-QOfH0?p@bma3NzNAMZ|BwBuomt!T} zqrL#eHiD+`L&V;ggfCmrh4< z6>kYye`Ao%);V>*R03CaO!IWh1IQ05GBrMBZmS-X$_TmZR69 z+w5!<=xim}cvAPBwr_F^W4{1ptdVOE3pRdf`Rx|k5XbLjUvSu))dH7A5?N&(-skY& zEiee#>m0cjZrKuL6#24K^y|hUezf%+yZ!?AF>q?{yVx-<#vFeQiz&DvLAY?JkAmkI zn*YG|7@C*CszJ*x2jEx+p#%h7IU7PPXqjUaJPI~Sl3w_{pD2a9s1_FoR50NNy)MD) zg6h2(HR+>x(ur%gZNc;N-t@uuyperyX#yzM9= zJcNoI()v?7cYI&@%P9=zPD;Feox7pZiBp|n-#(=HR!E$CKRj9@vn$?;skO8JbIe>5 zK@<1+;lC=86Pq#`zXk2w(&f=jLv!V~xxLznE+d~^GzsMzlC)~-#=`4X`GHQ`<1x5( zu=55x#SGO%m=Bk!OL-S@8he-B!3jUo}Z&=HW(@5Hur&jE+5A#Up_0wc{oU z_#dj#j17~Ou8QNzn1HrIJi$*JHGQH|lMkfbKR2(lPam-`t0BbCA-kcuYG$kp`=&W!A^se6$BQ~m8Qgji&zZdJ zaJ02@v#Bt~)sbzqw4Zv@PRur<=5ht+%tjP*F0E`{WlWAr`oIKbec-Cwya#?4P1Ote z>S}&I7cy`ZrM8Xoogj+c<@d>4i`v zD}dC5S)J|EOR>z=_(qlweffJ0wgXEJ`gpV;f*f`bh&S|MY<7afDnYyB%D=FlCw%O7$4txD|x^5$TxAN(n6w8-St1cQD^4FCBQ;<|rb zeBgXy6n{9ZU5zv|YJxd-rijs;SeGpKyQXo2GW-k=$$Tya*3iM71V5X`f&3UU`KpPI zc}n&kJ}(o36tS%w8RB5+xi+El`(5kSMep{u=r2}!5fOkRGUr1W3vx(o4arK8Hd$vvC>gvs`AC+R+V zY6|l2Qe)-=>8Z?)BDAQ=&OJR*r_)wOlf8Byao1T@ zh%ArHD|9vo+k&s6^(qRc3Y)gE8OIaan5|lvVYV)_idnz#;2b_gP!;$dObes+`|&tB zn7TDU7)KX~O+=32gP< ziry_^DfW_gzMLsgS7`k~P2`;*!tQ}k;Z{i2E}9pozT&QC99hjBR}JUNG4UMdSzI32 z^ed5XC|=`Ci!DiN9y;-(*!iokbJZjd8j^~)9~{t#FvuTWDI?WFUZ?Jn8;f|MI-7W= znGva&w%Q_s?qfO!Sw2p8g8U~kgu&-XN2U`bHRVELa^}ASoX~OdYd_DcJ5ojQV=bzm zE4+n_cI|T(FuhxIYVD4Fe*<`}0L24a0g=qCP1BnLF-;jCJZx<5QrT_O*^DF5P6RqF&~f+Zs~p^3 zS0PTw+mb69sS2Ib9G=U!cCfMk`f=JM=@NL7OLcb+?pL&uf=P!5#m80#VLfTlcpZ$L zxDfSib)SXTQX|cx2p3EczEl&;@=^amXP-COI;RH5XFUqE^6tCTwm#-m8i$h0ym9kX zo{xu5Jd3KSDV3a!;%(q-p>wf_ZW&Dl==KeoT#snqaqaY$kk9(*+Fcgchu53Zcj>ra z-;rVqBy*c|Ju=Zh46BNS4vC%vd|=kneySjoEf!GluXzvV0l}FE_a(|7eo);~k`a;& zes=BI@zH@izsgcvF92^x3v5B{0u6mW(pRgpXyxPE98zoiFKb^MEkR#W$M;Q;1-xvf zJUFg30I6Hm<5;kBa%xlSD!95RQ#m*RTX8-yIWu5A7$$J@N!CGlhI7s$1xl* zl+ly4lXcT~6Kp@7{7I zVU5ujG8K#;fl?_aq>*L!n$nl@83FkYq#IlTS-XPgbPpb}h6Nn^GPS;sXUigJkxA6T zsKMW(u{G~de6jCP0InW66bd33j%%MXsyF@(s2cEn^Z83kkdwX}HKvnjfSL1WNEZBS9E>9Jp)IXhSVnv{2W*zZ8e7c>|cvf_F^ z9(KK5T3_{(`@N2He>W%27tF*QSWLd&cc^|tnwy)Yg~OMhRK&dxS#e#wq_j@-pxSK_ z<40unQ0z@T=yVXS&&l#3-*|qmJiP*ZmT|=bk%{Xa0LI- z-g5WEeGsIFa7j;A81vjU(POFndJf3p0xce>KHHztGF4soxW--9R4vs_1vT=^X09+y zEF(j{hPSVQ9mUU4OaHJ?+!Ykn@?OrIEu*XA(}hshVK~|mH#Ygv`&;Bt%q6dR7-N!u z@&z(G;iQc}7)Br+xA+3sVkve*m4WE+hsIBx_|(GdF8?u}21Las^{`COSCu0;YdWqb zxT-a3i|0NDQfrF04=V7jO-}EJy)604Jt1S9<;SKod-5?m{V(*rk4-9SK=FJT_cW&7+*F3*Z5D~2E^J_JA zqI`VcR?b_ty7ZLJDrpWKaJKK(w(9QkBY4{etIlJVCP`@AcMC*_eH_c8gq-OqlVLBH zzo1dq>D7N?q08xIQ&H{=l{2;(6A(4CU>%lgR3ObVv|bI_81nZ`w0GgLVLinyQ1_t7 zbs+tQkMW~z$qda4N2G@prr9*9_bAalIeUPc2>F@;i^&oVF#O&JgIeiwSgga; zM=kY)`gKlCUV4$cC^-rbmK46ZZNIG1htoVVUh;m}I$10p=uOs!4@$XW(uca&WoU^Q zIWDMX^F&_`+|7{YXv0~aonX{^z%l7T=ZvHJs-}G`*NhPr;$<=G4wA|hYaJ;w14(hI z$yXLPj_bp0DjENpsf3Ujx?k{FxOcbCRb_}cn|Ij(LaDwFof6*hhii^E0Dq2-^<2g>Hr;UjvqZ3lM9l$Ctp~}}$=rI2CbRJ1-+?U}k0DfnB|pxzV?5db z4BlQAuM{})`9?H8SQ_!pY5}kd-bDrUlXpiiz9rLH`NUsX%hiM!ZShVUd=G-M_~gv?QX=}E*qltw;!3w0tY#hPD@);7sq=9GxV3? z0PBJN!`NZIvQH!qLg82(mRVer3_|k};?|S4gmdK7&`9M?@go@cE!E_{;xMe+Ey=J4 z?JLDa+@*Ch5FsaX)2rGQR>0k}j1w73gqCsfDDclH?#?Fq3NIq{ zvYW=QwfXl=m1_MapRF3CMt&XD}qpF*ambYYIbSIR+(clL`%Z z?zJf``d4Arf9W-PXB8VZ(KkH*GuxMe{x#(cL`w!q0YU!eWH5q22o%+qVKWqmlV@*5P|>mOlJ_^@DKop+~Q|TeNyZO;mCPL~!GUz;Hvn9|4$h z#Qj$d4@eZ@(LrK`hA5w^ypbpC!U>sPc*>u5X-O6)t@cfD%(vD$ASie7)LQGRFGV!$ znMZz**|MbW{!7l^EP?jfs>IPidx%T;#moI)NdUC#bMgxs`T;^7qkp=#^q+0+-ChrU zOISM$4)VYIub^bQ112fazAYr_Z0g5CjT>-hArAggm@m+tRGe{tp^@yp6O>Nz0p$Po z{>JS6O-P}yN%X)ce4)1&u)+B(&oD_m?)?=Aj)4?eHR(3U z29ndWd&NV4Ntpd6eKsUAuzawFBpr%LRZv{G9nbDsLB()h&X4BIO1STvyipzT$tCfj zMOrX{sFOfsAK3Pb!?<3L_>1GL_0N78fuy_kU!ebVHQOKjd)6nScA)4p8es1A z>c{?UlF)hkVCLFAW~AT4WZ2qcpfM$_M`hzgsU;syO0h+*ygo~o`{~k_&x&Qr?KTa~ zVI^Aaas8MR`;@NX8;~6@7A|{m<}0NC*@;+yah4%X6H0()^{XnR1CCN_X`y_Z?!ugk z#-dD<>Vo#2V(^Fx{L3v>MkTBi+0n>Om|#{|RV+%oH=j9D`kWk4Wd!`GhR-ic7`d}! zrNt*;vXGhUxyw&$rCB?`T_1!SDugZy(Gm^}bfocGkLs0vt<=3xp=*9ZPd$x?OhYPc z!|4UDXqlLHdwD4X)F z#;bi+FomOSL+qqxG2l#0o4Qb|_+bAeRJgI5!IKuHDyam(hz+CuT&V@kGRE(!1d+8CnwmeOgZr1h;jnxJJ_@f6z3$mE4Sy{zFY`zm*(n@q zGQjFq$-4^^%-$t(sUt9~0jrk$c?X-P?OH~Jf!GI3LTeVm`VT4iX?=wl=-O(;7FTDZ zIkZ%?;E&SX&e=)57Qa@iuf8f!@2~1G=>Bvr0lX6}*q<_Ezxnd-?!~g{-3y`%6r4>$ z;~0FaWG}&U@&2E~q)CLkR)2j-m{M(q$+%K!Yr{blyf`kV!I^z8t$u#8c96D%!S5`}Wh6-)+3Q?y7x!M{FiA9Q7Z~U~y3p z4Tc~@7(9%%SW4O) zmmsOLzaj`eyCpt8L@oIE`B&vI8jeu>A!Mw(RQDfhj7Sm?lU`?Ay%?L+Co=d!Cxdb1 zvE`T)j}sa!5uu(DNDSq;w=L}%(lid@HGm5ObyY71ZcAptX;*sFq>-A^twA zi6u@*C#+Gmr#5g)xTkr-&8;;RC+fiA0D!u>u`Zjw&iqbXNC~JIolB$f$c`TJBPI1d z9HyHQ;N*~W$anEt7{heUbFNyw`S17r`N9Z?#yJ#+EzY5Z$Uxcwyeq3`HyJ?=DtiOeNc2=4y(TwVu_YF{t^h(5>`KxxZJd#<`hu9&QzT z*lUT^u4ozM-)J&DQvsTQBKVFNspMuxnzmCsXUr$~r`9Rwq28`KdLwLE)psAVw9gjz zTiHRg7OKxA8P0$VY~%{PZ-3`U4!{d|h{1f(xLV~w{WLg4?tr)&^jRsP(!g zbGGQ#Fuz>=I9B{vhVsW2rJV%?nMeMc>ueK#Qi>Fsmlpk9QR~dVYhJ{coNoAP$W=8N zC|r*Xe+fb8lZKk4zBH+@cU8Jy9@Y}2B?hY81-O|c*Jm@#+P2RII0T5b-dT#MnrP-w zRdL-mkeYQYur2K2&T8JVm-ae;5|7~R<{i~6NV3#_dw#4XQo&TVlzN9ATUnqEu2iV>sqft0!bxAl{Uq)vkq<_QLPKFXZV&5i=8*z`;>uWJ8x6!&@VtUbE zT2Z;iHTV`}z1*tK}$wZ8nis=tkyUaw;LJ4VuzY z!&Wg&7#|1>>}K|}i;iLRDv|Y|>WYRbvrCmjoC|zkAzjEgU`P<+fq})V`RYu}(*S<< z&fTZiuNk&S+}I%;#G8BwQ7V>IR$fhUNl8uw3L-ZV_OOb-qV-#D^kLmrwlbI~$)C@} zytcIKhv_p0XWy25ImykO^mO#%!JZx&Xpj(>8Uz$L_hEiRxvYMB9041kCqZR@LjULs z2xS@aYTg*sjK83mQW-XtreOZK%j9`3L?Jm4zts8fb_r!UwBbjlJLdM|k}=q6;3~=f zc{o$GMy0Z#(Vj(>kV{ul4|8t@eyQ=txafFM$u~7y$E&KN2a6{D)GJxC8&lSww2Hk+ zQb&O=u!aCtr7lo(rtUEL7o^ig>RHymxg?v`F(qv@+Cw*vRo1`euRHW5NEm7X*M$o2 zfipn~419WUBXpG2Msj`lp#!Z{=$$z%^2;WE7e-B=&S+%@L-#A`hCt~AFb%vq5O5&H z&Ae*O8FPnX`jicE5w4#^N5Pa0EMCZ>&dGg2^6F?yka_j&r9JHJYippN%Ew(aJ_+#; z1q}igh6fIMP zt`MdKM70CEshGZXc7vsA_Tj-6vMq5k{`QcOfhK^<4U)I*IpB4O_x|ufd`4CulR_UZ zD74Vrl@He#v>*Wf+pJFr7Hv@noF2QXKIjy>o4Rzcfam zj9nMBYFZ3V`FGvpY5WXUKC>P)T`{(#g%-2ET}fAL)&4wtVip@*v>C+iKguA(upGXe zSt08mUCpRfVv#!@8YQuCWb-b8C$m}B_m1ms0I{}NCiX6)*yZ)$f2662*mIrdRLu%%y9$IXPZ04?zyM{iB!Z#;PC&-<;JUpIZ z3_0wWP+G8GWo}PNn7jntJpdtCmXssn0~f1g4P<-*TpK)A1-%SXsZG}yVRr_jz6K-J zmHVWDiPS`l%Y^9TQK}GT%sQ97!Ivid++(}X#Hus#&N(ASyccWYLLtIN^^2;niC}I` z&%AHj8Dy{FrYx16H?s|!C?cnL-GgI7o!XdQ!|w4iH;xC}>4f7VBBDF&3~#>iVxyv? zt`>`7Pn*V*`Z^LZ{LDgn%Ak_kCcR7RVGRU`rlSIWH;}$&%KKc^K^@LVQL817qKOe~ zi}QKGHV_HgDj7tct@@>CYZ000QTE!?6eXnzvGd<4Dd)KRaNRV(t8nVyJy^?U_oWnS z#evwJ!+8fa_wSsBp~CD)%ua|4q<|4@qy;bDv>Y>uv+o++Ipr9R|InIQ{t=eZld?UP zsGf-==mXn%WR&{s{0*Wg-0y@k;+!#hBUZyS*2|jAl11&x)SAzfFrmx6NcK>%L z7Ay5!qr&D0^BKmf$eXIXpD7Y$-kkf{d>!XEe~n?`mxPy+7mXd=`=S_t-qeROLtedy ze%wg7ol1QK1X$4n;>GxP=d($AZP6~zZ48v#R?6oNK7MxBHuk8QnIV-m6^L(VN7XzU z#FI)%u2ugGu@}RKXfPiRcNOlng}W@mlzYKAQsI|5J?yA5d4GtL5Vp&q0d^2iN%_xW zoXAew1XnpN}z=Q3))cUoRcBXK)EpHf0l$+pKO?x3}?9F`#$=PKBR$mtYD&gKgL(7|yR3r6zVaisF$c5(AFC;x7jvtv9D zO|qZet*v2tYYl6Ao0kg>jz6DjA8X}Csv^XGgj!&;rQOay2joF*866=gvlgB zSWb0TK&5^wGhj2s>u1h1Z}llq7(+!xAjl?6=CjAQ!1gz`Mn|77j_$lCXv zd#;)5nwdRe!tSx(E_kEV`6hO+W(L%P<84xhZ8Px^ZE5m6{g;mO@P4wIbZ*=W4EcgR z;>)wYd%jD2T2Gb0)jgy=q1{x!hlo2v6z|lqgQ&ta!9?bXoV4K8AyNFV5Oyyal_j-A zGM@Q{DcB}3+2vMCE`x(VM+=}a)XEDQefR76549X2!vx6?BgN-WuI=0MHd2FDyzn$_ zR$qZejAbP3kTV`gJtYyakWn=wdVI#U0pe_t0C|=lTmqdQnop#F&m!3xVEq5$8V20J z2QN@Af1wvLH3)&5ikCi7@G7`6Y#2j{E@V%Dlzj4)(4M(0da<7wb=iDwhRnP?*gKgr zL$)2+^kEcb$0Hp$D$zy9;p!d<5-qN(Z)~t^-@Il;WLC0#Loc!H|4!*m1Ex=IFp_rw zsLjcA#cH)?u2v{##1ovh`U}Fhd-l7C*v}iBX?IHZYV0STVw&#BO_lgWR~N1yaHa2m zw$QcH8(md>AoFjBDOahLfmreSPQ`xaR=nqEV*qipsBse|o8aru8uBTkJb}sfJU!G( z;$s63=*z3x!3njDl*AYOX)tk~J|_Z4wWuG&LnN|Py1iy5qpgE+t19-lB+&gSDYAeO zjqlzfgx}8-9Vm5TCA`KfAMS$+Ib^-+&KI*nrI2SKm{b~)vQOl5A}HkI`Rgh;U3|H{ zp9=E%B!PsNYZJ?XFTZxgsi)G!A^hu62J$;59j)0=)DWx!`+y}y{%zXd8!%~yIN#R$ z+Pz1sWhL~!x5pA5%+EmmyJFp6g7~ymXEj|S*iiv^J>834^nzvtp(=&#~SbYH%XP4oz8Wh+Rumnelh?4>+17@8WA zNp@5FFqq>fIzQ2Vmwh)bKJV_RS~`&OGyc(oDTm}Z2Qh!C0$xdmuakeGIfSf(=eNlp zZ|%OfZ9N=W3NE(Dti$4o!M5xT8+iOcBB*JFAp%CKhqAOg?^_FEYAhdDheF9t;+x(wM+=R z^Y>-L>DAdEL%s1R8YiDCuk|Q8jYNFp#Rm%0{>UV2?LN}3f~K+3hOv=6H`_l^Znxln zS5!f4{c$M+(`}QiW!=sOMi62XK1KR{`779;pRFEr4MC-* z)km#qMU=m*y^1mq2p)6+jlc$PR&Q4DAt4h#18-9v>$!JPza0CvcfD9doR!L@b?X%? zOYY5)*<(MH@oCGhrEzl9P^9ZlH-#^0O*<;2noLKkHo{ zQrRQp((~XSwJy*;FD78j@**|THy@>o5au$8rcbYs%bhdLXNwHsPW+Xq9b!)k{MF0T zvupQDw<^x3+?0GQ!;3Kt%1oB|*GmVR0g(`KE3DAI7x7EA+(Kf@vJ~=MM1*1AmQOMI zcn|KSPkw~!vvNjDY&I*e8C{jNj^<&6P$HYQdy5?2w;A8zrD$S=0Pffazv5p`34V#y z`ctSKVrTw5v*tA}17la3!ZCD0RYK3H1$}_jrzF(T2YttC1(iIrTx-^> zOp83Cocn6!i@xtgQdrNsq`T#HH-;Z@v-rA8im3E%3pdjaVksZ+(3-G#N75@2<$SwN z7h<}O>sF9Kt|daeLhhLhumN(U1Gg^`jB})dIjXNDClqhir+QN!7s)C!=KmcTfft$S z;hw#fr!3!7gQM-;ZPm!k1W)rYh&%N_{b-b^nlLlwQK~#jkb6c5j@P6Qv>Ef%vhRl# zSG_Y`=_RzSJ_5Zqn!#x9`Ytu4#lFt(K7FqD2!D4$V`cq%KPU^+mWw0nMjo$nLMB|^ zCrbgFp>o}%nIptPMSheqKN}l^{BEa9#I@B#Hs3DNb>Uu9%He!+cxZmAef1?qp}Q-L zR9NrbXT_WSGZpPYqqP^%$Zri6n9$Jj1u)b_NaU9l3f~(?JGi+uZ%ga`GQC9aW%PXP0Y)G?k$|$XpCo z#lCcHpccUX?W7KVYFHywKdoXpN3}KPZ+4O4ZCImip!!Doo=&F4g@WkvzHsQQPT>r= z_@gHSdws*mFKJbspVdyRz-O5>J;wm6lj#>~^BS4y8RR2UK;J};jlKFjNyXm|ks9`7 zflwb{pPf5VdC}sW_tse8$QRX?K2uPAzh500dZW$dRePO2bl@je& z2`RaIlSPgm`OmM~W*|*5f+7x#W5_w5nL1GXVHp2rrvLZKQZ(QGd;@GB76Qpar?1q( zdR;HpMbWQs|6M6*9`=IOv4x+6&}Nrrjo%ADxuC!Koa}>_oaf3OVEF0%%YBmHT*t<; zbNL9PMiD=gg6VS?%d$^Yh86dgO_WKl2d-h=;S(?tcRa17Nwk+(CWV*|lHC`opmCQw z2m2yd=RqNVua+V*_;%Ql=yCAR`C*z6V-X9vg@t9Q7!+snxJ3djaN|Q-GDikYf4n@t zZ=Re4iK0Sy^c@pX_&!I>GHIy&$x!|ecx|P3h|?- zk+5Rw@zarZ3)|uj-u>n}tf!m)1x=3KB}-lK_uyDS8ZvSGbSjUl%;|p4ngL5e`M$3f zC`g3msJFjTDjwnj)!!E(2|)~(AIHF#Ra<%q3v0P(50 zquWBkNm=%*ZyqW}WsP|r6Fj(>0AGljOEZt<0NSK_8c7I4)|kYyvIw55Z3q#pSw_;E zh=v>IeiQ#7__PpW;|faW zP*Vc;Y{G4l6FmOP8oZDSzELygzOgGM1m6gu5_ct~dYez+F%S!={BVkn%T>tO+aR@W|E zF~kiuv{7scwwZXC_kfs-n{&T~U3a6%Q$4d5w@3IP#+E^i{ApA&kL5-2^_K3dz9r@kaNKq)HAzL`d8PGyYWvO0MW7hVYB~C9Ep>w9gph31 zkF0fZ$E}zH+ba8YBnzv!Cz73)2@!5CPN9$K{ zP|luQG=ZLuY0MU>n?GXpVK{oX@4AHE47#C`59AwZ&l^yUOdgt{Zp z$nX5Ei5B-ADirkAPAADs`m9O)Nnr8#oX$^#fiznQK^g2@nzAKcA(O{j zUjVIgv$7=3d2$(>k4xhr7t?i(G6Yer!#zCL@+T)JmVKYA)42fA>R?OYj&;zSJ5N6_ z3G6Uo>&FU6OK{-;Wq^qam4Lv_m|2~-t5NURg?a@P_U>z7#|0-Cpm zeB_~o5Yxh^1y7WieOhLK6^j#Q%1$qzl1ihNHD#m`B!6-550bpo2hLoreW@O^!9M)4g0{g*%%^_W8VoS8l&+vgSeTP&svdTZ@(7gB^kk zG@Bp%80dH4ngJaIm65bge$OA_n0Ea!nzFJN7`xdrumqN*BL56$Em7H1DjfzvPD9us z?CS)z_qB(;uJ`Emf3XpQ)zI6tT9^W)$}J+B51!M3?lAses}Ls!TQWtrEcoZ;{R=#w zwHB8XR<+$T`XmIanooiVwPw1+xun*qxD@A(o|yBH)^RuQe=L00jLb+Ix-9as$Den& z&XUaTGc;>l+9~vS_0#4G%J0jCg2gn=27u(w>6{%7(=AlDzr>g-ep$d0HcU`(b7;zC zScPI-lgfS%Xn)T5R8vicN`@u?SmA*o(cOHUss1N>2B|+bp;xaL|Nb*y1X$T=0@?!G zcFt4LR@yV-G&ou_!=y)+yc~?2jSh*+{B-={hebwcbUsa>)xCFoLiYfs3;+Bq#19-o z6qnrrrg*h-gV%TJCpyGe(M23q%J&SCcw5zwn=}2&gbxEAV32MryAC$vxM~BNkP=4= zE{7)7P~9?&gAKms15@-ju(P8O6n9JBk){!KEX0K?cRQ~a)37Ki2v{4;$%mG-wOR3ZWV#`~ct0%ST!toD|#(u|Sp>=3~WMx*uckF|I*x6r|y|o{Lzg1516_&SA$9>b3A+<0A zU&|`A{7Bvj`56?qYb+c}Ddg^dq;i{mG(TkNEp%zaCsHgd_3$-n)tgkxQkn9x$(~{% zk5Hz*M&bb(C;DsBwo01FT8gdux8QtrA5F$({ zDx1a)Kzo93i9b3P3v=&D)sH#-*&6jRA1E$XeKBF5ss;)JW#?2v3+Qz+*u9qXb zvwvzTEg9~F&>bBpA+#XO4CW)DmH=6LG+7&LBk{)z&vBE(lWAH9YKE!@{k=qp?3G}e z?p9X(Y9R9HiYw7)y&RJ;QyA`NKEj$l<>6>SW|`-{F`Q@opg|C7zhA({F7K&-zcmPa z;qfzIy#76K>4df9-8BjN`n zKB)bw;BM?I4f3zsZg_$5$t$JnCGO4f(foUByy!OpR*!Q4jo79SF8MUPS(7tCuL3qR zdzTCGC{SZG392=n)Y9@_Mf#$YqTNC!cO>0=CU%GFQzx;$3^oHMhO*K>Sqx>^-DYXM z^S>~PRBy_K+@k_WT;T03B;|ks?Lp_%PqgBs}+sg-oud z=~>CmW;;1OYT&6F=VaRTZ2buyoWt0Kc(99n04~?pp#vHy+Sn-Rbkz_q?Cq@=kfwFR zVRyD!HrRtt(Y??>k#AK2FVjs%5J>tb_~uZRlk;HHI`=R|tB_zG7r_AWKT~n+Ybg(I z07!BwQMVdv#l;?0qpZaA!DQ|^Rz+UW4Dtmj+P8dNYr47H{v#UTlvHX|ZJD7JOVf+W zV8*N2_F-Mv1dbaHRjemFt_9ujXJ`^3zSKqSH!nxOj)c%({mhhG@lb6dusOJXR**!h z$>C#rIk8(JT(%euDuhkL>QSe!EvnJZZI=jvT?$+O*JP1$-I&}ypw*G*Z_=kBicYBR zq{%*Dt!9g!UFa4mr}Uwa_i9W~&}Vx5;@;H;?0p%Boj>9Muh5=XK^k`%R7sT#x@y5* zSOplio4Dl$cBgd6XmVd7CdI~T_h3##x0C+6NI|$b*hmauLW*~KC41sD3a%IGk^z!E zGw6Ck+fzpHlOJ*}B@E<`(C-!dT)KPlRcD*vYtBg}Js0qIo-RFcNUNV>WI5aM;`!!X zuC!N7tG@9@#QU2n`usR4-R{pqe7xV&ShOde7^0t7TnsBHf=UN!AA~*BWP=YDxc!sJkQVkFSjWh4ZAf|p}ALPik~uU z2}$>|%zNGh?1l=-BW|Or12W)(`y{!C7tMFp9~3QDT3<-Id zVZ3FVS~*@(FmkK11-M-Xx*mpo_}T8I#jiU=dhOS*LJIVAd(aJg*P2{HWAf#gjJ+uF z$?D|m#`sv)v~cB)k_aYKhiO}!v@}o`NW`8j{dUWx=!{!*omdL%WDb}o_(agnb#_0YFgHaonu33mW*)SFp&R5RPwz?Y(Fd6=v1HR2t6>(xBkJ& z9BIa#Lq8GzCpC*^`hG#!45?lmg4O+TnAe1TME0`;r7@cg;!TvvOrA%S;ycprxywcC9z)`-m6q z2+K6I4LU3uPfg{*0j$$Q^FGy!1fWa*Yxb~EL8IPYJjT0gt`czrvk@jy2LDL~x8gU$7OFS#C#Ko837s3r|`yCd)M^ci)z2qGFYJ8N)|oPLYj07sO1 zR{VKY2ZDHieqq3oV;C#e@!t|J895L1pa8$m==^{F>G_vA+NCuQO4)x9i1 zpsTa^8 zz&E9z+Vj?MpCZ;#ASsFLzB`fbF};oba-Y3z+2TyjQ*&fFy>4U}io| z{3ieDmY1#?^=qY^S#rz$B9MLbK_T*bVi_-7J;c#AS20dQe?dAXmgI-L{n zSN5-&jNn72V?}W9%OdU!z03rfUWMV)z)>yvk|_&jAQ42C`S6fpC2rpqPszbHOD*>e zE8rntN(_KRXO$7Xc%J`lEc$v{CYgZ*V8;GMmtZb*C2m-S=zs~Lf(zsXE-WWb=f&^r zQErHhu+F@-=D)@>7|sK~f3a1X1G1XntfP?@#obb@all|yojjJ|NoP*Cr$jGxkb5z> zPB~1GPeteMGe!LtWAtx&M6o+j7@4Z?@p8c`J%IkZQLyK1c6rh5CmV8s793*!w=7O) z1_Ke7vrgFmtp#8Me@^WaKc2ddQg|eAE}{(f)YLo`QJ#<0;!(;W$(bfiVhL_I&lS|% zT8QQmGspbwt3sNp{=Z%ib!sf)I13SQf@(GnMd69O=fI5Aff!-<8k+3PGoD(Hvarhy zl)yjjdeIJ7{{7o3cKy-5$1C0OERv^(i}h2-&I;MiH)%V8Rkoy7ouW`aE57Ki8MPtZ ziTgdxsE2_{rsv^-Qb+W!Nle4OGK?pCMIdY0Ml9L14EupGGzRO5Ts*j!2A#3)63mfi z5Z)TBK4Fm!k&b+2Z;uGVmtY7LGD+BkbWjI(P{W10DfTQEJ#aTI5b_xLdYE%_9)vJA zRIp~JkvfcIo;n|3m1C$namaqYscQtNE~VX(R?-Z z)ITga!-Zv}4wy4|qldtr3AVi_MVi7@9=YUXJP8Z)b%C!@pK(>2w?%1t?{UGo=8{CS zYwQ8FbOcrk*%Q4M>Kg=%YYd@5tqec(xbA9o#11nLvo`3I!u;qC-*bJ_UKOS**W?ON zb6-esXHD}aPIK$|?T3%?1mU++p;Qy=?l z4A3ofOZ&GrI9jE=XBxq~bGfphOdMo=1G9TxQ(X!^7%BPIuA43;Hjg^BtQI_H7v{WZ zIPx5tzxdt2D{%1FC0IlWjXRgL{YG&tA}#CGhNUXP?hV1^1kZJYQIIm%Hy;(eF{%-` z8N91Si3-DAnaZaQW`o6!yqW;>d2is0pS7}F0j6&!g8%4tKekt#SgoymzZRg{GNdeZ z@15(_qPQSyYCgw(!6)H2TX}0&C0sISznXF3l#fjedK9$TsAV%89wdoX) z0SAsC##I)&=o`5-VN;T_IC0Ls!UK?pRFu9ST=+t4>~>@U7;34ER`z}}o}F+PMO<1b zsQG{_bf}|HX*;#$YC6&DmH&5(3~j?fF_@zWn!6?nqJqRN?ZS6YcB2IJ0@m;Gwi4d& zLB?tRZdK<)GXidfAFAo=oZ$mBl&ycJpjDhYsd9F~OY|zJ?pW?6;u9EHGy=P~Lg&Ir zFYAt1EJFOA--hLhf)^AiL^8sKvz>0(Cqy!p>-sfUe4WK%BOSr&=HgI&!wzVyX-47I zKVl6c;LD<_PB7I^=!}ybT>S@6BPS?dJ%8pgJq0}5 zjO#xv1#cs`)1+ra3Yl5KNuieg2U`>n`Cj!$zVf}U2bPVAmlW`-U{|8Hyfy0i#6PYQ z;l56>A%J2j0VO3_6+Qq3-jKChmS+=U&j~S-(=NSLW0`d$cOETg!}_30hCz(Rw$*C} zG+g!&0xA}XzdCx8v1vArq@}e|B5I|=f$|!)>v9*%e6_XOzUhbN_Kp_EHv>R%Jz_Rz z?|oJ4B!Il^uP-VMPv6>Ppb=tnqu`A+X?I=Ls*+?D@$+4S-%jlBq*k&5UvRFIBp*z@ z>+f}lU{-HQL*Q!}DkWF!t$)xX?tQ!Z(XbP?3amEW;r;L|4p2hbAMH4X6l>IabAZXW6F+oTn`S z^GB_DciG4Qx|>rdi<`wBqhss)-iH7O0VTG2r!^HPs!CEEu-06!Q6IooqM`rMp`gsG zwZN<9kvFNCRyjy-6hnn2@dLiDhGlZa`+&(*KO#IIX_JB=mM8zt1sW`c54PQj4(Y1h zmH8e=9Mu;IUQ{K?U0&p}(#bxyWmC^LFuyb_gX!-8ovdk6t3Rs_4F2^!etI1@pu-OR z-=HfMcZGE65}xhW3wxd?$)oUOy=SK?dARQnbPI%2V9jRU}I$|bnfwr#QCXpbdMYchC?tS`gTA^n|)XK<|YAn1mC5f)&aH=}?HZ!VV@ zwnk`QqY`Ib`>DA#Dn>dY>VivKaf2CBs&e47_k~mIfh8^v^DNRSe`%Yl zZN&|~JLwJnRW}&e5;8i|G!eP98M7RmiaKTV!^)eE*_H)iqv>ls!80i%AK~5~f25(e z`bwG79X12Rc4oWHCw0>YEl*!!pb;Bo0|HEdN?X#^vm$k!uUWCJ%NrJ)&;IGuOnl`J z-^j`Wn@4LO)3O&5_Xy)fu9t_neb$AkhJtJ@1G9+CKem8Mi;{p0VPdG4DhX+O11}$> zb3AETwzHBqtI(S+q&E{|(qUcY&o>%;x^)0j z1UrmL=}|T&JUw5tC3lyjZC)%vG3oh<%mPi9{Pl7Hz2ZX#EvNzQM~jn0orM2$=AskX zFULqz{U5$v5XiKm+cW18;4SMM>xa`_~b`;b!(XfE3af1z$ zp@|w*2zw8mI3J=J-miNs_Vl0$RqbvXKfI-7s(eWlKu`@0v=q`^ z$u%HZaw+;hud0p+)U!a6&(;`cq#W-Nf0k`~<`LLLps0G87qG{%IQa$hTp7It&hBL9 zya*zp=7Axw(z*JU%X){+0s&1CY!;{Gsgsgkf8VOFq3)lT>dN1Gi1hYVbM>KcCNzL^ zqd1q7jk@Nz4uVhE*Jq8_+B{UOtH(U8HsGVq<+=t;SknL!@Fz!Od5$|q?KgZ3Gu4;K zC%g&DWFk=v8Z>8<9C&pvL);_Ec~;j~q3kq#ioFJhq=bulj{KG%2E#bNdQ^jNjQ;0e zoH#u|HeY_&+uVaiK>TIOATM;Xx^aLlgx*|(^)q$uVb42bq@{o!9~%YfT|}lia-^P2 zV+#fLhM`Gs#e%-afA4Xx0BuqE^Wx=9g=2nXEJ=PLFi|CAi1b_W78UZ=$J9+^1{TUu z#TVk?ATSY^W9KF5%Cro+q|pcOJ!Q{g!ngke&5>It(N+t%FlAJapZcY&_Bkt+XsiD0 zlVK*eo_jw>bo`k`GLUWV_f&cOX4)8%g~!As&kM?};XbS5{h8#&hveUtR`R_#GQ+pa zc=>U6r-@n}B)ogTgb9d(#Xt&)hr|oHwaKO_EL81q9dP`I7dxuHJcO@|XshnYC>}fI z#fTAnrT_QIvCw3MqjiEcT{rr?$|b`V7>|M4We#AeSA+C|Z z>VqpGv-pF0Moh=nxP|X_NthS zp#;cz40N;uok1WU%D@KjL)@JC5(5Dt8)Z7jgwfH$N;l?Ym)RNkco7^+jwTOCbYiX~ zBy@YwPx$i1`ZdB;7FLtSW!94O9_cB*1l|`Tc@3P(<_#0>egnjN70t92_{uYeZ>VrH zLiweg*d1B5M(>363$~P$P+nQ`fye(Zr+HG}m@_>Ml}BezCW`9AN=AX`SEk{YBRHQE ze#J3$Vus^$ir5#3?Dtsx;4wvHKnl5`l+pl3`ZAI&YBv-#+q7N`C=vF}bRIAfv5Tc}Wmysxdo6g(t%r`^3Le*mjPseIR~@8uu?muM zlM7F~Z+%5~24a8I%bYch{xtu?;L43Q6FOlN2k79bs~tbrOuV~yUq*>8viWC17>j}u zG%7Z-jE;)0GDmV~t?RQFXW}X6K5;OB9~-88y2YST*U{-g3iGcr0D#rA)B#@t@AnCA zFHNWh<*>iS^$VB^3ARqgcltNP2J!Hm=zCQ%3LaAS;hB)U@&by9ES(=ZTpnu4JzsY1 zFxj9B6BHtlw}tTo-)I3wY*wGpga-^Qh2K-L_9W5%fe&~>A{xH6sgGRJ1Wky~z*a2- z_ayh5^Z&=wKq_#ke8_QW@(1%!-o!8xxLA_jwD_e+A=HLqc9zlJs|dCjaS!&m@<<)! zT$tms^sV$wKv`~W0ln@;Sz+CNHyzmO53Q?@cwdS0ps#Qok+PrPqhdA(0#kvPZL5l( z4i0Kg-V^>;qY!0DvMu`1D@jS7h+_840?Vmf{O5i6 zU+MD(m35;)0m+XN4aM$a`GnLJ5lwd#vt{?VyVdrkU#S_Jus4k`C9OH@%7h=u;16gh ziJOco%){?551It-1>%Zjo3ExNy(|Lvz?Z0+znU-yYzBsW!7^}i7F*hB)E5EH-Geq_ z)eED|-#^cN+45r$H z1g(AfTl>l{y!Fde*)D_N*bCGNuI9LUjth{;@el8O%8=t)X}^3>l`xI=tII4w2>Moy z)%!O|K$Ckt<~i95ZNc6XRMj5f9vr~EmjHnMr7*Z z7ng^`HBi?-XtX=;CISwsd;Tz3mrTx)iu-$5o>3{-3r}XZvZf)^-cTsgSHL5T6W0?} zNg<1Y;b={=u?ma}Jum)AWd|n;R=_VQf~8U9shsVqstn0+PDLo!ia{%`j+oYqwvr*RkzX^XF zlL*R^pA?lXQQuPV`xTowKHf~xG2RaQSV}lMGHlU`vH<}t&3GMvqh)WSQpDbhWTAM*q zfdnnWcLcSsSV0V7iJAm>-K;LoywPM2Tj1e#+{=B6$1TQ+x*?%+bRfMj#|1^dVb=oG zw*PFE6rN&4EzZI-o{|8Mw^pL>sSwc`<8Lkoa6q&zPkLQfYdjYeK6p=fF7<>p)5;)uIxldhgjwiZ zO7z&;h4YrV&#-?(XM?5~0xSG01z4li#AT%yCf~n_M#u2@(b5CXWQ#8^PS<)x{@Zer zt|QrI7)f}eF!db2?rVyZ1qW6@25%Tach0}1S8k;EOoFo^C@y~=ym>>KH23x|wMn6l zg?D(ziMaGJq_4Ig(A!TwxK2W{tdh1)=oMvY7wPj##t;|W`|eSq7ypZtAMjk74YN+x zq&+u*wXZ~jK459le{e!mEjoRi5=|;9L2818le7(VQYG10ePr`@+R z?{!uAr4Ec0ev^f~uHIynF@p>YweMmloLsix)MDBRKVxucv_G%s;S_wnd4@x)bV`5= z1ajetYNo;$QtsKNW-9ot#1CL~c99+YLaVDN_=YIzI2t&Z_7ik3m-Y4AR=-)FAKaG# zI2kp44woUaqmqn`(n|G93x3s)+--0k8b&t#h2vPtyLn92C~H>Hor(v=M)NR6+fr;P zF)5o#r;_uVK2Y0o!s0E@usOGdntdtT#%RC&qvm%Rr6!ihppjePDhBPUz%sfkZ6RQW zcXP9m2(xmVkPmarImRZ^l0{xt`EFrJsn!h?2{(PkZp12;?WU@)>5R#CV`9Gds2qdZI#Cr_(J%pHp8o ze6lyiYkK`wh8)Xnv4qo+a5`B9L;%HtD}UQuThhc5E(R;3n=&eh4l-534CmhEE|+n^ zowM@IYfOFSw5O+;)?aWS$lU^y?&}=WLkfj6@D%T>D~^=i4R(qsDI|AYP1}a)+M==D z+eQ?Df?xgtl-=<6AUwfRLigwBPIF~_jU)gjOn9fiGc6$(h`rQ6(*;ZX623&gv#;Q; zJhC}&Rkks=mJYjBkI45a;Gi8PMZ77K7H9!Y3hBfk8L{cEW$^Y8l@-<5~ zH&K^(Z)ww2*4uB`7W4`+agD{eyVzud)&mzmh=TG95(oxm;7`SqHj|XYp1Spi`?;z) zyBm_(@))zo>#`ZIfB2TfYNiTO!uP-=^{COQ*7)BOFLxr)vH#QazkxkYX+qvZ3}fi` zaS|HT0yZo;j~De&o^`!c>n0gZ7e}NdL;wh^Sy70|Sv1q9LIHy> zE}b0upzMDXfh;VeK^)V{MvXjff>iMoo=|4)*j16vyLI}?U0m_V=}O>!vD_Ft+boJi6jxsa~eV{2Mtv`5$L9 z)$**LhYuIz@#m?1&HWL#%r&J(m!?{9g+3y0ATXo(1skd`wQWVyqVSzp*kPc;EzZisgMTslS2+Oy0N;~;=#G`tySf6i zXlgcMx-0^UcMB_|%~!uXv}-p0gY# zkhwK9w7{6OxBo1o#jeZ2RN~(M&$D2og7S>bn4Q%LYO48U*SrDc74_F%9~pE8c#5_; ze!@*I#TG;$>;!X=`gAuXK3&@)84q0`KquAvG`zvS?F^7yy*f7-wF~ z>N)e_=u~KH>FA~PZ$up?->A5dR!`&#wnQCJR#w*rD~{wy0fto=3PiW~$lQ$HhUrSu z+*gdXf)5rqp93Te-6E*u7ariA6z5`sJFl+yn;nAZYTm$H?Kqoll>bxS4&0z2<`nd0B*xc^@tpB4_(aX2fJBC~oYXi0 zYV`?LD{#|^%gHXkkj(Xg&C8-6e+??cn|inOkB6JI!jbL7P`Q;hCMx-)UeYbu8*h(J zuy`_M3>I~htfK3_SRhW5NdKf|mO3h$km_Ll#w041@UZk6-V}6mIO(8)Zc#d6M!v`* z%+<`}LX(-}Bv0bQlidNT4pZ2Jm1xU96NQ%*Uj4P0FT0YzF`_k$&cP*~D#73G?^sP@ zN-&h@I^RDv@OEs;EQrX1=pgkpdDgR7ypX=h_TCKd?Pt^hof7hq?pjjSc)%;ly0r0o zxq{}G^z!9At+l_{$XQ0f%7h1!uWv6=+~dc66mpQe-BjFSkxWKjRbGJ8p)x2WpQkS- zzwFHR=j-$h^Wt!K7f0f3^UR)o=Dfq{?#IvRb`=97sOoM5_u|I$_+_oG=xRkma=^dv z-aV|uxScaU5^10ge72`*DwMnIq;4ymk15A1i?WhNeJ2onD8QJ7KC5cPnv_0^UG8M< zFLAmOYKCb$hql+mc-+_h*nmGmAR;^xgdN&7hgFR6n$!fved2Ihdf0px%9Q3XEzy?Q z3Sji{o%zZi##gH2JF-M76!#c+C=aMV@>(u8PV0ExTe7Xh2DLF}0iUM1?(9?lb->t>L>rrj!gy=V^ThSN(> z0ay|z#HavC?>QcRhz&BSJ;zIUMH%=r(_$HEc5c#2OrEmlX+0K;Ys=$U{%v*Pf#4~_ zRrYg3mo(BoD&@R_?!Tz2doLd;@%$I$=winXTVT#zguHiYun7MSGeWmsS=K=Y5SD7N zx;qtQ;-2Ip2LJY&8-D)RNB#}yd_R*J3H^gfF^--j{J}48s)LlD#;_1PEhua8?Ofrd z`6fBAeh+o?_=b$6V}aKS>7}-k+2(Z=OqnZ&$^R1P`c-E2?#d_baS@zw+7Hl^cHG#H zucX2YhXrGajQpw?fHxH|WCcqQ9O|_q972-mMVi3qtIYy&S2);RzdGI(4w+neEW2T< z+eSdkM8XF;Sv)j&u|(LF0extx7RVS9+A64{t}53`4#=;4gez4dU`YwxP@SUiaSRB} z4iw%c4-EB`#nEk>O@HlwzgPDLsQ!gV3R!A3moxgAVrBwDWiQQ?Cd(Rvjf_jsjFpPP3Tf6~F5Bp)L${w56^&K3 z6vaQoGEj#UzbcjrV77X@o}b`$vCVj56Zv1y6%>u0V3+qhU9A@gAY;649Dq z*kEv)kjcq%wS$MT1A$hg4{h|qpJuBb6Hxbqp+T+FxLOuk0N@6BP=l3DXtM2veC;X& z%k=Eq;`}i^^6(Okzi~&LCho-Mvdts)sz_*q6^{*=5>Jqxb3M$|y3_Zbmg30`zOogY zW(vKgv6QG9d+H0;bh82ebG2QcXUAilo0Mlanys?oGyz$RED+|IiRoW|Y6LB3+()v3 z_x#;8YQR#nv}`%jedEU#@+vmM2unAj@BWL(p?zFEVlHwHZtxejcDl0%vLiBG4DO+j zpGkC-u{lFFA|J!Pgb0?y8Bo-7u6Ecc6+6?;M|ct;U4lipqZmu)A@)b%0g1O}_@!o{ z0Bt1I*Dsi?^ThyF-Fl4pRLPGXgLF&8+?s5mZC1B{x&8LwZWPGSQCku%ZV;WoNkTXk zd1SsX7)EQc_27hakPM#VW)N&if;cpWA?JJp0d5C>=CKq2_dMlTd)$~;8mHa;A}>2v z_`}l+ceH$ag9Mvwvo_{+fp+O~lFl6Ek2=FeX}jTcANrfj%(O92ABtyhv+6)HAp$^NkYKFGT?U5=Ipl2%vo|9MW?n5bosRC(k*)=;e#*w%Gj zv=6fzr2;Ve`usZ~wlm${C{4lKoNd%#j!!VhO*K?(M6DO{i+6X{w-x`Vkk{zd3VrNV$E%Uhmn$+i{n%cTlye}0Jaj_$E z?j)hoEWgvd62J(Z!!;^dpKQsZcI%Pu>#Vky;-XZ&lM1IsxkISby_|W6 z(W%JD3Z%&8%-x7sg`i5Jn1ef7uJtcw^W$&QS~>IRqg*yQ|MQ0t&dz(1AA-v;Vrb^8 zGghMtq|j1;U22%coaLEtfY(Iqv#Z1xmSUxrcBv_rx)6pSuCI@;vI;GJ_I>-UE$Zg) zc4O02D`%DAIJCCY(D%dN-JV!6GpX0ZODVJPgJn_c)#xXOKx&`Gkh&9<2@*$){*&jh zea7>-+I7b*lCF4a&|RZ4JU4W#tvVGygdzgYTfSTNMBqWqFDC+%FP*5H?pp?`T&Rt5dnz)AjCn!e$ z#$n6-i44A7->u6EL^KuyT5X(%KltNX4$;^fmtCC3wYcZ{PR&hp9^^r`%@K(Dlfrqh z*9%-1_E_83MUGC)=TCn-)4Vv^ul$uoGEHo-FAK0jyQ&{1f0fezMorEWE5%leJ-&k5 zZV-6=Bq$B04nmf6bey86&bx(0BZviM?oHU_;o`mSaBkMhGQb)9LAaX3zRo25$A#df#}+kjgKg$_u94)w z3_A2%Cdmm;KVn3=D_VyZe0_a?c68w8;^e~IXlsH`0uJXL&DHijMipG9d5<=1O`j&n zc|0gvYUfgLt)Lf~*w!5JzzkYIsnmBYyRWM|VsHD2gxk-b*kAaETl(Lx^*6}mwGCqY zOD&5AR}9j6Ib1>$w90BuZSk7?8xMCHpr>==X>XGD=5iVEIY&&sIP{w^!5rwJLFbD# zpX@A_8_#(|t|90H5q9~2o8QOB$99~DS%*MRoDZamq3I;us-VGMqQ!6i{rel48FVcA zgFP!pNe<{b9h9$1wngyn$+1D?;tWTqqHQF=z2r5GyT`?~J^IZ1#=ep!69(h$ z3z{*>^iHp~e-Uf;@7Qv-vX0|vb{bq4n6)Z9vgfiOP0@Ojn(Ug;_L1&Mn_q)dpihUm z^GmkzA=OyKaxI$rWMd>-k`3J`%#^iEw(dMxRGni>8ufKPwj|J-8xhH6aWf*6%4PBD z-1#;NF>$!Do4Kfd@l6*thScC2Nk<+XpKL6dxq8lf1MvUKwzaplC8B&?3G)ukb$_MV zHvK;|UG-nn-~SyYDIo}gAT81e(lNSIP`XP5q&tR4gR~$Lqq|#bq;!jPclUseZQs4W zkH_Z^*blp}d++mk&pFR?Mz)$9n$)10$>x+U_DUc1C&V)rs~W+16_0!HxhY_C%~tk* ze2!*v63IX(doOZ{>Ugr{AY#6ICP*4=Z^?#*VR(>3y(op8SV`$NqTt0M z8+$YiChh@;(il5qE&Utv4C^8);-PBV|t%_KO=#9W0v- z@h{`8kIgNje+m0*ip>g=L!tK-9bI;0+1@AcgOj3uO{a!^Lj2K{g6T_Ab`fs-TDgQa z%@E&J80UcoUZ>*%uHEkeN8nS0YG&FlXL?Y5(f5epF9^R|<5Y+Vj}--sYE-7Tp{_~8 zrRy5&U29f3vfEDsg8q$XyOKd{Jn$iF?GSd1lg&ZS=0B-n<8R zgR~dddVQeZi85_Hys`Ok3LF?Uhuiz1Dybw@a{R#Tp3122ZIhE*trY8#^7D^RJ0Fpx zWa70TC5Jmoyw=8ct%hpmm!Cyg(m~s8fRgiX2RLCa6Aku=ly$^e3#2cm<_8Iy1lijc z1`YRH?#1$zAN{6^l={8jh=S}(5siwpYPe1YI-`gzBGSc-)EwC3XcdZIPX&yp4_zJL z)oR^E(BnW>2PcDg+rk3U0`NF`_?ORaU-4hPlZdLH8KNGu{QJTl%blJ=SfDYKhoIGS zR4I)(MZagA>WbUcZzxY-*v0%HR2C&Q85{HXjQ+m8xi<^A@ z$u3L!NV8^og6!|+C}x;L1_wNGdOZLb3pH#8*MkfiK^P1v4==95!-1V1TnKPc!xTOq zf+1ElD{=9~n>1&2%2S}>5#zL+SrGV2VTURs=>FHIVisR4wFjnbS#yjhX3MMl2UFZ5 zkz}L`!5%M~cmM_G8Ru)A0oUk&iGAL>t>YphfYjqbA83r}VNvJD%6<~&Y^5=-Zq4Z{ zivXeYBqN#+OO0NIk^x%E1rvdKzs|QyV%Xv&s7*I*UQA=QCp>?^Yw&z;(+3u3f8rL^ zFyh`EvI`wfXZGZ|FPWHPUOm`X+?rj-fleDo)+evc)a=>}mEHh-&M>{Q0kYJxsY^3w z6R*|+;igLqYZu2YGwV}Ta(XJf_M_YgHRMN@-<83O89jnWkk9nw+Wl&q5~?ri*QyLF)0VVJq0qcBr`3y^Sb0*kdv{a0V2WZUE!fhJ!;#xhHWqT! ztlq1q>^9;(g>$ovsz}Gz>ICm|oCt$mHZCq`LnbDfuim1^Y5SWh&T})zH8X1LfL5_@ zk?J44wgsAY)SCyQf~e}sIyIfmRkEVhZ;!r@pKZC#_FegI+-0Uv&z}wou(-n@j4l5tsdJnbGAh;SRG_(J zIKMeFVU{Lwp!UPx%nY{EFe!yV8MFE&{r{i*FSku${aPhtikyo_IXR!TcG4d!=`Lsz zd!y=GLozWn7X=@U5UCa~sb0u7bH8;iQ+tWPw?u5zA$NaYX9O-DspbxTZ6a;(Q=F7k zHe)Mk0^8w%dg+j#ad4}!g|W^Jl|_NhgsE(-(KOeh-7=e7;(k$V#-#vn2F*+=aZ;wa zQMxZVga_cmc}o`%xW&giB`B{p*D!wC9V2jaOTnb;QA`d~Os~!6Kd=t?9F}c|uViZo z=gFt1ZfohM*o{9>dWzUM5N^euDTUbH9^>s#LVESrT8mc|Z;^-MAYUZrSDn=_XOtW=YR=U;u?yQGxsT+3Z6?MuZJ` z7AJ5c@%v2K7Ky215AUeXFMF3Xiyt{KmyyHrkA=&&#;T-ULra;?8!J#stlGEE`D+yA z!p)-X*eir|M97_4EffatrawDY2>5Y_?apPr3(>C0aN!Pygoi{?G#~c+3DB)vBlCWT$~5GR-vz`!m&M*3Io3by4UY}0t~g#wb8yA)|ChmDVO*kEt>hxpzq3;#z@VNG?*VgGzFwn;q3?(6s?HGa48(|(U z-Bz4C80y2SyJxbS&!!t#Ni{udYF^BeD321%!Oj2kbC~XOmy`F77VO>q0A`0yH}ZKl zqY;-=4ELq<5*LLxIfmZP+lrr7a4bp5J8iO*dX{~zAH~;)LitLlF)T;Sxn;hMV<;<9 zIxJ~xKpw_jP^U!b(v+eFL<9gB=vXjA8uMHaH>M>xht9JkN5F>lL#&@)mxd>^9#8!A zM$YZp3={`e^Ne4O&e=z2OT65@=F!?G`uHDS7CUxb*E(9wGuxnbY8^H#@oRElBmnxR zLCy{wQMG?9T5T3Uh}(17nB9X}e8CvV>78F|CrPl`C^WvnCInNd|MP4n)P1I@sp-4$ zx*t`hv9W9V5&vbJp>W?~UxDm`tML`Dw~Hx;?lM7wUblfbwIh8->t3y<54%BfO3V{?!>mt>GH{vg|+59)Ce<+LjfaF_fT zxO%_w4b?L?J=!H)s~K|t?GBj8@G8`hhhd}XR8p$pH~!w5ueYjEvMInMAni4tqAx$5 z8RM&Hd_W)bW74E_^wHvCX27Z~L#b}D`A&Kum)2A`#uJYy-^Gv%!iD(7$a{*k>y8`d zldh2Tu1paAV2W0P_!C-0+Sd?)Y+q^6-fE|$lgqiyt(LrD_Wr2oCiUG|W~TAu-txIs zfPEwxa(^d*jMq6gRJVziTcoxAAtX?(rUTu7?AmHt!)84Zje#dS;KfK97}oDBhg)&* zOgy#-`ZeSRMIJX|*77$*`-{}pePZ%9H_Qo?&uIFc+PUS7- z$3;&8!4K$oX$tJp^v>ISy(r)^&J^opz<_r1on4e?klS&svXn(84Ok>8~vc?+q z^Nb54_-C(Bx>;5d{^N!c+r*FEBhOsLX9S2@N1q+Q+DLCr7#`}JsSG89;|KZp(9f?w zN;x}yDQv4gs;s!3^vpT1HCt4&O>=dYlgi5m;V=fk0!|&}fz6(;{!P|RS|Cc%x}Hx` zeRIMPt$p4OE2u-sop1o;E8gWZuf`UL795V-#LeY&{zKt(ibKr~lFwC@P2XJ2nNbvM zi=b#OJrVu@EO}(@{4FqW#QT8@rVvG|iQ;I+<80kTST|H=P7AmO`>-7VF&u*bCE;!Q@{s44ZZlLFpiHRVjeE}fr5 zBJCjoj|aoqr>En6zGnO{f=H<6$ljGF$LBEMa`KT!y|CEwA(`CXZ|T~xIN%Ec#& zafGdd8bav^JPPwVTK^Z#eOLG&Kmses>QaZh-kLWCiQMpmv``Dgc5_}FwaLecl1 z)nLz-?$Qg*r|ue$?OI(onb2e;x%p+_$K_AvHX}OC=Xua`#m_fu>!F~}EE)mpGrSF2 zYN-66eu<~+25^ktJhde@%(u@fW}Lx}oHE^XMePcw)RM}p_x&OUVC_zjxY$Ow= z0{GgP5#Qa9ehQ2RG=xYu1cIFY&Y#qZ^X>dDeLjUP8@Y}`-tjsEE@ntWP*>*2o8#pc z7erb^=Z^s=gDe>_>fUgj?6|40^T8ims&*yrtkY&JhVy+K_81o|(I9hy!g*AH3@Pt&XKR zg_}gw!O66r#|u3k(0kfAGVBqbe9xtps`O+jYMIex5Vs=(NFOyLl{G)94acZQPESm! zkcEa$un5>F{wz$%%k+89hhQ@nDg0&SK2yX9`(%aKfx-}oNlK#Q>w&^s zZqpK1>lREes@RA~u^d}Fvf)(%CVa`JT^zRvTfee#$iDJ}HSq|W4+6MFxjQ4P>-y3Z z(ZU#Nk^7yCgRH{@K&SK4{HX3TAJa%q{pzh=PO6k#_BI|X2&3o^uJgP;k@DH7v5$sq zqxEfZsi5*o;stS$Z;mcO^bLy(JyLxSPR{2RN>ER@#K}s0=O^oj1TVhWq0h)^J27Xt zQ8{>cp#N^>gJIrxeD_G`O@KpmaZzk}#AH$Y*J}DKx=D|)VDtlK9d$(ox%V}Qrt<0h zi_z@@f@eZ7aHBNu9!i3eiB`4D|KLYW5eS^Sj9vagQa1<4;p`?%fxWIc(fsO3h?#|Y z3fWK5SN{^l4>JM?9%@DEir@eYbo>(ozLv{9GX=LMkGGa%|CH*WDB)?IZ}Ht&Y0hD% z7v9*xUo<7S#~S{1u4bgd(>-4n)y1QmHlLW1a-%gMU?)%8uo?W88M+`1s^t7f7?LL2dZ^!uD01GGh;bE9U@h>@MFTQ=ZrcdmTe=cXSLu|1BDYF^f+N;K z6Nja%Uaj&78nh8!5=!n3NOg`uiaB#U%=1S8+-U114Hl+vQ0o7iUbkuMv|>H=~Fq6OASRBXEMe+K}Cq(^PD`Jhk6+l;Sc zof8;Qm$*t)JJdgDYXgK=B?~4BOu970UUNvq{r^yMn9LpIiq+BGk^ML z+^2uSmwJ`qS2o;Ms5gW9F>Taqtx(GN3DcMyK{ztH`=KRHC$ zh|{^!d2AR;7ObDN|7J8s(iXLK>tcplGy3PQF0~ZAfRtpc@+-8a>s*r>+KvvBH$$B_ zs$AcrkNX3t=DFeO?Tk0Zf!B5)3FvCjezI#=p26-W@j54|<2WFUIpdbdHtMZs-`+B= zy`PWN;J58>@gG$0{xIppEn@je* z4ef=66r0_2*+WVkZhbp?mbP8%$XBQOf6GMSf%U=VxQmaYP(c~ssLSZ{+mx#Yc93E^ zRPfk!H^60MZdCtg1OHsRZ`7OFnnFVH*cLXiqx-Q%xfO^{yAH7Gj^y`5^ZJGFNqh1S zV3S~O;c~}G`{&aN+Ye$7utlHg+ZFn0h(GGdjhy^)0bi)}@wDAnrAlTiHGkhfxs3${ z2j*2uU4Av9V+WQ8uQfvs8@Pat*kxt?E)#boBzf(F{;n`7w*v+2FpIYyD+9%M*u5zw zXskXKa$kcAL+?gCG{b~e19d|5qQdfORbFujU)Q6Bk3>Pu?R7FPVEjA@=6;uhUQO+V zgfS`YQ7k=INtHGtp_6AT%=GY5kEb$*NZPGQ3zdd2GFN5<6Ly{kZx9*Z;X5JkgA8@m zJ8s^f{OFQZQS^Y5eF3-xwBvBfzahUbR)dE2ot#g#SKxWKgrVUGBTr|xx+;V1pU~T* zqWOb4we;hlBkC|Zns68TAfZN*wHZHOdP&j8xTGND$^?XS#~9v%MzCkm#sev<2haY5 z>zftLueVaJ8EXcGE-?|_5@>ot54`tvJ`tinDB2HE8D6A_NWH0l9BAKa_ER0DD?*HP zbof}s1PlFbCfsy1pPJITy@REy1LN&vlrfg4WY!K@hLwe*zu- z$3-LnB&-Gnt3hjma`=xYeB&*z-Py#wvS#}NhiHqtA0zj9_J~<2vT(yq#1F}~hGx9@ zWqv&rDQgS8H%V0Bi)ZUKr~63uphNd)Jz7%lnbtdnYVdd^u`5uU)|VAzm)-bcfG9%4 zEId$+m+sk*HTP$uH_YG(-5VF$M{-*+Tq=w}&oj)ncn&royQ*wdd0$8zdB#k8)J35; z^G&=Ma&690M9k+a#y#50_rvY_8~W!r23<2!Rehh;5h-%^oZ#X)R3dulK!E07LI{VL zWf4S?iUn;LIyAqIl=4|c&y%S=X!1vTtT3M27wRIa_s>0l%Dke@+U-PqbAVEF0>7uO zFAZ`+nzZbIG|gHSK}~Ds3T8&oDj2;rP#;uazCiFCK66k=b*e3XsnX!;{Ew4gyK7dv ztvNg-B#dhYLpu7){Wykf-TEAeNpi(}adjW@@LG>uGyRGJ0fGS*kf=Z3UjqNAb(z5c z8@$LzUa1bW)c7e~)xe@ux~BJ$Z+ez;a)YuG2)zvBHh+IALYu4;*D6e2yh2Zl1crTL z8hA?tIuprf?ieC}73W*Eu31G{7*4YQ(XtLi3{y$~d`UJ+K?c#0jTxBt@ujM-h3a#5U?w3lMC{vD3OvnNTI^0K`1myKJ#1U&+SVYBA?s+ITthgfLo#ls}%619VS z_lC9rUUw?xBss=-Qh<$98YTUxw9g~on$vS81wZ1$v%VDU$yBb9XT1SquO0UeQfjaKzau zb3EFyX?8a%Ef|}YE_=T^Qzct1Q)RrMmP858(yj~->A5pV&&AB+hX} zct`rZOl^cV4RmppfjZ7a7BY}?e zSXa2~ynVp?V8NZ1Ej$}PQK8W*0-<>x5h!F2fzev8Vn96?1e21ye3|*a)`I?&eq_*{ z>)49(XIV5%9=a0FMqDxJ>Jfnqi*5!ffp}$ zA7CGe0LWPo42X0&yNuIVSPxNTube&n=Tw>!aaMp$@sSB;&TBG3@`bO~Y*s~nMnPn% z8-XFD9?wXr8S`jE{BmZOfY6y5FPacJQ5@^4n@kxwNl;-JrAZMP&bnAoKu6GM^0D^u zi1g#tC$lya?O{DboX$|+r>k={jO#PTwQfr2V;1ePZ~j(@P{OF4>s0mhg}MTa6-<^-zY+Kda%=%T7088%iM_8q>Y<%kahc^o|-o<%%m)vT*IIG8{0 zytBWjG+B?q=msMX!NG`!@k!7}+R{ql*Q{*yQg0=I5@Nk)fjpki+AMwU7XAH}lx&iD zCcxQWos-?eqMYo^4Cyi!tus*;y_bW39}Bp18Q1L5U`c%KTR|FpZy%D{rl!+nZ1SZ@ zHt))+g+g`T+Zo#q|A!HV$%7wtzRhS8q%_thjs?fj(Ghp-xDf(iO@}}A)}?vW7MH0m zAYq~L#Qcq zBhFAQ8_?E0=XAwOKpZ_?PI`&v!{AMVa~kYF7#`J>x2Rf^{t8#R2IPMZM%Ij`^PnQ} zz(Y9zD_LF$z6;KbLok+e_qv>1TEvy3q6~<|ApVU^2&TDO*Vk7MP8kHj zyW61?=(;Qnv)9f)pugh3dDAepo~Cvi>qpR8ME~N<;qxBy;sB5TAVJ$C>^b!mqOV5^ zQ_v_IKZ;HG=t{cz?$@)`q11nQv-rrhm-83*)688S$HyI-@^zJ29}MFVIx~jQByrDa zocD!G!M*gO@0V(ZD(bC%6M^yvN%-3kVi6B$Volcraod;IHHm)u*qX0BbrM#j ztbpZrNwIFbrW*WBZ{Gx+Z-k(A7nkFm0jIFxtuWu_jUmGyO~Q|Fj^_7vm-NB=wwmYf zWol0B*93XJacLjYzN~v$lcP)%K1r4bJ*vKg4j=lVs)D`*rsuJ%gK6#VqDC=iP=6*P zr{vh*IP70Kiep;(bVVQI+Vi&||JWDT9FE@&?mf(e>-8tpm{BzHUpm6z-U<7MghkVADD>raXhGAe^!ly-3+4ngH zZSc_)rEA(OeGGwG3hLf4tCjo>mO#kw?d?%QuFn!`8`>R@BsxU-Sc%Xd2mfF z7G^EErjfAM`<`L3lR1*OGZ!S)L7)1i;)9HLCr{*mbriT5Yd0REeMYZO5hus>L&Nbu@Axd;H+R}d@x zVvnzm78=np*%IW(d&@~2avFgS}ldDHs+ZH@yR@MNX($+xGyKZS&pF;#vR*lIR7su+Nxa01ZCL6ajxLCruLF(#~^jv8{eXzHJtZOln z1ou^Mq6a3=O=0iG^`I{?Xmpg5mWoAuh-3VU?H}0a#UJXNCF;hP#2s#dwvam=qllcKUURfVDNoiV*>5z#ArO}=uFIXsEsDXJWy3|PzDB%;a6QYEvd)F$)K~?R^{qgJ2dXw zvIe7~r#6!2G1q+P|k_sWqrtKtglEi<<#+QU2Uu0jo6hFrWT5EzT$7qh}a{0n+Xpk3llnQ5r4V) zd<@!QE{(48JUgK=t5 zmi!?tz;%f&$XYb)!ueP@0{>{YBV~#0bG)s54yD1kgUpY7e)cyROgo*s?8nUUw@)7p zb5jX~W@(rdp34&$&0s1J^p}bKup&S7#uF)me`xTnRNajBYzxDc0TW4-h(ATXL6&0J zOJt;tc?sS(+;Ad878f)41O@4KIp6yl_8KCyw$whR_)F;2i+xUYkEtnyvUC)ZE=u_> zu_8z?mYYwAUJ5zU@D99^je^%fa@a9HJ!bV5(VQJ7Z(0}SI-EZ0gko7DBEB!wJ1a4m zbx?=?qGZ-K`S~)jQqyqddyOtn|-7F79N{doM zh+N&ThL3gy3NC-B1Ml;?xE6a@XRomB;LBGt_y?b%@ViZY)^Bx8K)(|- z*n`kh>2c?XoNZ=2pC$S_iIE#f3?QB5IWD$4h}r#-<@4S_&V$RkQ0sF{E_nd-Lk#|| zfs*EFNXfIA5z4N8&pOiur7WhcL%bo1M#GCx#U<}7g+RvDeRk}OUPrE8W;rm8gPK{P zlH(39o`~qJ!1zBV`C)Uui=V&xuST9UF|g$xBrxofu;C0HN{r}YC5q@x1meZfzmv$s zT&78!i;+Hf3Tq6Q#4s{5)4!+x)%*Rq!h>jASV%y9{l(`P28z}awLdP4MxQR}|Mjo; zZt)(bi`7hsVKZ>GiJ9AfD_^AbW>A$aX{QX6DSxikf9ub& z%Of^V{&7AH0MMP@byEb4@k!0W#$a{h1z)FFCGf5`7X8sFqz!DcheW^!#bVsA(hzw+ z@|kObu9$F_IF6@lzWkAPe!i;yR@X@bx_OUD_PV6F?yg(r zhK-Hmu^AO-5rXv|U-R5FT(Gwo(A(e7lBWOf;Wn=EP;paxwK9paARs6?Pn^OW`DvOa zrPz`FZ}%3f!pu z#TS=w)T{lza)uBSA;(c~z=Lp^JL67J))U}dMvbp|v!!mGnp;RIV_ z6y+qXB*(~-+D6+3qvmq&<$>@e4V~wigVM!do5M%mTY(NNQxl_}wDDyFC2=#4!nf2Es`vSb9#@JGq(#WXgM!3E|F#I|%*j}4}?J*#ghmcbxz4AQ|InmPxS(OV})0W@IUcQfY zN~9At%`{6FlWiKR6ccUa%j#4-ZR|Kcx58l%lg?U{+x^|W$74zxac0YxzW$xmOXfWK zNvek6+1&4U5ADwEZ8p9me`s1~Q3)aBUiWLx?X`Y(Jim=d?ern^Oci=P_}yU8o&ajI z7@bAEN_8c?-*$kGxu>B1iuvq6>S)%=1L@^Y@aj}oS&hw~NMa?P3d+9ezA=N~t! zD7f(-EjIW+use*jQP37uZ*&irnQ!?%nqEllXkW}WgohWUGN+ZvAdQQ0@bqT<2qs`? zrwC?gdXnr7C;bq0wZ3kzB>`n)B8-Lxk4BGqGtW+BRDnr0g}(&?r_zC-`-tg*(uB1vW0_`%;*dNeT3RE*K2dmeA`H4$Q^Yia|ej+9Mu z6}2A#hcXw%HmZW(Yh`#=-DKWZ!0wcJ%X1Fd%!d)6-gzm*^B0*%x2|(nuy>~+{5ane zOYWIesYIQ-(QTs7qW}oGKfle?&eMjLC3Wl+@n)k*X-@MAGAV6E11nP%>f2pbDhXsP zb9Xg!Algig$)IsM>P7FbO6j3HOjbUx!tQ2y8i}LI`~edZeR_`C-r*jj4q)J&sqfxF z$}jxqzkfO!^qUI~NxUxK{BSvD!OZdQ)PtGq^5?rxBA}0;@B}kZ$Z5N`r4^sUr{|Ip z6As{=bc1$2_F#N_tB6Y$U`9s0tHb*;+}^PhT3I~bqq2fNFZ*S!t>Svo=*r(=-(?E{rcq%{MSeSZMZ}6qNL^#b+Q^jSkDT8* z94vp*nXyM2-3kN?6N6T|wC}I$$)Xb;9&mnTXH z?(j9?r=pZS2ZPbMXq|(ra_bEaNxTtIrkm4TGmvivT-Q)$=Dd%0S-kHslT(@|| zw17+cvlOo<(!F^%f~Ah(~m8)-4W6i$C0u+l8c&4=d0r51@hb&Ro zJ2hD2Z;IcG09-j7CY&slL>H#h1O-Pxzu#_M>-G)46K$aq8}=L=b-{J8ou zvcUL4R`~p{G$UW`i=8hK=P#NYzqr!`>`^KF5UhG9v$=H*o|~IX z0V7uSkwaydnZc&1x@b&diwfeqRbEjI`&z25IjyZy7v%|hBs6pRq07&RJ*K6;rAx9F zS=&}V1M~XZ@xp_>sJyyE!j$KmhK| zFZMi3sW=C<|K588itX*(27c&UyMI>HOA*txMD@2ZOiIO6$EXnG;44tQqIIz7iF{yy zGTY#86A<47NM=d+VQSHXMrs9iSQl@A&#Y+XQTrG9Q#%{=f${@? zHv$VQ)eZ(M-?oTWL|oskiZ81q?4pe8=M48gtPP=G>0NpM7Jo@me%Sa{rgGA`rTQx;LoK#2hPELvj^|#2l!@t( zvyw24Sx4z$Bm=;B$dtkLp-sLgK|8<>5KZBAWFA%9*de=n+T;A{nidy_glYp6&-3kq z-u;OF5EL5hdjGNyWr8dUl>SVh&NCFZH+T8xp)j8ZUVlsdXTR&!&^`u5s|cpq_Av!0 z<4A=)xLNe5R4EM9YUu$rU$pnE``!!M#f`rij1wd6x)|U^O7YAb=hFcKFXZS0@7Ds( zI(3Kzjm3NWb=|=E1fT&z2MR;EN4tA;-N1UE3| zWREW_Z)(W?2sLwmcTi%U@<&KPPsF1Jigs_1xila>gyy2f=oP2^O3>PO*thFM+3u4S z%qQgE1YsHq6rt0E3}F!nAOXM@Zb-U52d^M1RMl%H(vL9Is zXRQS1=)Cg^Ejoaj)Ae`82svh0Y z=b(Elt>@(z`1f(p1JG>E_f`k?SCz+qpu#?c?{%mYKaO@p|=?o9v)__a9OgkIE(1SRXeG*T@b8zkHdSzGHs2ji50`Tn35yO4tN+f?{b7gPkBdl zfJz?^amfx#>Fts%7)Wu(NM5^L_uUQDTQ#N$;7Ej0OluFFzi|m0yestG!>9#Y);1<8 z-QHSANC5-)*P~Mawoms)rhj75KQAH*k{0YB<5J`!se36wfd72}ti>|BQ0pc@nUz{oHmNS{`=X5KajOXP~-yBf}fuJ9U@24HJ6 zE`CCQO;fI_Y7w!QkHNw5TmlLYl0Ysni-4e&4(+;BPrt&|GxB>&Brju;wuM^%qL$b+ zbO*P!dR1020bX=M&Z`kU{d!~#iS6S@cELp}9zyX14kSFmx`aHiyWY|Wh|M*nNmHYy z0!+Eifb_;$*sP1{`8nF8ZAcPw30uD}D#;P^enoZYrDKntQ6QfOZAn@R?h?hF|4$2$ zbv{!X&m4&PK8*B6poyU%ZmGt|EYH*<)+&shsWFUR`z-0;3Qt?X)ynOCprV9?9(}|c z!>@nvbiQkAG$!FDx$C{kXkvDfVio3EFU*8?599tQLW!$pejFOqDfou-+p{yG+2lKIf(R>2JN&zIa{T6&W zH@Yi>LpAr{oA7sh`u8dsKOV_gBt`_4ee0u3D|R4O&bdt_53Bz8^y=y}k6X!gS^>l% zb6Sqz4(sde+jGmVqXVrl-Ujw96R_1@gzHN{YgGMN^Y1uy$%ciTG;Tv^jD#%|I z{!{KcsbcE^=~@167dQ=OmqdN0V-w#5ZW z!V`L{@A*37crxkZ%VjpiC!?|=t*7Xo>4^ZBlygRYrStF!x1bTk-2#^fg> zYEYd22gwM)@$zn2kA5tu%jcoO!P_+Q$B*y3h`r}typa%{)zP5}%XPuR0-!Z?kR z+_~499~LdW%G|!Yfh})I3d&-hY;0^1{L|7PY+=dUCxelXRaRH$Aa}kCclluZtlu2x z=b*iZwg(d;sZa$}TC`%m2Vg~v$W$4A5Ju9|?F8&grEg3Y*Ed{Jw^KnPUQiRcbk=Z# z_xEAw@8VgkE^YzGlI=0o`7+6Zhv$326F^bhlT;>gwc(|sBeTpN(aDLq#G9hMibq>A z-b~(d!AQ6BuS&eOR*!hH3^+nw{wjYaJq(xPVSmGr_om__Ys#mPTIToc{?=4PH8b;t zBYcRmG6$k3L9~*v{w1K|_CjUk5e*jbs@Tgtm#E^+fwQEvgvFDYBGz{Wmz{49EPp z`s~t?kd43LjE^6jdG0#DgggusG3h47r<4Kn*aQ`@b`X68O2fbwazhC9tHe{eBVMCl zB1i9(MF=Fiux@o4L#dh-f}1V3KQ486Eng^PK z-Z38lHdM;D&j>+Z)Aq7;78n^$R=$hyX#0&bdCwkha&tbN0K@+ZeEnbROLv-pC{(?( zjzSe3h7_Z`6ubVRjQ07$U!h7G%l(3k%QIC|{QlHS(7a?#VWWHfJSi$|3ph=$I$H1z;Bw(lVnni^j`0>s*ea15AI}- zsBH1ON7|ll(aq@dlnR=&52eTw4@vY@0SX)DE4tZBSebvB zvBnvJ+EkM;pZ}46rsN%O^Ztjl5*;OFn(JekHCkZc=`Udeco!eMYg>M&EEEi2;HoiC zkc8jHfV9&kb*RNePkp*5)0cVWT`FngA2!}Gb!!gou(hm6vIn4Z>6bTAITK5{+UmOul-tD70#VI zcj}f~KK*uFBG)C80XTmAc=)y#y$DxcdF4NgPsC^}mB}9s;>b-bw#%pMg@AQa)A-60 zPvDW0PXj3D2q2Z^k!2i6mk6Ciy()r~_&M<68_GEGuJE0&TvRkBJv^)y0g@=}2#GRd z{*`$)NZ!bw^v?>;w-p~w$%b1gop7b+lvnlQOBF z>-O8*N&${~C#I%x#lfpE!#gs#y7)#Z?NId(kLK7@NL8;RID7h=&Bo-#maMlHJkuAp zqT~Ybi8ZzV1sr+o5nwemOg=cWuIb0i(~pB}>R0HMFJFkxQQBJd52C;r3rnf%Ve>Yz z2v8bOfZ4f~aI(HclZt~>LbqY0p?iV6_s@KPl?LUO6UQhYE>_5smG~AcVF;Icu zci(;2zxK7S{ViW87vFWsWB>r*ci;B551cr8^0747iX(I?3%mGPUVJA$=F_FUrCr(+ z`pVt=(g-k^3<2qVp3bPTj5cMYQlhquXBUZ~Eb`<1$y_c5OQaU8AT52-qwa3V_u8@c zefR;x&k0ZwNYHRM!W?mqfZdaf;PYqOC!(6Ar4=ITQiR=(Fgq)M8)0TW@~~dYKj|I* zK6G9Xq%t@GX2DTc;Hf*y@Sr20v@WSWM*sy50bgEOk6nBAQ`hVH-*adAW&1JkN>G)|Wk+$^M!;xA-mo zJG*ey@ntZUPz38eXMjk-0hInRo%l-D*G2!C;X%-+P{9N1S1#fM@WKt7X?b!gmSz7A zr1}@`OS~u@zIcKgI2%a$KvvekMEr6paUYmtk2hQ zKB{u9vaIveKUU9C^9;yDKwCZ=H*dkF&09zCvIeX5z|Hm(_>BMMb?bBgpOG9q#ojpb zC5tpBO444g<8dh*F9zxs)ff8xdw9A8w|B{K!U*UZ$^z1M!v_xxP1>kaZg z|J+-abWibHeo^u9!Ld{q2(yC$KKIzeF@0C1!{qUjOoMVA#Xxn6Po|J;+DY-m`()KQ z29j26*MoQ6S&jnE?~Ku@w^CTPNfeU!m17%OxRp?yHq78P@TcBS@}+3-Q;Kh4X%U;Z zZZ9t!mao8<`|wG^8*C82Y3!j=sm_d{z9*0Rs8~~tOk(&2%y|%@l%dR$G%hlO9K!tK z0!}=00;f)%Fco`sk|T;yL)qXLp$=QN@4&Y0yG=qTNKEwsErNivc~w(FA;F3+Yj&mm z*---1=tMC)SPf-B)_}+48v#_#5vc~N0Xf)x;_*ie4a?G6d{*3!^a=NeGk~@Io8`r} z@9X5BtEV0|ZQF*4$tf|Qlf0CvoU+Zor5_`UVfq#RU!CK^Z6(4#*suvNUcb4Jm-d*a zf(1UoOb3-Cf&cVR|MdU<<3Il6&$Wek@m-fx1^@tl=Phr!^R7GZ{8LWh#WOuhTBxLp zIE4$HV(fUIcECT}u!VNc&R54na*C)UPSwk_lR1_$(ahb27g>X0ZU8kD1R8FGYj+L zsGRgZeD8bm@dq#LiKe1c)C><&X9Az6ld{dkHk5nF@119o`91(@iYnWJm@cvlm(YO| znmL8q?+>tN{}nVlH(%1pIR<)??Jan8Fa}ge)$v(8T1l@g-}&<|tq}I`pP+ z^1b5|5-&JDEAc#^?;QRr`oZDf;jhJi?kjRn9Q#7k*GBZ1_ILFMxcXuJ2dO`aAE5Db zIzPcXLci+f={KZDCY>Ich2e4rysxJh8un#m)Ld54FCY(!+-SQ z-wY{qpizZ~Tob(>erWgp{M!w5E|7LL0KZT=ihzmcz99^ZNwXF0fBg)Mef4@B7Si|R zneI1&I_W%*fn6w3D$#>O?ZWBmp8>$s)HL=Vyb6<3)5CE`s(L(In4O!)p1oI~-yeWR z*=Lg^$JL}CkuFOu0=Y9^2)@RY^UISK~9k#d_l1U;gD6-tqa*fBs05 z-HY_PC83Dst0ww%%;sO zxb2!vyzJwR0z>2r01J!D*s^t7c0yS|XDCDIY?C4_oPrtqj#{OTkd7ziwvdYxa-H!v zdGJ=9Tn|#m#x_eCRNzULDK5gq!~~`$r|{&HM-a}R!|<1-kgsBhG~7|sU~&=%4qlC! zx%rsT$!YyEC{w#=2(>IN-P}&nzmZ<#)@BqO8lHn!WC3Zv<)TS|K@2g-o(rPkqAMsr zQY!iUxpR2p$m6lCO0VXneC}1W3q$(oa zRcQw?gQg(h?AI-LWi=6$zh6p9xa#WBnnlp8TEPdCSk; zefM47ne7+#bxCFb4j(>z{$n5e*c(z-DW~-VAElde_na2WDIif6i1$4ryn63JmDZE# zctEWD2dZ>%01A#;#cxuG;tN-5#j{BJ^Ovn;z6<|C+j~zSr5OuvZ9QEqyH`*$C z+9rB~0ru`cMDuCIe<#{O$rf3b!GmX63%RCtR8w%=>L9+L=)s%#QC0xTv>fB)`sGpK zL~-juCR^~8&a^IAr;AG~G#E@I`)^EvWnGM6|G~hkDZDURziQgfz{#nfX||3U4sdqy zSpK|SK1rrQ$e#)WN~UTG)cTz4UsJy<{3|GUvODRYRL*gD{>lbyo1ItvM+kbwe>xL5 z-4Uk!RwOD006)}_uTWfSHJqzKQcWty#YWO-G#DvuPg?W z&yWU|ai~ZE*fKkhTOWN8Pn}I)ZWQo*5)>iXbzFqjw=S|$=C z%aDxpm@T#f#b3zPI*nD&Y&1zKk`i$DB*xTq#Vb|7_>_<%QZ6DQ92?X!*6i#o&YU@e zXP$XlM{Ck1ZL_=|M8Ilck9s}q+OrQEHg6SQiWPPUahug=;6l!n^&2W4TINd$S(~D? z70PYaRT?>ts}BFAQe;i1nR){|DaxuWrBn_J>+hxR|d?G6MhrU;Ue}p1JXc8{d#e zfO(N$iJ&?v=K=6ot}_t?K$z7@lHGMef1sJ8GT=%*2E&N`$5 z!P%#(M#xCJnmh7G@BwRIgqH;1E)ry~Bm>Vh8xaTz{9y2#8H6z+kf7nSbMrJgJ)Lz? zEBD#+B3}b{ z{X4H#UamUBs{jPU^0hj1?$qoPj z;Eiv3;|GpCb^KobEw>yY*(1DX@_Bmn?VGk>_xw@?*XwrL_|*?*u1rr?@ej#gO{@8R z0rDRYtyBZnMn|AD5l8K8qB`zhYI%T>3 zIC>afzl3sssUDibWDAUEU;T**>^*P@v$J!CzzF?#wm^iH^&7Em=WcYWyBJ|VBkVh* zwpDZP!+7z^#uuH%9>c%5T>NeUXI!c(6}oa@MU#lIu(*uDV4|wlWnf%i8CA@)xwYUc zTHX)eKh{QzABk~?Q_duU+AKNcTJ;C%mm$t-eebs2T5KLIGJHC40IN6MbklD%IJhLP zOLhtX03LYYfwM3Awr>kp96I>@2&I3BS;0_JK%pdc_z2%P;4cdYI}lbTr}5cG9zfY& zQ1+9nZ@d>U;v-R4zOP^4a)46pGnWek#e$P~6z~(i0A!sW3=O!fccg<4O93W8#&Ze` zTP)bB)YR4M53pnRUQBT3n$mSeWd6&R;W_Q^?Bfnf4rVan?$ehA5c!Z5$^rq%N0Day zAm{^n3d6jFEL;*If~)^jN54P7%-kG~KJf(3ub!{Bp6Z_zwi};k$3iq0tZAtV%in#(ZB$@UJu8PK7msw zo)Hamt_waVL*6(qr-m&%6$~k6eaXJ8Lz}#??~KxQ*tB&!CZ{Ijf;v@i?m+9hvo1(H zwtbOiM15>1@hbI`e-t(z9yx}*0_2kKu zqdK{Z_PV4q008i&H^2FvPal8guuQ>soKDY+IZ_Hfln(>N{hKlE!)c>`uzn-1T3(;i zMKZ-H7Y^psRTtkv`C~x<@==qCmpA|wI3izFQl&&>7MYapF*2aiaxz_)m};j09k@*A z1qa8bAe$*blcJQg3a>Se;?IH0)FudX^Yb(_GfQyN@v)jNrvP<2ksl)CFg!09f}kn! z1L}SHB-4rUIf7UuCg3623efo2Q--IsI1!->u>1Tayi6A~rZ56TIulq}SfV{w97M0z zZ_8ilOz4^jlT%Z)_rM`cOid9VP3Rt8c=k>e#?HqMq_n>;KNy$@@xi@i?E4ceR`(f$Fb^#B`B!*Aei~ z_g%?}DyO!3pcfP+XCOH0_*sYNR_&wr5sw&%dSG%1ji%|+YW`>wXtB)bL|9o_pVI%5 z=BPn8tXNygP5CN$o>!<`*Up`fS5zk^bHF-X)?-}yt01%-cibmCbQDCS;yCH{iMrL0G5_l zFc=I(IhVZ#Ah@@qX$>jx^-dwmf=(u6Tsm(XHf-Kf22ZKYg_WRi+EW0jh=r@PE3*{DEgL&&I0v1M%>5l963!`Ta*hf| zmRZz~^mPF^IuhN$L!1J+^l+&q+J(I#UtC$l3ogx(J+92R0%Tjqe zsu-2*w$@3_R!{S+YA5MW>HN6u$@|Xil!n@AgZPihzxxyy981TtV}}M$K7Q- zaeT9#0?2R(ov?x_`L$SOE~k^k)R49x3Ag%BI_Y|}vT;M1`cgiH_C9*nnv+7;Q*3s+ z&-$N$Tc48nE&mf4On@XpbB=?te|L&i*jEc4B!1?g=#5qWz1#NCbg!Sw z^6s9ru$YZ9-3Y%F`sXQz`|rR1)9?Sl`)|Nya9tJ|004OO(MP}hsvmmQYX%c33qU+5 zmEVKr(HwsmD2G~*8G`x2B%VHV7Jqx}NRITOqODSa-uoZ%RfHy1BXcxW50*8K9JwF< zyT5hL0bn!{RT^?7JxMjJF4(wi1SMhI)&@Qe;V<)HiJH0y*s^sy7MGT_d_+4kwjkAx znpytxOojwhO)YvO)+y^=l+o<#wlqIL!Yw7mQYvDinqirU^jj@bj4z!e1FkaSbiE#C zXBTkn=n-t9t0K-XaBSKOp_?Z;rgl}bFp;Q& z8waD5S<{Qqf|6726Ed6yQ}nKV9U6{eSN&cek3IS@R?nZ;j_wB6wS89bM$qkaB*W_x z;@5UN49~WiX-QrBEW~1j&D(ciVsbKxj2)LVIkip8+gjxoUg*Wwe~rIwb{0Rp>x#5H zy4WiB=Q10oZ8fO=KM7#a~Ih*2#{gb<^Bxk#lGqg^|~Em5nCPyrCS zPK>I>XdXcG106YX@0h*fL-UO}|(PjArnRSsL zTZ!CSn=Z15WtnDYWySSJ!Ba~XS!W`v7I`gj5QtZ9SzC}kUBkuRa2Q((3`dC&6MSlB zhGysHl6+~1{R?<2+gAB3m1w>db(8RA4=ytU-q{ap;*UgzB6z%HwMAK#Y@1BVSa?8M z-wI1?z!o0Z++_}Kljhq zWitQ(fZzV@-+t$lPd<4U05NKoBTOo{-l$p>of{1BV|x!~TefibgVk$-I}MP*0>i`V zaHJul?`Ay;vBBS(XgLIio%A;jtpH5V%wlF*eBfpzj)?4Oc;T}3XB1IEZ$#17krRI5 zM7m|4vK-&qsCG!%9NC*i3w9zA>am^!~DzC9h|bw zbz5={*b^pkHGchN8;!uF5%xJuq*nc||E6q9*I{Md#URsZXiz|jEkOrV8)8vqs)pt1mk&k@jS5BWkGpeV& zWUk9*3IG7^zyJO-&wJiA$FIKnnja*RA9djVDCBbh0{4*WNZ$C2@pjHF;PxjT!;w=b zblNY#T_jXKHdKn1w3|#ZiriL}VGEfQ5ODbJyT!+VAp9ZsaF&2A*huS94I=}0I`N8s zbx}#vd2q$MeLI-?yAE5nY{x=!UYl;s&(eu9#QZz(pnfM#r7mRkUMHLaE>0zDzD=*i z?N{Ilbs~ z?oLrgUFdNA_Iv48zjk4!YG_u2P4SCPJ+ODy_&nXC;6aEb!)x=lHdXC2&o7Av+~SKt zSFhK@<%ENfP(qJ51#|Ti;M`!X-6Wl&odAvdOi9d4qSyE z^{Nzq>d*)HoRshYGX;RUeRg4{Y!}nCDgL~O5Mqi>;&c16&zS{( zq1Ye$q+0NoKcmcY>fg1Ajx+H3DBnBUh>yvHvss`SHF7Z!ymrx-vyl;OqU6F%?MlX z>5tcyODp)UE!!hp4c0yKVhEjYO`YZ`HQ@d=V&Hi}-y0thVbIuwd;-np4W~OdKaZ*D znL5IDesJWcPFjuaOFG~XXE%c-_1yhI4j+DJT|Xpj-M$m^^9v{`VDWAd$pgi=r;AK>$QRxO zj?$+fS?^=#e;oY*Q2Ahjl3&5H_{^sewYTz|NFPOO;j($20NKI`=uwB9>nx2$Ip+cy zpN#mldZqo#wo+48>dNOf%Yjc2_`WPGyrh;zJFTrLS(#~*tSibi6@H$25VQ;m-wY-u z@Yq8SVfFl3)RzcAj44XG{-DfZpd#Qg=z!fmL6mjM_r*+IP8S7Brtm=VEpYbq*<-);+rRyjU;EnE8r!ih z-RrW;006)}_uhN%J@5IwUk{-=T7R zY)(;$DFn3&+4))+ak^HnqAsaoPGNQVo&fGja*B<_J%)S@P^JJ4eznS1z`b;2Vo>ev zS9ojTPr^f8e}Z?t9?i`!#1x-Q%cXtx5b3@E4v0VPCScrX0$I~|2YLp&Mtyax0ihd}1Se_cIY>sQ_5?+VTeg1QA zy#D&@@4;nwU6vUD0J!CrTMobKhkj`P#!Z{PB@Q6PseeEoc*pUi8G*|40n&hT*Zcy$ za`XruJCVNko#D<1JKfuJ0Q48qNJko&v4x6^^z~sb)!PoZNKIb8p31n?46m-$QPxt%y znFCj2rau57F2TrIBBdUk=>_CFfe^EKgr|-jd*JoI@f)vs=%I&BS|Rfw!Xh_+oM*2#yRwgFmPK)V>XyHd8O1+69zF zU^2qXi`0ju50yd2cX@x;&i|QTnhCRS|ZV3K#l9|Di-E zB|_5pnH5NQwAsM-xj}4S!lo@;$}C8Dte53mwKbiji5A@2zsz7H`A7fjM6mBfgEH$T zWx>3%A91M1*$T!Fs7}{ZvOt1E&@?A?vQCRE{ZwC{M5p!RQJ-`qbC;d{32 zruEY^seY8KU8m24;r_q)q{#ZFj-~@}{S7z#$``-zh08AW&(~!=0C>mS-+s?6x7_lZ z@B#rIY__)Hm;eg^cF!%~mAm$7Jc%PO2Y;>b0O0(2@v*5z9AW~?be<_^(}m>0oa(9{ z4qY;ZEt^D`pP$F(E!)%A|B&TGs`D)T6O1ma_CcixRlP!Y7OsvMZQ+}=XP{69Iz%;G zw4JN~a#7K90%?d&nPvs&k)mq8e}pjeui?f%4LmlZ8ihFv>$J>8VzsuZ0EE=8EG7## zDPGlRF=$XPelbNsBy{HD$2!QqK!g2hbS6{RL~7>=99e|`oIZ65N1u3H*Foy0QNMu? zdjVFYtew~^xc;XYAA;)gUyUCE`hx+srH^@KWeT^bKBku8ly-6mxRI~yatB?9e{sdt zSR72I#D?yF3(Fzb%R8KzA-45I^8CBs{jS%1@{^yuyDf-I|60TF@zTBC{N^|P$j|)D z&wYR>DUAy7y3H%FhqYrpuJaCS9N2qBPxM_Q|)qfWzB z*+{<)4*?zMiNlA(-+bmXbo%ru5U(hU&Ehc&FK&p>I2D?^m(OtVW-IXM2?S&} z(KKUSj2}urF4v}WUCEsZ0jnVdP*`1!cL+r20%uUCrl!O8ox5pfb`}T-M3m)2ASGXZ zexdSRjqjicPx|-DdNf<`RfG_C8jqo0##e1x4Q!to4ix&UA#|qfF#Cy%;R;QbsVwW! z94X&1+UNO~Dx2Chvi*{#;1#?8!NJk(m;8ycsO7JG-IK56mH8i0|7r-~{JFDq-`DQK z(@z}@tAU+~vVRc2$C589|CVqu^vP~#{LlI~oM-HdgxEeYx>jYS|CW&;@!24ezg1yh z6h*+Y06-fyZ4TRa@1w5k*4F=J`)Ksf#y-Ah+b(?nwp}4j_Xzv*?aPA$A;Z6P+ik!1 zvp@4Qufb)0^}pE+{I|IN{lEWzKK$L^{oNnmvuEG;muw@y>&yt2e|jV9qD!=l_P}C(#D>tvJitqXkE1S0Bns0kCP8~g#sbm*{-Qq+!qn&G%ftmcV ziV+h)a+p4%ltRU3-9|sv#&e}xd zil^cvG*A5=U20U|*vIJhUqIPWeC{cCe5Jw2Qg+yx4mA!)Fb<%3(yZa z@Bm=j%p9)Wx>Ikq7-4_e?Q){A^rcTf{q&I!{=pyqpCdTDEUwFb3IG6(A3q+5=!@50 zd+pCsNBwL($skJ#ncrFg!L0HwE3;!vd~j(6fAQEuIClC>DOVjU%+HyVSgA}$$#|Nl z9h7%=gvrGPte!oKaP}NJP%`u$h&puCq3d<%x*n+O(4hl$U7E*=FH}Moak?HrsZ1}z zi3B|T1`-}oq-AIlbwJml>qyWSG{EpKeO)xd`FBS>bp0Nv*F%qgC;CMeT^d}7kpo}y zoCW?3n3rbAw@Royr7-S3b*Flp0u8C0PC<*{E2xt7ph|{rG6@4@n^bT$>MHH8cCi3a z7eUoty<8;CEH!(mlzR?>yccx{`H_@w(y5iNeFGKt*g;E01k!K_IP0_p7wySAI^-7` z89CKdHw4&V6#sTz@=q$hLB*%WhWALo1U&m3q3gxIf_mH!L^zaB zcA0KR6#JB3FY+n|=NaA!d=K91rM{!c#7_4VGcZCV;osFcD(NP@uqS#w{ELHEVcq1k z+L*&rNYQ=RE($GnAqrIO|L{uJ8V?*}Z%A zekY|jZ8`=3om?d>_VLD*qhghBK!*1S*gd~UpML1D-0~c6#C=V(tSByDFe~utC|Cqg z*Q3Gw9GyRP8mni|D0m=32ga%S2cT9_=LPXhKM0(P|4tuITa~q|zy*FPTf;^B1)rIJ zv7$P!b~kO}vSk%|&3_2|wJ>wMZ(^aezg>Ukqti5HSx_xgscYSShrXyk$|S8rX8IC8 z)NNJmtLk^`+i9O~f8IF^J7`?UR)efYZlQkI=+U)-_-u?u2|e3Pt{{gSlr zx_-}=w9Kf22iqpk{}@hE*1`GOqw$Z<_qi-fOW%}@mQ<=O_EmK4*@R?Q+g$CR(GI-+ z!|_kY78Fe?J3f?sQKSBj=w(}X;@dWCrmC+N_Q(85I2%Ls-vWQ~$dkAK^1u7#e|7BW zF|qpk^1Yrl0{{Sc;Qj~x^5rjo`KzX-y*7yHf9%z$OtfxC4oTLFg1m9VHJP< zerxS=$Dyx(&Xu*&bF=RlFfJ=lW zn95Bti>NwO@H)cM(WF|$Bj8-EUIau%UPOX*DL=H~q6#;{r_#PU!q@Fr5q=hxj1*F& zGBm=saJD(o7IhMwut^dI!el-B5%yKgG=R@gAW4H75x$_Q;Dk!%MU>;(SNex6ys(d4 zG#GecieTj3v_B}gd)@+)U1&j;b}Ct-FiQoVL3ZW5uvL}=NxRF9B`qfd=c>r4Ni@K* zInr<!V0t!5%wHxc`XZK*3r7uCZI*2?5x$WTdHCiWm7Q}7_=)`o(NX6vPAZtS z&+rnAAn1qHaOSOVd)rUma`Vk!!?WaiRt*5|z4zYJ+qP}L=S44i(N7S8;vye2f)%6P zuIs9(FOL=z-W7{WxZ{b(>B%#v-Dt+r)DcQOYPRqIUH`e{B)IXCGg0 zgs*jyW#Q1Y*^ah-afT%QpR6xA?W<^s^D^Wcn|f6VvS9pOu#Q1(UWL?)@0kIcXsn-p+mU&fc#9N`Q3HzR!8K1U}+L{q&Ue(uoo zX<;xqRR4a40$Em){+GV=rMLh3umAd+)Yj|E{JN~q0r0MW`)_~oV}JeEfBpL<>JmUT zx{rW*1g`NWsE1#;>IInY^{ZgQ(o_X#T)3<}&aKVw+>VLG`MN%jmdBGQO0rSp6jpK? zURLjj3e9qguDs-FUoskZP>37_HfR<;>aspfzkW>@wM?8n*?KB{NZHtDOUx!s`ItPT zG;9^{@b)Qa7dR9gLDS(`Fc^Sr1KO7y{W(pIJ1hD9KfJ7K|E(TwMEwd%G_>r{`UyD$ zaAE$j7N3*Vb4WbCP2{W*kxXQ`WnH@*VZYkf9Gr1!0(kk(eb_oXXG9|U?@-%Zu2RY+ zwdu(xj~;&8+uruSpT3Mf^*he>tjYlJ_4G4O-}=4Z^SwVg7z}20S}^_13-U?|F+a;5 z$7v=0Dfh!cDKDmQ_)-f4HO|Ds9L}FQgL9|EBE&fGEx!vH zs2o@(CDUL6r!W&&%G(T@{ZT{#^P~LE)GaagLCS+Jy_&zsgqViG&kwV#P&&XYlY%5u zg!Ub-t9N9Ym~d0jVv0b$pff%ri@uHkKON60)==?>WS6n_LX)J-OY&FIv<%9HgQR4x zMJtS^$XCeA+83W+Ak?Rc5@np0{pFW$HksN!d5TJr8cZ3hJ`oCtE0 zV_Bmo{7O1#h-U*_y}W`S+;t#v&;5_Ig^=Z+7XyM)ReEYNng@js{K5PG^@l$6p})Yh z?0QxX0RHN){_3eihYmgZyyrjvhp4d#E;x&H0z#Dz3y3MEJqt_pz|&9Rfs@aObeCD= zmv!p|wlRA)?L0h5ahbpG^dte!bT^@2U5DTdHkq=TFywP|4m2dcE4EGyxR zW9x6FIAb|#E+VR?0K$kAvXm8K>RHL8YG0kU^y@e9uINp}8&f=cMP==ir2p3GV9)1>i03WF^k3a3S=MW_ zl%MBwd>@bO9>YFG+Mm*Ni}DX=3P3O7?9$L-xczwb*HfsPSI=f@`PNea?i2wLRwk$D zUmkoOO~oAtx_@f*&qVQHK+~7RsU^Z!zVg@C{rc-(|0^d?o~VY}m;3dsn*!kLm;TpZ z`n|jEy7S|5q^eK-q8dp>7y4n32>N`_TmnG-!f@DD+P4TOenWZUz)a`TSAj>w|SAsg9*{fkGAJpSyN_!rl+G6MhrCr+FQCr_OG z?6ohu_NV&&{&YSs&)o&UX(Y~2nInMHv&nuRdlwe*=MNvoIh!g3%2C7O9yx^^D^uxI znMo7rtVk<9BRlI7p7AsZVic|wu52;NNCV}@oyl-5Ec%=kssy5 zMF=*ns*b8HNSUag!cV*sRT;{veStfGM*9U^@nPhbWhzuD%uzEdwlPBtpOWSU>COO@I$Qj)PN(L-b$Yg4M-%xc;>!f^L%lDVsi6=&PF zGO$yWj{hoK5DrjH2SLJ<4%C5Z7B2AF4k%jrwcB>%+c$1j8o?CPtp{%0kfq*w9k6qnrvVAE;=8>eTe zBci{4^2k^z#8_v6cQRdDtun4xEHlp&^Ye86)TuNbQ0ihjbqWtT1v~5<0L!170#z%2 zofN2>qKA`WEbR-ZcL~I@S{4dUVxNC13pA#9AXfo9^5yoE)E`ue)28TyYQMivKGKKQ zze~SP@dhbBE?eWq0>SyrC|Q12ozDRvsQ z%ciz9MU(1%{+`bjT2$3Ptbfq{A?#DG{CD(M?918b&Qj}qT}%6s^m%teF!~+kpZ+-y z0$--%ohgcL#1w_KMQ-2z#btW+-UH}&J*5k;PHta*=I}=#+nGDy_ zNN$k{lDtJijAHlsbBGxjmg@KRN9zwSLBlCU7p8r-{UR7BH+5BsS%c3qGtFZ-!GjB# zb{$w}Ir#fb_0{K4mAwm(p9Rj0E``R5UK zk%L;$jewIUP9Ax~8{Y8BJ8r+@vi;QWIM=g&0000dPMrA6wb#DvpHB?>v%^m6^RfZe zBA7TBr)!qh;nv3=rsL<%s!_EYA)8Tm4R<0;IQyK5;yD4QPSNU_Gh;>gv7Ne3HNr<7 zby@zr3`68qM+hYG1^w1Zf5XlpC=piT8GidLO&!4uS>#A5UV)W~6JZXCuxp-;{v4TT z`hJ8@>E5H6^+txjpk>){KRHysquQ6(a~b*9_Q^J(kH#WA@HqfA&zirQW^8VfeAPctUB7L!ydP1&*S{(Gw)hkBUDG)L zV!qw9X*UGI9=vo`h5*RysJpuFDs z&UfDX!4Ll7{~{OphybEI(}U{jnJm0GIfY-m=7sTm00*uS89W?bojN8N1%QBjPC$QQ zu1SZ>MsyJ|d1Y0=Gn_vKE=<=vo3i=Q8usCrljihigU6!TpsSk{kmpui2DsE(=#SsN z!uJKxY?LkQ9#Hh%l&?dmbu+5DQ0lQ|jsD0jEBiFc{D!KWUw`Ej`Lt`-3;y%T8JV03ZNKL_t)&@lEfkn0gjo&t(A8^{G#N>dP;G z#VZbO*s$Rm04nWOJ|@9lSyFgxfYl(OfSNc zDKt^P&cStKZiDHtz+EtTY&{-5@-onuS zCh&=<_D4fsaAba@1&>*cJea8j;M;_PdkbgFkJ$WBguwz4_T%Eq#gVLg;h!Y@)0F{f zd{G&)g19Q#erZd>UbPIJ_Vbk$PI&(E2K>;T{qeOfnv@p-3j@jf{Ie{$UoCxBAmI4% zrw+gF*M9v~ci(;Y(|GQBZUDg7nKNhb&_fU1{C(f|egFOR^z=$VNuhfC^aJXnJma%c zw(C0DJ->+KXV1{x$B#u?ou&oAq^+rfmqjlAK7W57F=29HfzF>giPf{`D*6HAEQ0e= z$W98|Jx3w&3K~v7_)`d)zM;t}!qkhjKZdi$qk1WyL@e4r#uR|y*)Ea_fy^RFr+sTl zp&I#mGPClnQv8kf)r;(?Ek9Sj+J5VO+IkThmupQ?)l(RvY|>RAGp#VnzwADLTfaFo zp|&X@i*AJeI&BlO{nCEyw4T*pCwWFRP%axJe#3r}9^AI5K1tM{+b`>PY@rEf`J!GJ zspTX1CnT%rXzTPGe)iBcG|}}0Pj z0jTSvAN}a#|LH%y{pZi0KYy+amhI>?-SUD#`Am#tc|lqS;HM8q-JKl0in$~I`G2R_XvUBPpx zAH`myjk6~DIo&$EeHX-YlddOUL*KT~-Fs~+bIPhy<=kt8{f1;0GSbwQ)eo&tP)*a8 zZN~FKm#cZ@a=!ih`}c?IghuFk)Hx3ExAvt`G$D@n#TsmlTJ8*jY#JHO*Q zCU))F^PMuSR;BpU)Bwp8T96qoPCEtksly9aHsHS;c?8d#J)=@=BvNQ7`PfWIucC(P z7)qv#jh(>MayQWtCg$gH{?sX)KXazQvo{9gi+V*d36@Hcpawqv6hP;9qNz$6W0iSO z^(Q!YWK_$i`dVKlKM5~4dGR(``Ewo2v!p>4Jk!j=K1cLS|m07$z4{C%{%{}chBj%a2uphJsG z`13~|r1Pm?>Z%kVL<%eTDOQmV?K+bJl=u)Sx_*xa^K*3W#7Ts6=S_;Sv5%eMs!r4? zK)_AmQYs5r`lT&hgQkM3_NUCEnED84Od$j$`EmVSMW6Ti=d^zS?22L-p1oDxmHhc# zL7JwrCD}eVg)65BTJ6)%+a(By>|s22 zsq47`fO9?c&_m~rA3uK6wbx$zll}f+rbH!icv;Q_h$Gm%NKa)0;>bCOsD6233R`C9 zaO zQa_Mz`G|_V1pg98p_oN}>Kp)z&+@eLGm9vT&S;3Nr2Nr@m$q=Un`Grg_^M$HPHec* zYW`7-xfZo(yQ;t}LX+_HC%uMr8%zYR)z^Rr0|(2cks+cY_g02T%7DQH__iQ^YyTVq z&%VjNZRMBX#MuS|SNOL5ydyCR<|P}q;uSmhLiU?M)+w@PAq{V&Nd%c8NM$?f!UsO^ zfq(Okcf8{hc$cl&JGyhnj<0>ox4z(qdtI-WNA+ew3!wMWcP5&)5%$j6 zdFplIq`)DIpkh?oj-W^H^G_r33@1#?&(pb+Cu#NU*&IQPJ_lgD2tpp4$GmbmD?dqp zbcP`^AK*oxn&(PBhUXH^Mrxi+tM<7W7AFF7+Q)V?_F%B=`9bUqtEgn*F3DKdC<@OJRSpK~7o6CtCO!P7a)Dy3uClTFY|02;bA0 zs=sAF9OF7RspRYUCv5%0`iDmS+P=ID$=VmO>h^OJBmSYK75a&N2lGjPoNe{%Ed7@5 z-%8nt4}AXfx4!Fjulo=G{`BcHc9{Cyh36Il4!!=(FZ{xXKKHrL{m1-3m{1-dw-)Z@ zs7li$BK+{~1Gsj}jv7Z^IqJSpxbuvFZqUd4t{v#l%qAKQ{)f>4L^z|{Tv1rVK6Sq= zhc#A^4FRu(KA}o945dFd&ub{_%9jKw$)M9#^~jX9#l5m4&N)co`grv_^X+L2Bl2Lj zELjwD1r8QRm$jJ$Ao4!cu1DKH<>y`p;PH%ajQuoiUX)cVQTh9gvfp9$1;gW7?TeyS zY@C|GYxZA-0rfQ8)JbBWKOB`P@*Qyh{WM+mUy~0P9gTE%3sTY{AZ#>%X04d4Q z(h}0$DJ3P{NOyyDcf%O*?)$^LKVYBF_T1;jx#yk}=Hzp10NE$a@jiN}yDME+gSV&w z_zR~^|6UILAD`Fnv#v3(Ki;On`)O1!T1R0*z=mJBB_$gdS#-^QwL{;(zd3!q%*vX! ze@|e-P9!cMDz*u%{)Ik2W4xbKnh?{laPDUASXX9chD@=rxi7GQH*eM9u0I2f@)EAF zv;Z4WZ~KSpqo2()u`%CS)sQP}6re(V5xL_^roxul+e-3ab$(4P-u`uZcKsgLNS!Ux z^tE~nHJx5QHTLW8%9`*~K-vy5v>?VdN->$ZqNL5G#Tl~iiN8ekSbQDj91FMfpIER; zW$G<9G##pYNP{T^I1(LO;67tjN<=2q>02qzMZhz%5X~!Nzt^#1U>ws9BP`-56H71a zQsv%US#rVRsjmTrVM^29K0Vd}?YAF2P3qmXj&bmAx8|kE)Pfpb)SC!9&qg+}Q;)}; z!vZ*wAOmvoGv{&E&-2XAcmf+BYZxnfb z-*V}_^~mQ7l4gXq(I1vm$xdP@rX%xL)%SXz;c}-5k7~CD&GlyMHXM_6@Zw^Y% zrf4Vu#RcQ~Bv8R?Eb8f({~rzc77p;U@ZIsEq$8m(tn3k419YvIbo=o0>T{d7pLXl> z`d0pe@LBA#BXVkc@pwj~xtA8Ij-nkFIcDX+H-T&y+=ejU?+{_xlQ}wMK zX5|0c>ID1e|Mlew$_LH+sLnjAgExtYOKwk z#K}Fr=jn8%eB|R3++eY^ykN|~`#q%c{)c*gDLTc=kqbsv=38m5fR4wI-)vXz7g%&c z*-~$p_ouP9nj~=>Jxbl!k`g#u$AREFLq~L_Fvo&l`DMhq+dA={6^4j}H-a7Xja2b% zzkBvGf4fQW_s4$sk8}NyTp3?{JoCPD=d#28w@HgQEWt7>S|i5MruYuK1qamds&JBS zgy%ejJ;P?}Us-{k;`91eGz8uPT{f*bl)Vc@~r>f-sV zwZq9_!ZQtl@4fjRxjF86(0&XLO&3<9USEEwj6B7AWL->24#qq`aD%sS3CscdQaU_3 zSX8cTkMQQ|{!n9~Y%qE>dboV}!P0^R%$4q<&n)KeTp8aP0DZP@O_8Uxg|xHSn*eG= zU4wYHi_vXhYFQXpc$?+pw%ksme>QmfOX^b7Ian|Q74s&`xV*anT~do}hn4*xXo*2b zqyOs*gHT@&{GAT497%3}+Kd9N&MhyCq|9gm#2HkSFh=%U%ZZdqh~+K4b1AwbfE)4V{=N>Ia!uE5 zh>5nD@n@M6(#EmUp@>nlHQ*T&Al`0!86RmnyJl#Fopq~JEANES zz)M^mZTry=LAd%E=gdG{T3&!(oF^rC+Ij7Qu5cyyd`EU?^_-aI`xyye5vyGh(ged~ z9;#-U$n91UBrW^RhagY3J`^hwC#Ce!`#THa>E7H>=Uz5}g0pwt)@5+O}*t45WnjG%VJYsk5Z5hY8 zERlZtc-WuFd`K@W?y{b6Y;MaugH6S%lksKcuLL)Vbkz+S_jMF7tngm1xNR9WUA z)01|@mC-7}BJX&%8UU9UNMLVRC5^g#T=Krm`RpFKNSUZ3H84oc`fVloj_#p|FNL)2 zy($vy%PJ7PaN@|FNm2gcHsVWez}b#fv4EStsIN>(X+;)&-6ES%eT`)c+OPWtylSHc z6LhJ*RvX>dm_PndpY^@XvUaSCG;ibm%|zd(0}gAw3n*jZ~e$If+d@d61T z#$h-cfkl)k;SE0gi>Nz1hHV)Km6I|=-uCNfy(iYpl-xr@RZa(#wV37rz`0zaYZwcg zb#afBa^Ab<^E`ZSsmmx;a&;=V+#ke)3MH$miwAz??&|AJ=QCxMe~`A`5MA!PhO!*&&l(`eO){Nx$vUTDIH?ZiHEILIU+EP=&+K@uC4$aq&O}=a_P57`hCeg z2-W?pHLTd!sws-SIk=~|cSrFbvI(SF+)xR?KB@t9S-GZae)&Ame8Ol(K<>Uo3lJ%E z6|Y7TAzI)bVlGqR3E{&?+nL(Uq>=vpkJ&E@e#`ndUtv(~lcIwZs?pA4co|7C&~)e8 z0B4r%B_Q%w(iNX>JrNQ4B9Ru1o2k_nG;mBcIM>AmQL;HEj}K0aVsVTztP3qo&TPF-+hM^^gLy`*c9fH57&#Go3ScV%$yVh8b011&dA>@l%;kd;ws3cq%n1l(lv z6GV|**55&4-gInl&*!>@8dOtkx>{0r7dn;^koBcuPQro!ie0{dxH*uCwvbNqw zoIPMOrDGMs46Rhe*A1F177&!wVu z0<-2kACPw?5tKw|#U1+XG1Y@p>G;=uqyerzCZR!C4LMBmBe>=o*9LABceSVpS65@A zv-9Pv`8oADQ;n#*M#Vv zE$8Q3Ah}dvR7-BrtG#srX|jg#sQqLfWdKFCu$;jx}5`RuqArxuh&aeW~{tV@x{sKq2A3Ct{mD8 zM~B^?`atJb9AQu2KL}#Uij`3JY!(B45G?p_AN#-VFT6g9Z;;|g>UWGheR2w<4N z6{RlHB8)DI%J>om?ol<@2%~9dn zNKu*AqXUjw9Ly5RCORF|hbNub2+tZSiv^l78|Y^!Cx|yQ7W%#A1ii0p_nmqQe+g}= zt+bsI?Z3@0n-N!h)lSPDhLsc|p6q`9E>if~w+5mYf%=(zG10O0=c_cE5z6}F*PWo9 zfWmW+I~T$G7O^LKVCsT&#!51jqK^o3)J#8Qb34qT?Smrn!I7wS>AcYng9+*lH4 zc~Xv#H|F*(-LFfDMiXDrgl7Y=+Lg(tIf!w)4knnj;ZfCAj&LL zFk#BSMaj--)T@ncA3O3RkKU?(H-01LUwTJCm*XY=<5|=fgbh_Jxc}{|JY{X1rz+$& zA=deHvmFbnMWFhD;RtZXs_}!}JvD+?a{e6TM*;P8bUtWLgR@0Y@G`Gi1znQ@6yj0b z=#Wx{?f8oc-x#j=008_Lu@cfmPAFE)$@^h>Vd zv7mGd_12Oh?Wt9W{ED?>Mb&+dOIaoiE@|=@iU!wHKyenbgJ&O}p6}R{sC7%L`6!s< zt-NW9)`elX(-m@ay(!rVM-4N+WWVmkioWvCGXA{S4~FGoVX2O6ztYq5xoL7K51ET} zHF+=k?ojXS2L=|M;3Z;&yiskl<8C8@PGxjya3c=zck12mb#am!hjnt~>|v{C$WzzS zew+dV3bZvFavYhLAdv84s)KAixHJN(()UdlbMo-oz3kVx?A&$j*AM!-q~tYhDvymn zNtLiyH8IrPhFCEhDqWbBmhs;=l0Xp>2VDp1Ptq|Je;2f+^{jky0&G4UwXOuR%CV(D z@p+`p$${J-c49AaXb|~j;5WaTfz*#Kae#bLk?dHN{7&`W~qW_p}coB zRK&|TBeP`7Pjc;5_pz7O|Ftsb^=ThBu$L#05&9an<2ctfj2n!z_E`8$?2y$THflJp_Vgc%{pu`P@vE zjENFg;4#OpdgYq+bh(xG_URzDA;i<>1xXq?xIekN2{ei{7hFgLOKaU?ecTo`okVIJ zrBm?J5xGt8KfgAu2i5$X0(Jvz{05FTmmkCWr8JhSmr`1K7%TH;Oocp8Zv#3HVyOe= zDKl?J>j#vx>V0KB^rOwlB!};jUidTghjmVw9qf|WCkO_*t_jdpajaFN1vC7V8fgd9 z{ilH=eBGdT$2Y->*Xe(IQi`)9wIV`pU$*?P@$iCwJGhLIJQ+e`{hxZ*pAY0fFKM@% z$IjBu5&?h7{*&$)f%ixL9h&cxz5}|*lH?@6wv_I_pYZ!-&g*NwXDILe=5w}9R_$@2 z`?+>X@GC;zXdgT~&!c{cO-G~9Mwk<8Dw%703(^Z8!%}G{$l@W0w9VM)#!zt>e^ zlhp+9bSC^IL;n|)RE35d>+Kx|o`h+FcLDEugz03OZAirJZH@OU4U4niQNFoq2b2q;J&hRy7 zh-9%yq6eOcmfGmu2GYSfVX(XewhdITD-EPH9CLf*aFvUiYBB z=hWkr>o_{@%E5))xLz-DFe2N@x0Qq%Q9G8m;|3Fnr|#r`>#XBDs^ zv|0O?#>8jYw&Zp+m1mpKxN8MbK2ql&o35DLp`c%6z{raIac;#|qN?Ri(#Yr~R&mLq zH_aYYjpND37>+J#9Z$}fs51x<)7+8;TBwLCoIs)wr!q(sA}G_1*EMtCpk;ld{nvYW zk?s!}##~{S7KVUJ77!^IQNUp{t)7SL^S?;J5VOW39k9%`hCz69aNJe1afXtNM|2I> z+l2``lSCqCT^JI^+PnQz#Bg+V8TSec%g9iKyxdPTmLg-ArJmFK!EA%D5rW0f*HI!F zM830sf0J!%5N%2%?h(;_iekC7pD~k#Ze@~Ogwa6i%^7^{%t+te91_Vs=J?ayk!(E1 z#H!iu`W$S|R~pd5H{=lKe|M)VyCsX%Kgd^!y1w?oIb_h*9LOWw8X!6eApT}UlE>x4 z1OfmZL)3zr%N$S=OO6T3e3c+G(&BKzM|q+B$6kO@&kJ_ZFQu3m=yMZiTV(E<43)pJ zIRfu4PbawbTmn?vjhGNXQQvi;^mjB86}l~vBw56X6aJ>U(0uQfF>x5UDaVgpo3FB# zii@GLYQ{gy_ArEyT%&{AL=mb$Vqo&Yoz0v0wr~RDdk^Gw@cqsUWj9fPkYuGwZaO}P zUl7}di}bAS{lN?St?a!?g)&p6Qb|0IJRNyh$bL8R&yt?MD<607I%a`eSA(|LmN|sS zK&pBKnzqN~il~n3&W`I)Y)V0kv0))9%>Ujq^UM5x_Hm)>=-Hi!pQOmY(sGgduY;gm zzRDvCc5!iEqTvpI9co%y&U>j}FZ3rM2kbjTd~t8eRp!cQDC18p&a|IVg~i@|5b}<5 z92DVFc~)W!*LaAO3-aYP>?=?vr7y9B)Z32u`4!cGy*;s7@7 zx{(NZ?9u#&%8e#Xwiv9j*4>|g!lE`QU8P!Pct+lsFqA56liQEnm~Zge0L(;lGCHba zNBsTe|6G83CG^K~^$B#0kAGNa*Xhub!(?cFN+KFf7AaoH;lNXZRRZPE<)eep?17Oo)q;WwH!-!-5@bS z#K_=T=!ad#_T*HO`MpFiMsS1~`n=PvRXdmjOIiJS;PuCRze?-%VVlsIfc$t2gh+PiUl@$4#^8-{yPq?3O}fi7D59YK?s(0P1O za50NIAZrO>Zf0H=!361+sF0Mtghqb1Y(YuPV~ZYIWoH>jA3<8&dD#MSP*w{G_>5_g$@RMRQDWUC=eH{Q%X zH~U>UFeFlRnIu{;?hS2XeSc$9kS!*YE}@n|>hXc)tHkU|L}aGE0;}$z+Mrjf+C=p| zxh+-{wRk(J^!=o$qxU);Dl`Jy#vjmdX!zshkIL;Icgnbq+!g6lOht1hH(cN#XDacj z8E+3O^0Pc*@P6F7K}Ln$#v^%2cZg!Jh?Jy`Z@8&7o>%C8}Ypp!x9`z|EhbSkMsu(0d(6wiFFF?btG+V zrXjeN`)!dSU-wG2C>K1Bj!&Y))Se?bIB4iTv6BJJX#X45iu?wfBqx^`)ggl-Zo48! zyJ#(m$JI%+ojB_^UD)4cI$!inFq2$cWr)+UTp{CK#nEA>gwTYARtpe0+xDO;v6ooxlgUB~O|<;~?ATiQIA3dwc~ zRJPMhmyt&(XLOw<75tT<$M`kJH^aMyZKennqywbEnih3Ffy?Ymy+6aoRv|^;wX(`; z)}x|@hDJ% zDQhKxO%vKAZ{r1y{5#+e8+5zsI&m6NR@loEVh&jQ0%zJJ()yg#hSEMxdX0g> zU~7g)S91UzA`@w?4fF&^_VOb745eJRJ=%S@YMHdAnq$I@OUO7j z!BrR$p&`o<;Zv<+=#dXXT=|5!txdSExP0>NE5@$81S&fZx2K=vx9{)}mO^Dg+zy!< z#0{vabgw*TRIo<+0flD1M4qRHHzNsG@(Q4ldxyFAn_<$nKI4xhV4f z)9DlEgpO~gz--u8GmiG-_L-!T>mBI(r?40T2q5(;MM>K!4YG_#*-z!y{svr zFOq-JG%;eQ{qArB9&Ew1BVQE67ax|f#+@8PZ`;votK&%Pvp@$mV0Q{Ri43>qPnRgz z7kJaRw+!6eC%p`fysTNDCJ+t~jMRu~PtCb!A(=tgOZ(!pT+*GEv`D*;D7N50tw^d_ zRvN#^{z=wpNw+v!)^%WLK=S!g212Y=T6b-=#HohtYl9((V?$-?lf%%Add4-dVh62Z zTo7ZGVuy70^Lc0X{KA~zvBY<)SGAIZL`Wg3@AM;n)??#ny>Ibg!AQ!TTD6k}9?jzl zX+fCoDbvPsRhp-_xjgXzz38I z&Mz9@zHRnIaX#H$Pjos{n*YMeO|Y7_pZ&*)bEXfU@e%|8Hr18E{zBJM&K{pDa_JDl zUAx|k5aT_QG&icGGh8No8dQvfA(ysD4)2WpK8mTJRkAx?Zd3=0uuq!Qa`0ChJz4C$ z#vj#}R1S8;dl!Jeyzi9~h)r+heRe=TrTmn2>K)tF!H8K9Ew2bQ2c|>w_lJL1kn6GEVbC;J~9k9u?(X%Y4f~Pwtq|Y z<0BQ)1?P5I2?-~hpT>E7lIeoLAjLGizK_*k5j1lt+Czd4X3KXBesWU+V-Px@{kiNS z>l)|{%N-p~klg79c#)}g;8;Kvc3L0w>B@uA#fCq0cQT~Dok3u%Jdajr-$K{INI%%S zo$oheH&eEE{TT67nn<6K2YQcUf)QYBilEt<%)(-nur6p6h{v0iR~m0g0cP@%@_fvd z-7A|VaY;wxfHshdRuTqp=<`&TNB^EI{AZK$7Cn;Olh+>d4+n7Il|09A)Id06`<192 z8*A3S`xqa~Ep8Pva+S;zxyM%(Man9U{sh~PxaplELs zbgYtwG$tSrS-IsYiYb4@$G~pim?J>5z@7Vg>$gcftI~Q0pvg!P^i3&L9 z{k`j3~K z<#PhVtPNP<1ERF*EJLSt$~DW8G$NdHoc#R>9BiwfqaZ88JVvYh-mYH5tc0(9Zc%ida7DHttZm_>r)XN>(>s?ssEPqddhYGk#j4gfFdhd2pZ3+g?a1^0b1 zo%T|u?J{-hWWcnw?nlD_`BRAm{9uVX&-l|NDc;P)Jqu*FLb}rE zH=o0bj=s|5u`e4DaaP9KC~Lg6 zNe`TOL)B+`bnPM;O&q#N%G88mKbCtI_u=k6?hh^oCdjHU+-B@9&k>a&)SlZzZ-VUv z9X=)EJ872}cn}RFcwi!8Hw(*~#{l!V`6X$P=cj-&Vh(Gl`YN1qXE_E}KQYaaaS*|E zV@<#GpR@h`_6F6D z>=P)CcEyZ=W4$CKHcYS$I^g90a1VMK&Tw)hLk{~!4Tl1Q=yE1{;pQNZb_OdCw3)J}d zCs`x0TJy|G-&z%Ed8k_!&|m#{tz5suM$L;0RWZlro#$( zi0y9kb_VhIy+lIfE0@mYh~w9rbTXTrEj-R7+ncre{NEw*`G$33$iTqyTq1C+T%|J(4* zE?8@iW_&o;DAO#Tr|rZ1W1_3x{YOPvuz(m^&ET*CO}a|< zTNkV0!_PC?KyhwryguZ_$j}W5i{zo$-Xn`j+Pa0g^qe8IQmv92CwZ9pu(hrp^ovM8 zkC$4={M<6+WgeLav8D;M_ZX=YY7DZfja?>{elexPPxfGZu9&{styQ%C8TQYl-ekEt z$II_<;ty~Hp%vQO_@+CW`>n*K&fODpd=gLNS=C&mfxuf4+xdb;QZ zb!`9m*wOUue2qaUlN-LgB*cp%-vuP9F!a%CnL9iJL{Ms#okm3X4` z4lb?H(IN(aN|P0`cVKtGC`KkSYJ0B+9A1|{lDJYuP0-OVq9cbdszfw!i}L9GS4=qx zV}+-6^ZzPrGL(2uaiUR%vSZ>Ay(@aQZ4D*g3Z58jm6|TnUe1}PSNvh>ONG|(>qfkw zH*lzt+$H~W4x;MLlQ(zy_Yq42DR5vQLD7V&U7Ajg;feU!Wq=$*%G}gFzv(h9uRstP zYfANeF0wQS9ML{BQDirPEQXb&JkJcOoEgCFxUo~-LdQbxX6T2xvdH^D1vyno}ltZ@?B~*w1I@i|&E+stX>Gmt2Wo|t#j3HWS=MV*AtSDtoulOtMwYj8tFK+O5)Hmx*ivVRfi;j9_AT6 z^x0({&+JN{j}=GMVJ1Q-Rf`J;72Y`*n7ljSy%g0u`=W^;OAI;Gx@8#YoKv$-w2y+J zy-2@Byeo_%u&MAlKkN6rQH;{VExBuLMaR^tbx;0h1YeFR|H{a&NVVsl%7>#4zvXSO{5&XzVqx$gOe zl`@M$>FYoeZr*Oj<6O%!%YjYgYI<^^`Fy=cmZpv-)FUhRCUo#F?AF8pW?oX7KhiWW z@FO3$**e4!i(>L%ed;#q&}9B<`_bv)R*9}hZkf4Pz=*E~m?XFpI28R9ZU!*-gVe-b z!C6=qzcYywTg4c=s@KExd(a0SuQl4AzxOB`2P z94jKfPTIkYY(6L#j`f#lAivlrmd4|s+PzB1{P0CCU+P;;*88bbgMspiR%fTQs#)i> zH%cAW{U=@j&spM)yxU6yW1Q zfj?>(AqBj25SXra_!Ys$2a+_w&H7)G-688fIN z)IZ*ZMkhb&9dosltBe#Zl1ez_*v&ajeOk#sk~WVVDAt(QGT6tJuR^JX^wqaWk12*W)c?J0dNAKz0MXjt1uSucH*&e}00$(Xj60sjcE z`MRBDw>$F|;U^;&5{0V^nAqjLDW52^_ylpJ)?T^#->a6_u)8uEJOQbztR@GF4 zt{+%Aj>%t5D?|X8$pE>%Ixm z44yzv#7KM-XDlYABEsf2_AySX|5DJ%NKXrwsJP21@hsYQjl6-Dm3Zz3QuceT1^BOf zZ+P;kTaCI8=`9T>8?V-n^sx6k3UO4(XFP1g8hU7R$)!zF`~m!0w#*v4eLvbv@|Bdq z;_b%0qJqMgH)ROYw1}n(8D%lyS*39j8&Gr8g2_=liW4+CQ6IJibMYio&{wWYLDXG# zFwU8|G?|7L(t>gZ0%>#(I;*r9@4JL)nC8hD^CQu)eQ_;{0`M#C@985Qljo)Tj{ElG zg-U}3TpVT;IKIu;+Y^ep{ysT!Vmt@*ny{Rn&Ld6+$UwutBYSly@}lfF zG;Wuc+pRkPL5o!Ujj#tqDGsalk+H*zCy@@9Fg0~VA3>oBHpRWVx;AKY9Lw0uE_dMj zp5ipUF!EjcE6*2sZ_Gg{^#xoM{((+o`RSZc-b1!Hon9S|XXKTghBm zmQpzbTDQ+n(bOD8FN59_X?0u%vc9csnF!e$L(T47wyJwwtinYuz&O4?ORCq$D>Vj5 zb&hl6H+MINa=#Dv9ACVA*tIQ@^f790{K<5RISMEAe6tf6TpR!lZWyzqj5Nb>?uQls z*KhvcTvb$J4fehfbi-Q?$@eB5z7u<6PGW8Y+$h1@+Y3}xZ|6%@UgB?^?rJ>`dKfJI5e|yY0(SE6F?%K?_GfFtvRQfaB zMorUp#v%&u*#pe3vrK}L$KI)y=&&ZS>#h+@Z>cttRDi`@ijsLkKrxusK zqb?;Nop)EcW_yTT!`XdSS4jB@$L+& zVn4l=Fg&7-;s|OE<`%}w$}>1*SfDSv%-xLq=dkkTEwm;cTw$0^Ym1j2GfvuR^U8+> z2-?C~j*;cHld~fP@?M>CjO8zfIQmy;s-*U1ZsorD<-$n#$$NwL@WZDY)9we4hv`|3 zyoMH%E~ML+A}Dz+$`ql)akcuZc$Jd#5R}IoU?AwE=XF1E{xTYj3w&S`%1ngU(4S;J zcPFgCtHHMuZf(4fQdMm!%D{qbbP2m60^+MdNz2A3`$$##QpHr|d{ z;}L{drJ2>-fMl4{Z~3aoIdYWk0s6&0TO+Y&ugSo&gV;lpa3SFtfQhgrN%lWp8jT2K zI)+G&Opg0bGmwjaKq}FjovG*6r{Qv)vsSoRUdPSWZ_;~NU?G*V&3_T{!xxC_|IkZo zY#l8IH+@{iw70jeSsm84abSM zm?^Ldq@&za${v?Iz?T*WON@3NB5%WIqAXo6=$26N6a4t@s7bB9zDgU4uQ(NQkhm|k z{2}P-aH#a^)IBC|5*wDfLLk>*qF9Su67xc=0kMJU={~cyzPJw&`PxkrwV7S-hB7|LHHS6I?5Vj7s@G+JF!8NHWJtyW{gC z+TLi|%-r;xP4Z#^#QIA2@O%D9|4%8HV3z4;P4hW7 zR!Xp!IJ`KOMb)(gA(ptY3CAA%M*!iF?fHw1O*qBkCSxcZ9S^Iqohe?OQ2|{aB#w{2uo@YW zG7>~+UTnw!(@We6AJVBnXbGH&8USw#ezukX9{G0!LIt*1keUW+R1B0=;9|85c}0DT zsO<5wOaJv3tTqcc#v$^4?|YwHKpdLCZN`C@REglf+?^5^^fnnx4zAlHgWK@GSPbjSC#R3f;s-ao{2hxrkq_BdpJoxW%js~@iH z_DK-aYL~@#9|)%^%Jfe1e(9Q7`1bHKTi}+>(tx~3BS1S)O(60wrnd0yFSQ{&>b`Kv zA2wtws+EzRoeb@&EnE5jBEMA`&WMkT8IFtVlA~%mFut&0g*g1QC2UZdhcPQC2Y>m* z&^%6)TgHZ+lO{x5l`2XttD`ewD(5d=vikWk%q!9Tr7#d8UU1^i&mC7HBhHefuq5|- zaq!v!mx$`D`_hZr)bT2Y`T^h>y9M3PS%29ZhRpX$Q z5SAVS9t3Ul&IxW|`CjYr5 ztQ_Tp)!iz+vJxw3D#e1hzCP)MPkdzdl#(1d96j4J#UKdJ_I0{i7z}@fp7p;cReN6) z`X1lb@^E8K4>1}q3L;dUrbifID}~FSh0<}X$4V~ux%eO5G5JX(&Afz7e*5!ndjf9i zUEb;sx$g}Q4nF$~IhXh^!;n#~`||m`!f#qytE#HRj5LZfQxqA?p}}4E+1&i{mD6+* zE5d;FuV)T6J-AIOA?eZtO*mBFH_evxlQ}?`@jgS0#YcRq6cj-&dAY;=JZvq$Zewjh zu@5*Ev>>OsPcvqNBL1=_bb>H6++$NP$^DweGDh%D+=5gmB>7J7IA}}8@-t{@U zoI6qXjr2uw^DC>c601wjJIf9^)I-I}Rpx+{anT{fg3OZnc zHQ!XaY;e)1=NQ^wCPd~gy5}1mh%+!h6B0Ao#TC3x%h^h4?%MGrUo~{RD`DX_91nAs zfK6SSTFuxzJSCFX~C+vpM4U3Bp?q7?+Z>f^HzvnP+J6JDK&KiHGnCh$yAG|Pd`xR zP}9g;&}9rW6gD%}IGo|l0~K46v$Y3cxSP-rFzvq8fTuA7l_fpWbutesugW7r?)! z{A#F+m(3I&tMR}O${TFv&{|F#Od}7mS!vWS<9j8&p|rnPtp>ac2b0pJbP5bY2X_D9;cYW2<}I8q&-19LZx(%$W?8J!{iMO zs-wF#hAidBH-T-rzo1eqZZT&#{~{aJF~kaeGoE``Z-$3ym$XNx7E)9KONpAqDwRM3 zY6q(wUx}Kht$g%-UK)65D3y8xtdox|y?mpI$rua~|1G7qIv4+Vn&E?+2M3P8Y2h>)%aRD`&YMm0nhE z?=$4f+4@M_Uw4M2&mSs2GD%8vCPPb=H!AP|x%64zUH>+8qYX56n|>wpu&z_(bjDVbS{vHx^5Gny`=M7W|8UiY<#>Wbunk(6#qfdz_E@- zw@`(YSFF|i&?B1Ha{DygK<6TTrBQPf(y@1n#S>d2BOa>paS+)X!YVg~5KjGm2CW_& z9gm{~icj49hY|g^WMDw&1sRy`R8AtXRi!t~@G~9s?PCx`h0LWA8y)cTh;rj<+}0V| z$4O?OaU7M%N|z?*I8$-!zj6BRo>{NIyU9n^<%e<;9pGfO!G6~W1Y0UO?|_lU;$mU_ z5%0VwhtH6Hk<=eOh-e@Oo6HsE<=Uf}!mbiv@F@%6NU0!_$yT-y2mu$}g?Jdgc0O#v7bpY7eSQY=9L~A!681L3%{RDyKg7k0skh zgvDJCEz!--++^$ofZ$VvQ7wi&5&uU^rJd8d!I5Ze4!z$y=l*aN)~9>;snaPiNG`nB z2aY*h0A+zWVKPx9VB-e6x$0s!_qoQVhAf0|8u$Hv5zDhpmTswOLJ~6o6RpVv2h!iv zVG?8-1#>NdOuk0qamob*CheeqOYeyus)G1qSrok|vFOmTeg)@uiim$=&iXP&iM?v| z{ENumrT^!(|69~G&PrOS)VEA2X$?+DL4pbH72nNDIs0$CJ&EhbUoL>UtOTuOX!B2{ z9hRd?9@0c8-CB9KgDPd*lUrr0Vgbt6$G0@l3{)?DGKyKG>l;BfK|C#-c(jF2xa!1O zKLwNm&}K4ZyS0o;x|lx}cP*wXy@LMCr8VPRoS>$G&L7th8{SB z+4y0X3v#*iUsDCbr4c;uBU1TEtJyt4p{-=rW__12>PRS|_=}mSCjZ z0|eP&B{7a)_G8bxBGOO+rUUJ{iy?_9S4mDu*H^t~Tx&qK2{*X>J5+=Zj5>5nh^%_lPXaHVf6v_^Ats1bD2sC$C z^2<3(Si58VAIZhLG8@fX9kRZ6M-d)gsmgl(Fd)i2ooWU|_Wn&cj%58UZUdz8Q4YD= z3tu8K`D$(}el# z(*lN_+HdF0U)_$uIql~iJ~>CHr@OQ1y)Y`WoW9NHaQFr1*Lk=j+t^3AXqqoie)zmL zA}p@D35b}cqJmm)jq+Qv84J$-4`Xfw2g0{Bxq}KJ@7_JJlqg~{ku5f0MlQXNM*tvm zeuRPt>GjVvrZOLK#;2V6(KP7tt^x${T1j+SzSYXEvf(Zd&8SkU3il08eKh-cq4PsT zAa5yx-nP-PKw;T7{u&oUYk5GN^IJ#LU~D57H()}_rmQOPdceJ_ZLe1Kr(ysa&6_o; zQ(a|OZj0`0+(2+ly<9S`Xh27=9~=rgIDf51d)aP^JzHsAn0m&+SOs*^6OD0jtbzVr z{p5nHBd7eEU0f6zQrHYrP4&PK_+j?JeQ6v_c@S*j@U$L!qw>Gp!_@KI_dNR`eQ0QmRWLX=h->pt3DR3_Nv)9by;gN)~MNp5uOg zMIZS1V!Al}LI%6AM+B(8C$6h_sXrpU|L1eAaQO>h$jZf?mFk zEjQS|wigui+S@yQd>b!c2LaRVyo8@KlF>k7)rrzV3aoDh9Lsoe+>=1YQ`(fhlfZ7v1 z5&!a$wTgi)biRc`q#YfmJ1hqi@k~1(yz?T{(!$2n|9CMxet41h^>k5P+A+o0#f zdMegJWawSfZVIb3;o^#ulfeyK5wqIlKe5_ZzlACzsf`Uh#J>O`&FD^OMrWFPFZPF3 z5zFDk*y}LL5_(~QAy(Ko{YH5A`F^PmTl?%EcG&l@KnZdoBLhy-FV24*;rTBi^V`t9}lZL7dD4sjPYfJ!#x?(VK{9dh5AV&vXW{2`Wt z&q^e51dn`I+15IA{AOZhT+Qgq75B4L5km*W8T9^NLrkTx8{qooOcMSqzQD7h_UP_Y z?Ff3oP3RL#{tZe$X8jXcq+x_FE(7MIGJ=7Quxb_{p2*8E5jgootiE$eA$cXGzmP|-x9c##vHL}K7vJA%B82dK1VVLjr`7^$+U+)k1abEY_ zvpvswo>#A;N0-P0YMTCms2cKP*_dYEk1>uwg)cS<5$mh(%zQ0SR7lRAWA1U7lB)4F zK9}|+0$aYLbGhcGs?;VI9P*^l3DuW>QK_N!Qv2wL)0AI78Pq1r9BjzyFo-h3YUBTc z-#w)ikhX#zN|8t1#x`e~h`!U1i_+%bE{F%nBI5&M@6G`KqZ-oQ?#8NIvr^Ap|Zp7>%J*K;BAyha4frb5JdK`5; zx>sCdTb7W_AKrwzQK?*vT)n3ptG)i(<-A=MBiC+TC7tc_4hhsA8&vu=C_CnEipBMV zBz?`XWj+NK?s|T001QSJdX@2fKZ_atky96uq4I*gPiUC^-SUb#BkzTV=C0<-IfjzP zB*XWUDKP%BVDMgPz(PSDdWSw0xbgF(QC$(g;co2C1d6nc&=(j`7R#1n9*wFbH|FO-5dt-zlCV`^>6v7SSytaa|F=bw8+GXLuJ>WP1 zCl|r?7xzgwD}Dc74w%8#GPkQSN?`r1zgqTp*L`F-DALHwaPdKDi$SUwG_)53FYOAG zE*?%N^tMHhr|Dlq1sMqbhTgvXz~x26R+V?+UHR*NAp7n%A87q?T#%s(9B(vXqNxrF zJwDoZdtCW!zyJa;=WGBG&w|gEr;^yIB=cbag_ZWXlG=sz4c`YJUPkfhU$wZt{_=zW zcj5Y(Af;c<*`IFKY3<%>KTC!CxJ`DlvVys}uUOnDs|Fvsp9;0UVGEA%%%;wZ2gwqO zizo@MSsu`;KH^*pc8Epf++c;_Vz#EU$9c4V;-_K6cV`?_ch+K*%2eB^ZiVlT9nKq4 zs%_+UP>6Fd5=eLV^5zK5hlM-u3h>!=UL}2B>CwLS=2_O&3K{lD2yAL+(o@gvXty+k zvVm1>BBksoLyc+}LR&o$dY&=+Yjlr!s+qO-?mocz>Brz_eKq~3BH5zVx@-%_uJqce zZ{m?vD;2&nRl^NtWo^@gfIfA0ul8@sxGt~QV-GoJCzy^CZ=UDp?R)b3p0fQLDUEo5 zfg2>U&Z4_(rT`XF2%V9x+2jk7|0TtWvSDJII1kDhx@mN0WSa?j+0d2q{T@%hsYM4c zs`T?Yhe1(8iO|p)$m2`*>POLktdIGJ>EXe{oxxVFFqE9nf&26VsmF{)VS>;W=oZzk zMAS~k!i>A!|GrB6seT<1H>?i`JyJ(~?@LM&4r5Z4)bvJnDaHB;redb&^5wEdMk!3^ zBOtUo+4Wwmq}=QUM!)o)BP4{v~M?KKf1?uCS}1uRE`+Np?G zxPnGRx4p@#7o0Jo(_U4pdM_^h(VKTpI}JQmrPZ2t$vED^dgj4DR))*mIdai=*24 z&v%RZxcjkH@9mebVUBBp%U}VN)}qG_BHM{3kec6I@DSWnrz3qaq^3+$J4GOLZQuReNu7St|G@&z1SEpd-OO8-%U`% z4egp^%43kgZy0xR4R-tXSa{2sITojvF?q1%h9lPkJE$ zxAIjjv7~U1D(`-4Gl*c-uFMr9@E4p@3)8L6p#Up+JoJ4KR{_HQssz3xK@A^+x)?+) zvHD>hut#vDlajQBsPplHs@pR&o1CYHcFH7Z7l+KCo$|Y5DP(+8Np4-8|5s01Htb7* z-=)tgV5InYkNNgE85t%13jHMMcjr>5S1xb!*rrKd9Gl99uJ-{DvvbN?7spflCKr3M zZqe_w8!!{r*5y~)H<|wQRF8Em_e$!Ke~xxxsoIUapVFs8@Tm-s8M$#hXP)Wgm^-ebX!8^Y&U`QRFqEat;`kU}FrlPk8Ws$Y(l1Mnco9 zVBs>@>M*SQ4sUI3?QbZ@ayWp8!8IfnF0tz2gr0LGo*hoIxf_}d!fsD!nYDo{eZ(MI z5k-m`v51_vw{`G`VG(6#^h4DbcbtFIEZAi#|C?vnhUyutp&2M9#r z<@CZq83`*EgQd&Qbn=9kuJ|Q#9l>;t2b|{B*uP95yb^7h%k<}=&l{f!W=6g^W*D1o zjZ~Dfs@usF)k~or=A>Gy$ZY@k*)n@@8XrD*Jn1(dSD_yG%drW4_;IMTxESYBKG5(z z2LrltzS#Rix|Owqy=f<(yV1Uwf{YUCQS&P@Y=L27Igi|dQVy#+xd{0I^~?D6@=}DJ zsDh=`tb2LeOA_Nk^uL|S(atr-KeHd50iKwOr~oVD#F z?g8_#uJBVvHJyOYw~IF2h?ED{WY0T>WNeY(_ z5ZRwWXtdjnLz)}=G`fLxPa+NK4)kb3WY`0~_k_*dUTmiM-rP5&`qlw&jG`N1Rbj(J zY4$XX{0H8rL8l!8NcMU(%ejj|#l^*It9Ei9;y-U5&*yuOToK?f_aagJM~K8YCMG7` z^GeLpUuv#Llrfye!;m3A>bGfodp)!P0YO7$=1E3D_SXy5?9HL|&GY-u6OC>>%?NpI zbYr|qW?o3EqzUcZX}ebd3Vn%0(npddQ|S&%gJkqBje>U*6cTcfB~G-=T47EVO}^7k z&JV~oe%I~{>NUXuuoS>boh*_bwl0sqa5Q z91kqRv_`R<2Bz0N4wv%G=p^sa;8maz{jmFQUtbIkS9saBDOd4x2D3@DBG5eAzV4br zI$~}^bKossgQCIE2Hh6Z5LD6JUWPc;{|~6j{9md+kjeZmTgmfV0m&M%JD>xC=XO%N zNwiGB=plcgkKHb+Z?$2hr7XTXwq=X~az~uXy;B2>BXuBs2=)M_#%e{AjLlpdi_n3g zL&sZ|JMF?q@jJf0igp#iBFF-Au<_Il9Xwr!A0xieFgP#L>(~?cm8!L-#u#y}P$>B? z1rFhasn?U@6sP<)fAJxM7C$&fyu{m)Q^%dvnPywPWEd|G{P%vO4XP?6_nEI8aU%ltpj^v}TH z;GC6b@9iOJYNxikn%nxMWCu{k>*A-}t4qeMO_srUo4;W&<>znvip25h>vN3{u$BwP z2-nOUq_GSTs=IFkLz;2&be&RZ7g#b-B6YTCLLb9Yq>(3ClBPumr3To^y?V|KwNs~l z#E7il8;}1FxLJ}X`8U-|JMakn2*``s!WS+afsDT=y8}hV>ZyQptcOR4rZbRCM>SaO z_m7>JYNN05WwuUJXT95oC&pRDMpqIlODgrTf^+f7atXpPKcTXhwPhjGjfx6xgm0V4 zz(wwKy*_8Q;hovo8VGd-&1^QdUNEy(UVjS-@oO=6%%m2S6c}lwciFWwjpczpr*K~c zpxw&nywuQ#vEX31_$Kf5re2IjH=bnr?E*z7*ZtWYG0D`^X6A)&z^-!m$@uaxUh2s= zJKKbbl+oE>Wuvvf`35@>J4!fh59 zMgsX0DD#`%+XJ#7+Aq`RvCZY3kUJyPj>T#NhW@<-yJ#(GUSV{o*0RYKD92cW1G2N< zRP~&)@T;_GZ~T@t(EjD=G+H94RLJ>v#+?V~AcjKO)IACIH~FmGTg*Ly%|vqrMtRnM z#*VjdHW~FG4-Q{76{dM9>elQpeXe>BVCur3cCa~M}A9}kU=r_wy1j-SV zbS&1<)Wl3tvzGl2qK`}|EBFDp(2BDx=*iI~C)M4hsXZOvLtY^j+>K~;`A%6ex?w5B z(IL^M!1(fIe*Up>BC+8m@R}a$kmFsxi&B;~U2E;+85Dq2D=Ec-Qmq4)(kk%kfeS#n zz3&6ue_NSXOkYXT$ZV7|lYC*@e{p?1!m!CL zsD?q5RJGfu(v4!ZqrKv(+Tf$*I3f|p2V~zZ4Xc9C_*0FXagQA!5I(H0u0mC9+hdo2 zI0~y~^D~?f7Z+D?FF`0+S0%%hIrQ#kpON(^kCjR7Z}DnU8q6)e+VAK(x%|R3Tdi1G zQ>kR#+SW*jo4ViTOr2WbUeUx4-;MFzmC@3WqqS<^QuB&-mQ`gXC<6EaF|DfMg8!C1gE5E?wmeK&hjrKi6Ediis!frpcm z695VTF|}qYhaU7)$}2iPnHYaE(VmcHY-BW~?mAg>hJ%B{rNH=wB?5tPGBD^JGnN>B zzgd3`vBN_%5Sr}Jo@?olWmhN#HYcMPx?{^96&QDOKp-2yna=2S@O7*p5CKjW)quc2 z-__}Ax1$MQSVN;MBxJfhPT&Zj$m-ts6eS3S%KuOJ`{7&j9eED;Oyc0=Bs@1eJG;E3 z#MKyqXiZB`Pj4?QEIc%pNWP!*sA|cpPAcQUXwKWN@w*Zx?>6gS;3;o2$)XI+6fWLw zMs$1h>CLCc0M*B2UFxF(c}t1pi0EHW6|(UA5a9f!P+tY9PiOUz4q3tAz{A7C0KiRh x!c@E#`+-cF6R?6##~Yvhq5ogb15+A3+mPY6{)jxVzyJig|L=plC0eMk{{wFILv8>7 literal 41557 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 From 3271346b129f5b5027aec3d5343f771bbbe30a10 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 4 Sep 2020 10:29:22 +0200 Subject: [PATCH 378/813] make sure saving won't crash if current files are not valid --- .../config_setting/config_setting/widgets/base.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/base.py b/pype/tools/config_setting/config_setting/widgets/base.py index f057f5a0f6..066c00c96d 100644 --- a/pype/tools/config_setting/config_setting/widgets/base.py +++ b/pype/tools/config_setting/config_setting/widgets/base.py @@ -144,11 +144,14 @@ class StudioWidget(QtWidgets.QWidget): subpath = "/".join(key_sequence) + ".json" origin_values = current_configurations for key in key_sequence: - if key not in origin_values: + if not origin_values or key not in origin_values: origin_values = {} break origin_values = origin_values[key] + if not origin_values: + origin_values = {} + new_values = all_values for key in key_sequence: new_values = new_values[key] @@ -518,11 +521,14 @@ class ProjectWidget(QtWidgets.QWidget): subpath = "/".join(key_sequence) + ".json" origin_values = current_configurations for key in key_sequence: - if key not in origin_values: + if not origin_values or key not in origin_values: origin_values = {} break origin_values = origin_values[key] + if not origin_values: + origin_values = {} + new_values = all_values for key in key_sequence: new_values = new_values[key] From e87288998db354bae408af01fae131219f69a6d0 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 4 Sep 2020 10:30:03 +0200 Subject: [PATCH 379/813] pathwidget does not care about label if is used as widget --- .../config_setting/widgets/inputs.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 379b255359..f925e6e163 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -2096,16 +2096,17 @@ class PathWidget(QtWidgets.QWidget, ConfigObject): self.style().polish(self) self._child_state = child_state - state = self.style_state( - child_invalid, self.is_overriden, self.is_modified - ) - if self._state == state: - return + if not self._as_widget: + state = self.style_state( + child_invalid, self.is_overriden, self.is_modified + ) + if self._state == state: + return - self.label_widget.setProperty("state", state) - self.label_widget.style().polish(self.label_widget) + self.label_widget.setProperty("state", state) + self.label_widget.style().polish(self.label_widget) - self._state = state + self._state = state def remove_overrides(self): self._is_overriden = False From 3831af2a26d008f9c01ccd7b851b554adeb50e6d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 4 Sep 2020 10:30:22 +0200 Subject: [PATCH 380/813] roots can collect data --- .../config_setting/widgets/anatomy_inputs.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index 11d94319e6..c0a64a8d7d 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -151,7 +151,12 @@ class AnatomyWidget(QtWidgets.QWidget, ConfigObject): ) def item_value(self): - print("* item_value") + output = {} + output.update(self.root_widget.config_value()) + return output + + def config_value(self): + return {self.key: self.item_value()} class RootsWidget(QtWidgets.QWidget, ConfigObject): @@ -162,11 +167,11 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): self.setObjectName("RootsWidget") self._parent = parent self._is_group = True + self.key = "roots" self.root_keys = None checkbox_widget = QtWidgets.QWidget(self) - multiroot_label = QtWidgets.QLabel( "Use multiple roots", checkbox_widget ) @@ -268,6 +273,15 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): else: return self.singleroot_widget.child_invalid + def item_value(self): + if self.is_multiroot: + return self.multiroot_widget.item_value() + else: + return self.singleroot_widget.item_value() + + def config_value(self): + return {self.key: self.item_value()} + class TemplatesWidget(QtWidgets.QWidget): def __init__(self, parent=None): From 69dbfd2faf101facbe611c57eec4d8f925d8d248 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 4 Sep 2020 10:41:14 +0200 Subject: [PATCH 381/813] updating global values for roots should work --- .../config_setting/widgets/anatomy_inputs.py | 34 +++++++++++++++---- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index c0a64a8d7d..e4f7a6383a 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -78,10 +78,14 @@ class AnatomyWidget(QtWidgets.QWidget, ConfigObject): self.label_widget = body_widget.label_widget - def update_global_values(self, values): - print("* update_global_values") - self.root_widget.update_global_values(values) - self.templates_widget.update_global_values(values) + def update_global_values(self, parent_values): + if isinstance(parent_values, dict): + value = parent_values.get(self.key, NOT_SET) + else: + value = NOT_SET + + self.root_widget.update_global_values(value) + self.templates_widget.update_global_values(value) def set_value(self, value, *, global_value=False): print("* set_value") @@ -215,9 +219,25 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): def is_multiroot(self): return self.multiroot_checkbox.isChecked() - def update_global_values(self, values): - self.singleroot_widget.update_global_values(values) - self.multiroot_widget.update_global_values(values) + def update_global_values(self, parent_values): + if isinstance(parent_values, dict): + value = parent_values.get(self.key, NOT_SET) + else: + value = NOT_SET + + is_multiroot = False + if isinstance(value, dict): + for _value in value.values(): + if isinstance(_value, dict): + is_multiroot = True + break + + self.set_multiroot(is_multiroot) + + if is_multiroot: + self.multiroot_widget.update_global_values(parent_values) + else: + self.singleroot_widget.update_global_values(parent_values) def hierarchical_style_update(self): self.singleroot_widget.hierarchical_style_update() From 7efc99b5ef87dbc9bc2b80fbeed3acb9f4970d63 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 4 Sep 2020 10:44:56 +0200 Subject: [PATCH 382/813] add first row to list and modifiable dictionary by default --- pype/tools/config_setting/config_setting/widgets/inputs.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index f925e6e163..ae0ece42bd 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -982,6 +982,8 @@ class ListWidget(QtWidgets.QWidget, InputObject): self.inputs_widget = inputs_widget self.inputs_layout = inputs_layout + self.add_row(is_empty=True) + def count(self): return len(self.input_fields) @@ -1311,6 +1313,8 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): self.default_value = input_data.get("default", NOT_SET) self.input_modifiers = input_data.get("input_modifiers") or {} + self.add_row(is_empty=True) + def count(self): return len(self.input_fields) From 18812a64e093619422e33834696a786862f9cb25 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 4 Sep 2020 12:49:25 +0200 Subject: [PATCH 383/813] NOT_SET is False when used in condition --- pype/tools/config_setting/config_setting/widgets/lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/tools/config_setting/config_setting/widgets/lib.py b/pype/tools/config_setting/config_setting/widgets/lib.py index 4669004b53..0d70885de7 100644 --- a/pype/tools/config_setting/config_setting/widgets/lib.py +++ b/pype/tools/config_setting/config_setting/widgets/lib.py @@ -11,7 +11,7 @@ class TypeToKlass: types = {} -NOT_SET = type("NOT_SET", (), {}) +NOT_SET = type("NOT_SET", (), {"__bool__": lambda obj: False})() METADATA_KEY = type("METADATA_KEY", (), {}) OVERRIDE_VERSION = 1 From 1d0f00dced4b4001ef0d53d84fba66e4c98e1c25 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 4 Sep 2020 13:01:02 +0200 Subject: [PATCH 384/813] modifiable dict and its item has right hiearrchical update methods --- .../config_setting/config_setting/widgets/inputs.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index ae0ece42bd..eeb6cf64cd 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1228,6 +1228,10 @@ class ModifiableDictItem(QtWidgets.QWidget, ConfigObject): def is_modified(self): return self.is_value_modified() or self.is_key_modified() + def hierarchical_style_update(self): + self.value_input.hierarchical_style_update() + self.update_style() + def update_style(self): if self.is_key_modified(): state = "modified" @@ -1374,6 +1378,11 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): self.update_style() + def hierarchical_style_update(self): + for input_field in self.input_fields: + input_field.hierarchical_style_update() + self.update_style() + def update_style(self): state = self.style_state( self.is_invalid, self.is_overriden, self.is_modified From e215f2e1407ed14919892d9fb72532b2324b09f4 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 4 Sep 2020 13:02:51 +0200 Subject: [PATCH 385/813] inputs' set_value method does not accept global_value as kwarg --- .../config_setting/widgets/inputs.py | 59 ++++--------------- 1 file changed, 12 insertions(+), 47 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index eeb6cf64cd..57785a1bce 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -301,16 +301,11 @@ class BooleanWidget(QtWidgets.QWidget, InputObject): self._is_modified = self.global_value != self.start_value - def set_value(self, value, *, global_value=False): + def set_value(self, value): # Ignore value change because if `self.isChecked()` has same # value as `value` the `_on_value_change` is not triggered self.checkbox.setChecked(value) - if global_value: - self.global_value = self.item_value() - - self._on_value_change() - def clear_value(self): self.set_value(False) @@ -417,12 +412,8 @@ class NumberWidget(QtWidgets.QWidget, InputObject): self._is_modified = self.global_value != self.start_value - def set_value(self, value, *, global_value=False): + def set_value(self, value): self.input_field.setValue(value) - if global_value: - self.start_value = self.item_value() - self.global_value = self.item_value() - self._on_value_change() def clear_value(self): self.set_value(0) @@ -525,15 +516,11 @@ class TextWidget(QtWidgets.QWidget, InputObject): self._is_modified = self.global_value != self.start_value - def set_value(self, value, *, global_value=False): + def set_value(self, value): if self.multiline: self.text_input.setPlainText(value) else: self.text_input.setText(value) - if global_value: - self.start_value = self.item_value() - self.global_value = self.item_value() - self._on_value_change() def reset_value(self): self.set_value(self.start_value) @@ -632,12 +619,8 @@ class PathInputWidget(QtWidgets.QWidget, InputObject): self._is_modified = self.global_value != self.start_value - def set_value(self, value, *, global_value=False): + def set_value(self, value): self.path_input.setText(value) - if global_value: - self.start_value = self.item_value() - self.global_value = self.item_value() - self._on_value_change() def reset_value(self): self.set_value(self.start_value) @@ -709,7 +692,7 @@ class RawJsonInput(QtWidgets.QPlainTextEdit): return hint - def set_value(self, value, *, global_value=False): + def set_value(self, value): if value is NOT_SET: value = "" elif not isinstance(value, str): @@ -802,12 +785,8 @@ class RawJsonWidget(QtWidgets.QWidget, InputObject): self._is_modified = self.global_value != self.start_value - def set_value(self, value, *, global_value=False): + def set_value(self, value): self.text_input.set_value(value) - if global_value: - self.start_value = self.item_value() - self.global_value = self.item_value() - self._on_value_change() def reset_value(self): self.set_value(self.start_value) @@ -1020,7 +999,7 @@ class ListWidget(QtWidgets.QWidget, InputObject): self._is_modified = self.global_value != self.start_value - def set_value(self, value, *, global_value=False): + def set_value(self, value): previous_inputs = tuple(self.input_fields) for item_value in value: self.add_row(value=item_value) @@ -1028,11 +1007,6 @@ class ListWidget(QtWidgets.QWidget, InputObject): for input_field in previous_inputs: self.remove_row(input_field) - if global_value: - self.global_value = value - self.start_value = self.item_value() - self._on_value_change() - def _on_value_change(self, item=None): if self.ignore_value_changes: return @@ -1349,7 +1323,7 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): self._is_modified = self.global_value != self.start_value - def set_value(self, value, *, global_value=False): + def set_value(self, value): previous_inputs = tuple(self.input_fields) for item_key, item_value in value.items(): self.add_row(key=item_key, value=item_value) @@ -1357,11 +1331,6 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): for input_field in previous_inputs: self.remove_row(input_field) - if global_value: - self.global_value = value - self.start_value = self.item_value() - self._on_value_change() - def _on_value_change(self, item=None): if self.ignore_value_changes: return @@ -1444,7 +1413,8 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): if value is not None and key is not None: item_widget.default_key = key item_widget.key_input.setText(key) - item_widget.value_input.set_value(value, global_value=True) + item_widget.value_input.update_global_values(value) + self.hierarchical_style_update() else: self._on_value_change() self.parent().updateGeometry() @@ -2059,20 +2029,15 @@ class PathWidget(QtWidgets.QWidget, ConfigObject): ) self._was_overriden = bool(self._is_overriden) - def set_value(self, value, *, global_value=False): + def set_value(self, value): if not self.multiplatform: - self.input_fields[0].set_value(value, global_value=global_value) + self.input_fields[0].set_value(value) else: for input_field in self.input_fields: _value = value[input_field.key] input_field.set_value(_value) - if global_value: - self.global_value = value - self.start_value = self.item_value() - self._on_value_change() - def reset_value(self): for input_field in self.input_fields: input_field.reset_value() From 87085481551cefa94c9ddf7775ab6238deeb34b4 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 4 Sep 2020 13:03:48 +0200 Subject: [PATCH 386/813] update_global_value can accept values for inputs used as widgets --- .../config_setting/widgets/inputs.py | 102 ++++++++++-------- 1 file changed, 56 insertions(+), 46 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 57785a1bce..ea6cd121bd 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -286,15 +286,16 @@ class BooleanWidget(QtWidgets.QWidget, InputObject): def update_global_values(self, parent_values): value = NOT_SET - if not self._as_widget: - if parent_values is not NOT_SET: - value = parent_values.get(self.key, NOT_SET) + if self._as_widget: + value = parent_values + elif parent_values is not NOT_SET: + value = parent_values.get(self.key, NOT_SET) - if value is not NOT_SET: - self.checkbox.setChecked(value) + if value is not NOT_SET: + self.set_value(value) - elif self.default_value is not NOT_SET: - self.checkbox.setChecked(self.default_value) + elif self.default_value is not NOT_SET: + self.set_value(self.default_value) self.global_value = value self.start_value = self.item_value() @@ -397,15 +398,17 @@ class NumberWidget(QtWidgets.QWidget, InputObject): def update_global_values(self, parent_values): value = NOT_SET - if not self._as_widget: + if self._as_widget: + value = parent_values + else: if parent_values is not NOT_SET: value = parent_values.get(self.key, NOT_SET) - if value is not NOT_SET: - self.input_field.setValue(value) + if value is not NOT_SET: + self.set_value(value) - elif self.default_value is not NOT_SET: - self.input_field.setValue(self.default_value) + elif self.default_value is not NOT_SET: + self.set_value(self.default_value) self.global_value = value self.start_value = self.item_value() @@ -501,15 +504,16 @@ class TextWidget(QtWidgets.QWidget, InputObject): def update_global_values(self, parent_values): value = NOT_SET - if not self._as_widget: - if parent_values is not NOT_SET: - value = parent_values.get(self.key, NOT_SET) + if self._as_widget: + value = parent_values + elif parent_values is not NOT_SET: + value = parent_values.get(self.key, NOT_SET) - if value is not NOT_SET: - self.set_value(value) + if value is not NOT_SET: + self.set_value(value) - elif self.default_value is not NOT_SET: - self.set_value(self.default_value) + elif self.default_value is not NOT_SET: + self.set_value(self.default_value) self.global_value = value self.start_value = self.item_value() @@ -604,15 +608,16 @@ class PathInputWidget(QtWidgets.QWidget, InputObject): def update_global_values(self, parent_values): value = NOT_SET - if not self._as_widget: - if parent_values is not NOT_SET: - value = parent_values.get(self.key, NOT_SET) + if self._as_widget: + value = parent_values + elif parent_values is not NOT_SET: + value = parent_values.get(self.key, NOT_SET) - if value is not NOT_SET: - self.path_input.setText(value) + if value is not NOT_SET: + self.set_value(value) - elif self.default_value is not NOT_SET: - self.path_input.setText(self.default_value) + elif self.default_value is not NOT_SET: + self.set_value(self.default_value) self.global_value = value self.start_value = self.item_value() @@ -768,21 +773,21 @@ class RawJsonWidget(QtWidgets.QWidget, InputObject): def update_global_values(self, parent_values): value = NOT_SET - if not self._as_widget: - if parent_values is not NOT_SET: - value = parent_values.get(self.key, NOT_SET) + if self._as_widget: + value = parent_values + elif parent_values is not NOT_SET: + value = parent_values.get(self.key, NOT_SET) - if value is not NOT_SET: - self.text_input.set_value(value) + if value is not NOT_SET: + self.set_value(value) - elif self.default_value is not NOT_SET: - self.text_input.set_value(self.default_value) - - self._is_invalid = self.text_input.has_invalid_value() + elif self.default_value is not NOT_SET: + self.set_value(self.default_value) self.global_value = value self.start_value = self.item_value() + self._is_invalid = self.text_input.has_invalid_value() self._is_modified = self.global_value != self.start_value def set_value(self, value): @@ -924,6 +929,7 @@ class ListWidget(QtWidgets.QWidget, InputObject): self._parent = parent self._state = None + self._as_widget = as_widget self._is_group = input_data.get("is_group", False) self._is_nullable = input_data.get("is_nullable", False) @@ -976,7 +982,9 @@ class ListWidget(QtWidgets.QWidget, InputObject): old_inputs = tuple(self.input_fields) value = NOT_SET - if parent_values is not NOT_SET: + if self._as_widget: + value = parent_values + elif parent_values is not NOT_SET: value = parent_values.get(self.key, NOT_SET) self.global_value = value @@ -1300,7 +1308,9 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): old_inputs = tuple(self.input_fields) value = NOT_SET - if parent_values is not NOT_SET: + if self._as_widget: + value = parent_values + elif parent_values is not NOT_SET: value = parent_values.get(self.key, NOT_SET) self.global_value = value @@ -1989,20 +1999,20 @@ class PathWidget(QtWidgets.QWidget, ConfigObject): def update_global_values(self, parent_values): value = NOT_SET - if not self._as_widget: - if parent_values is not NOT_SET: - value = parent_values.get(self.key, NOT_SET) + if self._as_widget: + value = parent_values + elif parent_values is not NOT_SET: + value = parent_values.get(self.key, NOT_SET) - if not self.multiplatform: - self.input_fields[0].update_global_values(parent_values) + if not self.multiplatform: + self.input_fields[0].update_global_values(parent_values) - elif self.multiplatform: - for input_field in self.input_fields: - input_field.update_global_values(value) + elif self.multiplatform: + for input_field in self.input_fields: + input_field.update_global_values(value) self.global_value = value self.start_value = self.item_value() - self._is_modified = self.global_value != self.start_value def apply_overrides(self, parent_values): From df2ecaa6696fb55b286b158783cf18435dad9cf8 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 4 Sep 2020 13:05:03 +0200 Subject: [PATCH 387/813] styles are updated in right way for as_widget inputs --- .../config_setting/config_setting/widgets/inputs.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index ea6cd121bd..d2f7f6cae6 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1053,7 +1053,8 @@ class ListWidget(QtWidgets.QWidget, InputObject): # Set text if entered text is not None # else (when add button clicked) trigger `_on_value_change` if value is not None: - item_widget.value_input.set_value(value, global_value=True) + item_widget.value_input.update_global_values(value) + self.hierarchical_style_update() else: self._on_value_change() self.updateGeometry() @@ -1247,6 +1248,7 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): self._parent = parent self._state = None + self._as_widget = as_widget self.override_value = NOT_SET self.global_value = NOT_SET @@ -1615,9 +1617,9 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): self.update_style() def hierarchical_style_update(self): - self.update_style() for input_field in self.input_fields: input_field.hierarchical_style_update() + self.update_style() def update_style(self, is_overriden=None): child_modified = self.child_modified @@ -1797,9 +1799,9 @@ class DictInvisible(QtWidgets.QWidget, ConfigObject): self.value_changed.emit(self) def hierarchical_style_update(self): - self.update_style() for input_field in self.input_fields: input_field.hierarchical_style_update() + self.update_style() def remove_overrides(self): self._is_overriden = False @@ -1886,6 +1888,7 @@ class PathWidget(QtWidgets.QWidget, ConfigObject): "darwin": "MacOS", "linux": "Linux" } + # TODO be able to save and load with separators platform_separators = { "windows": ";", "darwin": ":", From 575ea828759f22b23ca59213b95b4dc75ae7ba45 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 4 Sep 2020 13:56:32 +0200 Subject: [PATCH 388/813] discard changes and remove overrides works for roots widget --- .../config_setting/widgets/anatomy_inputs.py | 48 +++++++++++++++++-- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index e4f7a6383a..efbcd437ad 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -41,14 +41,13 @@ class AnatomyWidget(QtWidgets.QWidget, ConfigObject): super(AnatomyWidget, self).__init__(parent) self.setObjectName("AnatomyWidget") self._parent = parent + self.key = "anatomy" self._child_state = None self._state = None self._is_group = True - self.key = "anatomy" - self.override_value = NOT_SET self.start_value = NOT_SET self.global_value = NOT_SET @@ -88,7 +87,7 @@ class AnatomyWidget(QtWidgets.QWidget, ConfigObject): self.templates_widget.update_global_values(value) def set_value(self, value, *, global_value=False): - print("* set_value") + raise TypeError("AnatomyWidget does not allow to use `set_value`") def clear_value(self): print("* clear_value") @@ -97,7 +96,6 @@ class AnatomyWidget(QtWidgets.QWidget, ConfigObject): print("* _on_value_change") def update_style(self, is_overriden=None): - print("* update_style") child_modified = self.child_modified child_invalid = self.child_invalid child_state = self.style_state( @@ -154,6 +152,21 @@ class AnatomyWidget(QtWidgets.QWidget, ConfigObject): or self.templates_widget.child_invalid ) + def remove_overrides(self): + self._is_overriden = False + self._is_modified = False + self._was_overriden = False + + self.root_widget.remove_overrides() + self.templates_widget.remove_overrides() + + def discard_changes(self): + self.root_widget.discard_changes() + self.templates_widget.discard_changes() + + self._is_modified = self.child_modified + self._is_overriden = self._was_overriden + def item_value(self): output = {} output.update(self.root_widget.config_value()) @@ -175,6 +188,8 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): self.root_keys = None + self.was_multiroot = NOT_SET + checkbox_widget = QtWidgets.QWidget(self) multiroot_label = QtWidgets.QLabel( "Use multiple roots", checkbox_widget @@ -220,6 +235,7 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): return self.multiroot_checkbox.isChecked() def update_global_values(self, parent_values): + self._is_modified = False if isinstance(parent_values, dict): value = parent_values.get(self.key, NOT_SET) else: @@ -232,12 +248,15 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): is_multiroot = True break + self.was_multiroot = is_multiroot self.set_multiroot(is_multiroot) if is_multiroot: + self.singleroot_widget.update_global_values(NOT_SET) self.multiroot_widget.update_global_values(parent_values) else: self.singleroot_widget.update_global_values(parent_values) + self.multiroot_widget.update_global_values(NOT_SET) def hierarchical_style_update(self): self.singleroot_widget.hierarchical_style_update() @@ -293,6 +312,21 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): else: return self.singleroot_widget.child_invalid + def remove_overrides(self): + self._is_overriden = False + self._is_modified = False + self._was_overriden = False + + self.singleroot_widget.remove_overrides() + self.multiroot_widget.remove_overrides() + + def discard_changes(self): + self.singleroot_widget.discard_changes() + self.multiroot_widget.discard_changes() + + self._is_modified = self.child_modified + self._is_overriden = self._was_overriden + def item_value(self): if self.is_multiroot: return self.multiroot_widget.item_value() @@ -333,5 +367,11 @@ class TemplatesWidget(QtWidgets.QWidget): def child_invalid(self): return False + def remove_overrides(self): + pass + + def discard_changes(self): + pass + TypeToKlass.types["anatomy"] = AnatomyWidget From d6c3d95478f49bb815fce1a3c91df9ee96ee6065 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 4 Sep 2020 14:40:18 +0200 Subject: [PATCH 389/813] added few missing methods to roots widget --- .../config_setting/widgets/anatomy_inputs.py | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index efbcd437ad..4fb01a468e 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -258,6 +258,33 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): self.singleroot_widget.update_global_values(parent_values) self.multiroot_widget.update_global_values(NOT_SET) + def apply_overrides(self, parent_values): + # Make sure this is set to False + self._is_modified = False + self._state = None + self._child_state = None + + value = NOT_SET + if parent_values is not NOT_SET: + value = parent_values.get(self.key, value) + + is_multiroot = False + if isinstance(value, dict): + for _value in value.values(): + if isinstance(_value, dict): + is_multiroot = True + break + + self._is_overriden = value is not NOT_SET + self._was_overriden = bool(self._is_overriden) + + if is_multiroot: + self.singleroot_widget.apply_overrides(NOT_SET) + self.multiroot_widget.apply_overrides(value) + else: + self.singleroot_widget.apply_overrides(value) + self.multiroot_widget.apply_overrides(NOT_SET) + def hierarchical_style_update(self): self.singleroot_widget.hierarchical_style_update() self.multiroot_widget.hierarchical_style_update() @@ -265,6 +292,17 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): def _on_multiroot_checkbox(self): self.set_multiroot(self.is_multiroot) + def _on_value_change(self, item=None): + if self.is_group and self.is_overidable: + self._is_overriden = True + + self._is_modified = ( + self.was_multiroot != self.is_multiroot + or self.child_modified + ) + + self.value_changed.emit(self) + def set_multiroot(self, is_multiroot=None): if is_multiroot is None: is_multiroot = not self.is_multiroot @@ -337,6 +375,7 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): return {self.key: self.item_value()} +# TODO implement class TemplatesWidget(QtWidgets.QWidget): def __init__(self, parent=None): super(TemplatesWidget, self).__init__(parent) @@ -344,6 +383,9 @@ class TemplatesWidget(QtWidgets.QWidget): def update_global_values(self, values): pass + def apply_overrides(self, parent_values): + pass + def hierarchical_style_update(self): pass From 2a34a77d168e49f815adebe80937ca1d40078cdb Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 4 Sep 2020 14:40:56 +0200 Subject: [PATCH 390/813] more value changes reaction implemented --- .../config_setting/widgets/anatomy_inputs.py | 44 +++++++++++++++++-- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index 4fb01a468e..6b174ca575 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -77,6 +77,9 @@ class AnatomyWidget(QtWidgets.QWidget, ConfigObject): self.label_widget = body_widget.label_widget + self.root_widget.multiroot_changed.connect(self._on_multiroot_change) + self.root_widget.value_changed.connect(self._on_value_change) + def update_global_values(self, parent_values): if isinstance(parent_values, dict): value = parent_values.get(self.key, NOT_SET) @@ -86,14 +89,41 @@ class AnatomyWidget(QtWidgets.QWidget, ConfigObject): self.root_widget.update_global_values(value) self.templates_widget.update_global_values(value) + def apply_overrides(self, parent_values): + # Make sure this is set to False + self._state = None + self._child_state = None + + value = NOT_SET + if parent_values is not NOT_SET: + value = parent_values.get(self.key, value) + + self._is_overriden = value is not NOT_SET + + self.root_widget.apply_overrides(value) + self.templates_widget.apply_overrides(value) + + self._was_overriden = bool(self._is_overriden) + def set_value(self, value, *, global_value=False): raise TypeError("AnatomyWidget does not allow to use `set_value`") def clear_value(self): - print("* clear_value") + raise TypeError("AnatomyWidget does not allow to use `clear_value`") + + def _on_multiroot_change(self): + self.update_style() def _on_value_change(self, item=None): - print("* _on_value_change") + if self.ignore_value_changes: + return + + if self.is_overidable: + self._is_overriden = True + + self.hierarchical_style_update() + + self.value_changed.emit(self) def update_style(self, is_overriden=None): child_modified = self.child_modified @@ -154,7 +184,6 @@ class AnatomyWidget(QtWidgets.QWidget, ConfigObject): def remove_overrides(self): self._is_overriden = False - self._is_modified = False self._was_overriden = False self.root_widget.remove_overrides() @@ -164,9 +193,13 @@ class AnatomyWidget(QtWidgets.QWidget, ConfigObject): self.root_widget.discard_changes() self.templates_widget.discard_changes() - self._is_modified = self.child_modified self._is_overriden = self._was_overriden + def overrides(self): + if self.is_overriden: + return self.config_value(), True + return {self.key: {}}, True + def item_value(self): output = {} output.update(self.root_widget.config_value()) @@ -178,6 +211,7 @@ class AnatomyWidget(QtWidgets.QWidget, ConfigObject): class RootsWidget(QtWidgets.QWidget, ConfigObject): multiroot_changed = QtCore.Signal() + value_changed = QtCore.Signal(object) def __init__(self, parent): super(RootsWidget, self).__init__(parent) @@ -227,6 +261,8 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): self.multiroot_widget = multiroot_widget multiroot_checkbox.stateChanged.connect(self._on_multiroot_checkbox) + singleroot_widget.value_changed.connect(self._on_value_change) + multiroot_widget.value_changed.connect(self._on_value_change) self._on_multiroot_checkbox() From d570f287100fb325e8f10ae9c59be93cfab077d4 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 4 Sep 2020 15:05:35 +0200 Subject: [PATCH 391/813] _was_overriden is not changed on remove overrides to keep information about change --- .../config_setting/widgets/anatomy_inputs.py | 12 ++++-------- .../config_setting/config_setting/widgets/inputs.py | 5 ----- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index 6b174ca575..331921ea68 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -98,14 +98,10 @@ class AnatomyWidget(QtWidgets.QWidget, ConfigObject): if parent_values is not NOT_SET: value = parent_values.get(self.key, value) - self._is_overriden = value is not NOT_SET - self.root_widget.apply_overrides(value) self.templates_widget.apply_overrides(value) - self._was_overriden = bool(self._is_overriden) - - def set_value(self, value, *, global_value=False): + def set_value(self, value): raise TypeError("AnatomyWidget does not allow to use `set_value`") def clear_value(self): @@ -184,7 +180,6 @@ class AnatomyWidget(QtWidgets.QWidget, ConfigObject): def remove_overrides(self): self._is_overriden = False - self._was_overriden = False self.root_widget.remove_overrides() self.templates_widget.remove_overrides() @@ -271,7 +266,9 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): return self.multiroot_checkbox.isChecked() def update_global_values(self, parent_values): - self._is_modified = False + self._state = None + self._child_state = None + if isinstance(parent_values, dict): value = parent_values.get(self.key, NOT_SET) else: @@ -296,7 +293,6 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): def apply_overrides(self, parent_values): # Make sure this is set to False - self._is_modified = False self._state = None self._child_state = None diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index d2f7f6cae6..81dad86c25 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -177,7 +177,6 @@ class InputObject(ConfigObject): self.set_value(self.start_value) self._is_overriden = False self._is_modified = False - self._was_overriden = False def apply_overrides(self, parent_values): self._is_modified = False @@ -1546,7 +1545,6 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): def remove_overrides(self): self._is_overriden = False self._is_modified = False - self._was_overriden = False for item in self.input_fields: item.remove_overrides() @@ -1806,7 +1804,6 @@ class DictInvisible(QtWidgets.QWidget, ConfigObject): def remove_overrides(self): self._is_overriden = False self._is_modified = False - self._was_overriden = False for item in self.input_fields: item.remove_overrides() @@ -2102,7 +2099,6 @@ class PathWidget(QtWidgets.QWidget, ConfigObject): def remove_overrides(self): self._is_overriden = False self._is_modified = False - self._was_overriden = False for item in self.input_fields: item.remove_overrides() @@ -2234,7 +2230,6 @@ class DictFormWidget(QtWidgets.QWidget, ConfigObject): def remove_overrides(self): self._is_overriden = False self._is_modified = False - self._was_overriden = False for item in self.input_fields: item.remove_overrides() From 38325b8d7f106296273a4b1ac28212cd33d1ec16 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 4 Sep 2020 15:06:24 +0200 Subject: [PATCH 392/813] AnatomyWidget is not group --- .../config_setting/widgets/anatomy_inputs.py | 27 +++++++------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index 331921ea68..96ad0c786b 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -46,13 +46,6 @@ class AnatomyWidget(QtWidgets.QWidget, ConfigObject): self._child_state = None self._state = None - self._is_group = True - - self.override_value = NOT_SET - self.start_value = NOT_SET - self.global_value = NOT_SET - - self.root_keys = None self.root_widget = RootsWidget(self) self.templates_widget = TemplatesWidget(self) @@ -80,7 +73,13 @@ class AnatomyWidget(QtWidgets.QWidget, ConfigObject): self.root_widget.multiroot_changed.connect(self._on_multiroot_change) self.root_widget.value_changed.connect(self._on_value_change) + def any_parent_is_group(self): + return False + def update_global_values(self, parent_values): + self._state = None + self._child_state = None + if isinstance(parent_values, dict): value = parent_values.get(self.key, NOT_SET) else: @@ -135,17 +134,6 @@ class AnatomyWidget(QtWidgets.QWidget, ConfigObject): self.style().polish(self) self._child_state = child_state - state = self.style_state( - child_invalid, self.is_overriden, self.is_modified - ) - if self._state == state: - return - - self.label_widget.setProperty("state", state) - self.label_widget.style().polish(self.label_widget) - - self._state = state - def hierarchical_style_update(self): self.root_widget.hierarchical_style_update() self.templates_widget.hierarchical_style_update() @@ -310,6 +298,9 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): self._is_overriden = value is not NOT_SET self._was_overriden = bool(self._is_overriden) + self.was_multiroot = is_multiroot + self.set_multiroot(is_multiroot) + if is_multiroot: self.singleroot_widget.apply_overrides(NOT_SET) self.multiroot_widget.apply_overrides(value) From 7c48e2544243e832cde214f837fcc67934706af4 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 4 Sep 2020 16:08:18 +0200 Subject: [PATCH 393/813] apply override pass right values --- .../config_setting/config_setting/widgets/anatomy_inputs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index 96ad0c786b..229dff0bd9 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -303,9 +303,9 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): if is_multiroot: self.singleroot_widget.apply_overrides(NOT_SET) - self.multiroot_widget.apply_overrides(value) + self.multiroot_widget.apply_overrides(parent_values) else: - self.singleroot_widget.apply_overrides(value) + self.singleroot_widget.apply_overrides(parent_values) self.multiroot_widget.apply_overrides(NOT_SET) def hierarchical_style_update(self): From bd6d5999c82ec2f7059707825ceff8787dc0e30e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 4 Sep 2020 16:09:11 +0200 Subject: [PATCH 394/813] is_modified is set right on overrides --- .../config_setting/widgets/inputs.py | 82 +++++++++++++------ 1 file changed, 55 insertions(+), 27 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 81dad86c25..93ce5603aa 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -313,17 +313,15 @@ class BooleanWidget(QtWidgets.QWidget, InputObject): if self.ignore_value_changes: return - _value = self.item_value() - is_modified = None if self.is_overidable: self._is_overriden = True - if self.override_value is not None: - is_modified = _value != self.override_value - if is_modified is None: - is_modified = _value != self.global_value - - self._is_modified = is_modified + if self._is_invalid: + self._is_modified = True + elif self._is_overriden: + self._is_modified = self.item_value() != self.override_value + else: + self._is_modified = self.item_value() != self.global_value self.update_style() @@ -427,10 +425,16 @@ class NumberWidget(QtWidgets.QWidget, InputObject): if self.ignore_value_changes: return - self._is_modified = self.item_value() != self.global_value if self.is_overidable: self._is_overriden = True + if self._is_invalid: + self._is_modified = True + elif self._is_overriden: + self._is_modified = self.item_value() != self.override_value + else: + self._is_modified = self.item_value() != self.global_value + self.update_style() self.value_changed.emit(self) @@ -535,10 +539,16 @@ class TextWidget(QtWidgets.QWidget, InputObject): if self.ignore_value_changes: return - self._is_modified = self.item_value() != self.global_value if self.is_overidable: self._is_overriden = True + if self._is_invalid: + self._is_modified = True + elif self._is_overriden: + self._is_modified = self.item_value() != self.override_value + else: + self._is_modified = self.item_value() != self.global_value + self.update_style() self.value_changed.emit(self) @@ -640,10 +650,16 @@ class PathInputWidget(QtWidgets.QWidget, InputObject): if self.ignore_value_changes: return - self._is_modified = self.item_value() != self.global_value if self.is_overidable: self._is_overriden = True + if self._is_invalid: + self._is_modified = True + elif self._is_overriden: + self._is_modified = self.item_value() != self.override_value + else: + self._is_modified = self.item_value() != self.global_value + self.update_style() self.value_changed.emit(self) @@ -803,7 +819,9 @@ class RawJsonWidget(QtWidgets.QWidget, InputObject): if self.ignore_value_changes: return - self._is_invalid = self.text_input.has_invalid_value() + if self.is_overidable: + self._is_overriden = True + if self._is_invalid: self._is_modified = True elif self._is_overriden: @@ -811,9 +829,6 @@ class RawJsonWidget(QtWidgets.QWidget, InputObject): else: self._is_modified = self.item_value() != self.global_value - if self.is_overidable: - self._is_overriden = True - self.update_style() self.value_changed.emit(self) @@ -1017,10 +1032,17 @@ class ListWidget(QtWidgets.QWidget, InputObject): def _on_value_change(self, item=None): if self.ignore_value_changes: return - self._is_modified = self.item_value() != self.global_value + if self.is_overidable: self._is_overriden = True + if self._is_invalid: + self._is_modified = True + elif self._is_overriden: + self._is_modified = self.item_value() != self.override_value + else: + self._is_modified = self.item_value() != self.global_value + self.update_style() self.value_changed.emit(self) @@ -1349,15 +1371,17 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): if self.is_overidable: self._is_overriden = True - if self.is_overriden: + if self._is_invalid: + self._is_modified = True + elif self._is_overriden: self._is_modified = self.item_value() != self.override_value else: self._is_modified = self.item_value() != self.global_value - self.value_changed.emit(self) - self.update_style() + self.value_changed.emit(self) + def hierarchical_style_update(self): for input_field in self.input_fields: input_field.hierarchical_style_update() @@ -2019,7 +2043,6 @@ class PathWidget(QtWidgets.QWidget, ConfigObject): # Make sure this is set to False self._state = None self._child_state = None - self._is_modified = False override_values = NOT_SET if parent_values is not NOT_SET: override_values = parent_values.get(self.key, override_values) @@ -2037,6 +2060,7 @@ class PathWidget(QtWidgets.QWidget, ConfigObject): and self.is_overidable and self.child_overriden ) + self._is_modified = False self._was_overriden = bool(self._is_overriden) def set_value(self, value): @@ -2060,16 +2084,20 @@ class PathWidget(QtWidgets.QWidget, ConfigObject): if self.ignore_value_changes: return - self._is_modified = self.item_value() != self.global_value - if self.is_group: - if self.is_overidable: - self._is_overriden = True - self.hierarchical_style_update() + if self.is_overidable: + self._is_overriden = True + + if self._is_invalid: + self._is_modified = True + elif self._is_overriden: + self._is_modified = self.item_value() != self.override_value + else: + self._is_modified = self.item_value() != self.global_value + + self.hierarchical_style_update() self.value_changed.emit(self) - self.update_style() - def update_style(self, is_overriden=None): child_modified = self.child_modified child_invalid = self.child_invalid From 0f0dd751e8e088ea286ee9e708a1252ff1e57bad Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 4 Sep 2020 16:10:16 +0200 Subject: [PATCH 395/813] is_multiroot deduction is better --- .../config_setting/widgets/anatomy_inputs.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index 229dff0bd9..fb95f17785 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -288,12 +288,15 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): if parent_values is not NOT_SET: value = parent_values.get(self.key, value) - is_multiroot = False - if isinstance(value, dict): - for _value in value.values(): - if isinstance(_value, dict): - is_multiroot = True - break + if value is NOT_SET: + is_multiroot = self.global_is_multiroot + else: + is_multiroot = False + if isinstance(value, dict): + for _value in value.values(): + if isinstance(_value, dict): + is_multiroot = True + break self._is_overriden = value is not NOT_SET self._was_overriden = bool(self._is_overriden) From 0e95344bfc5f321348c8f3c30a6bd1ca59f933ee Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 4 Sep 2020 16:11:01 +0200 Subject: [PATCH 396/813] _on_value_changed ignore changes of not current roots widget --- .../config_setting/widgets/anatomy_inputs.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index fb95f17785..aaea588ab2 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -205,6 +205,7 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): self.root_keys = None + self.global_is_multiroot = False self.was_multiroot = NOT_SET checkbox_widget = QtWidgets.QWidget(self) @@ -219,6 +220,7 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): path_widget_data = { "key": "roots", + "multipath": False, "multiplatform": True, "label": "Roots" } @@ -269,6 +271,7 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): is_multiroot = True break + self.global_is_multiroot = is_multiroot self.was_multiroot = is_multiroot self.set_multiroot(is_multiroot) @@ -283,6 +286,7 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): # Make sure this is set to False self._state = None self._child_state = None + self._is_modified = False value = NOT_SET if parent_values is not NOT_SET: @@ -319,6 +323,12 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): self.set_multiroot(self.is_multiroot) def _on_value_change(self, item=None): + if ( + (self.is_multiroot and item != self.multiroot_widget) + or (not self.is_multiroot and item != self.singleroot_widget) + ): + return + if self.is_group and self.is_overidable: self._is_overriden = True From 2d77236f4c05fd466ee28dc1eb17063ce22cd70b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 4 Sep 2020 16:17:52 +0200 Subject: [PATCH 397/813] skip was removed on removind overrides action --- .../config_setting/config_setting/widgets/anatomy_inputs.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index aaea588ab2..f3fc1bfd1a 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -153,9 +153,7 @@ class AnatomyWidget(QtWidgets.QWidget, ConfigObject): @property def child_overriden(self): return ( - self.root_widget.is_overriden - or self.root_widget.child_overriden - or self.templates_widget.is_overriden + self.root_widget.child_overriden or self.templates_widget.child_overriden ) @@ -389,7 +387,6 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): def remove_overrides(self): self._is_overriden = False self._is_modified = False - self._was_overriden = False self.singleroot_widget.remove_overrides() self.multiroot_widget.remove_overrides() From b9db80e1bb7051642fa35e85100199360551623e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 4 Sep 2020 16:21:50 +0200 Subject: [PATCH 398/813] discard changes and remove overrides set multiroot widgets right way --- .../config_setting/widgets/anatomy_inputs.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index f3fc1bfd1a..8e7bb4bb8a 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -388,10 +388,18 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): self._is_overriden = False self._is_modified = False + self.set_multiroot(self.global_is_multiroot) + self.singleroot_widget.remove_overrides() self.multiroot_widget.remove_overrides() def discard_changes(self): + is_overriden = bool(self._is_overriden) + if is_overriden: + self.set_multiroot(self.was_multiroot) + else: + self.set_multiroot(self.global_is_multiroot) + self.singleroot_widget.discard_changes() self.multiroot_widget.discard_changes() From 89e96b6cfeb04d9b1808653f07bea81aaceade00 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Sep 2020 09:21:00 +0200 Subject: [PATCH 399/813] modifiable widget can be used as widget --- .../config_setting/widgets/inputs.py | 42 ++++++++++--------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 93ce5603aa..10dd9913b8 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1292,32 +1292,33 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): main_layout.setContentsMargins(5, 5, 0, 5) main_layout.setSpacing(0) - body_widget = ExpandingWidget(input_data["label"], self) - - main_layout.addWidget(body_widget) - content_widget = QtWidgets.QWidget(self) content_layout = QtWidgets.QVBoxLayout(content_widget) content_layout.setContentsMargins(3, 3, 0, 3) - body_widget.set_content_widget(content_widget) + if as_widget: + main_layout.addWidget(content_widget) + else: + body_widget = ExpandingWidget(input_data["label"], self) + main_layout.addWidget(body_widget) + body_widget.set_content_widget(content_widget) + + self.body_widget = body_widget + self.label_widget = body_widget.label_widget + + expandable = input_data.get("expandable", True) + if not expandable: + body_widget.hide_toolbox(hide_content=False) + else: + expanded = input_data.get("expanded", False) + if expanded: + body_widget.toggle_content() - self.body_widget = body_widget self.content_widget = content_widget self.content_layout = content_layout - self.label_widget = body_widget.label_widget - self.setAttribute(QtCore.Qt.WA_StyledBackground) - expandable = input_data.get("expandable", True) - if not expandable: - body_widget.hide_toolbox(hide_content=False) - else: - expanded = input_data.get("expanded", False) - if expanded: - body_widget.toggle_content() - self.object_type = input_data["object_type"] self.default_value = input_data.get("default", NOT_SET) self.input_modifiers = input_data.get("input_modifiers") or {} @@ -1402,8 +1403,9 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): self.setProperty("state", child_state) self.style().polish(self) - self.label_widget.setProperty("state", state) - self.label_widget.style().polish(self.label_widget) + if not self._as_widget: + self.label_widget.setProperty("state", state) + self.label_widget.style().polish(self.label_widget) self._state = state @@ -2044,7 +2046,9 @@ class PathWidget(QtWidgets.QWidget, ConfigObject): self._state = None self._child_state = None override_values = NOT_SET - if parent_values is not NOT_SET: + if self._as_widget: + override_values = parent_values + elif parent_values is not NOT_SET: override_values = parent_values.get(self.key, override_values) self._is_overriden = override_values is not NOT_SET From 0ca9cf309c9e39a4b82d1efb28f66f370bc47bc1 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Sep 2020 09:21:17 +0200 Subject: [PATCH 400/813] widgets in roots are used as widgets --- .../config_setting/widgets/anatomy_inputs.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index 8e7bb4bb8a..c6c06a42ae 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -222,7 +222,7 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): "multiplatform": True, "label": "Roots" } - singleroot_widget = PathWidget(path_widget_data, self) + singleroot_widget = PathWidget(path_widget_data, self, as_widget=True) multiroot_data = { "key": "roots", "label": "Roots", @@ -232,7 +232,7 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): "multiplatform": True } } - multiroot_widget = ModifiableDict(multiroot_data, self) + multiroot_widget = ModifiableDict(multiroot_data, self, as_widget=True) main_layout = QtWidgets.QVBoxLayout(self) main_layout.addWidget(checkbox_widget) @@ -275,9 +275,9 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): if is_multiroot: self.singleroot_widget.update_global_values(NOT_SET) - self.multiroot_widget.update_global_values(parent_values) + self.multiroot_widget.update_global_values(value) else: - self.singleroot_widget.update_global_values(parent_values) + self.singleroot_widget.update_global_values(value) self.multiroot_widget.update_global_values(NOT_SET) def apply_overrides(self, parent_values): @@ -308,9 +308,9 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): if is_multiroot: self.singleroot_widget.apply_overrides(NOT_SET) - self.multiroot_widget.apply_overrides(parent_values) + self.multiroot_widget.apply_overrides(value) else: - self.singleroot_widget.apply_overrides(parent_values) + self.singleroot_widget.apply_overrides(value) self.multiroot_widget.apply_overrides(NOT_SET) def hierarchical_style_update(self): From ed6c1609dca1b0167af3f6b2e89307da94637cb1 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Sep 2020 10:57:54 +0200 Subject: [PATCH 401/813] fixed discard changes --- .../config_setting/widgets/inputs.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 10dd9913b8..a9ca88c171 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -201,10 +201,11 @@ class InputObject(ConfigObject): self.set_value(value) def discard_changes(self): + self._is_overriden = self._was_overriden if ( self.is_overidable + and self._was_overriden and self.override_value is not NOT_SET - and self._was_overriden is True ): self.set_value(self.override_value) else: @@ -1575,11 +1576,13 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): item.remove_overrides() def discard_changes(self): + self._is_overriden = self._was_overriden + self._is_modified = False + for item in self.input_fields: item.discard_changes() self._is_modified = self.child_modified - self._is_overriden = self._was_overriden def set_as_overriden(self): if self.is_overriden: @@ -1834,11 +1837,13 @@ class DictInvisible(QtWidgets.QWidget, ConfigObject): item.remove_overrides() def discard_changes(self): + self._is_modified = False + self._is_overriden = self._was_overriden + for item in self.input_fields: item.discard_changes() self._is_modified = self.child_modified - self._is_overriden = self._was_overriden def set_as_overriden(self): if self.is_overriden: @@ -2135,11 +2140,13 @@ class PathWidget(QtWidgets.QWidget, ConfigObject): item.remove_overrides() def discard_changes(self): + self._is_modified = False + self._is_overriden = self._was_overriden + for input_field in self.input_fields: input_field.discard_changes() self._is_modified = self.child_modified - self._is_overriden = self._was_overriden @property def child_modified(self): @@ -2253,11 +2260,13 @@ class DictFormWidget(QtWidgets.QWidget, ConfigObject): item.apply_overrides(parent_values) def discard_changes(self): + self._is_modified = False + self._is_overriden = self._was_overriden + for item in self.input_fields: item.discard_changes() self._is_modified = self.child_modified - self._is_overriden = self._was_overriden def remove_overrides(self): self._is_overriden = False From 93658f8a3ea7a8f2226fa7b48c765f0db800c227 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Sep 2020 11:02:55 +0200 Subject: [PATCH 402/813] added representation of object to not implemented exception --- pype/tools/config_setting/config_setting/widgets/widgets.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/widgets.py b/pype/tools/config_setting/config_setting/widgets/widgets.py index 42ca49d600..e547b8181a 100644 --- a/pype/tools/config_setting/config_setting/widgets/widgets.py +++ b/pype/tools/config_setting/config_setting/widgets/widgets.py @@ -353,10 +353,12 @@ class AbstractConfigObject: def set_as_overriden(self): raise NotImplementedError( - "Method `set_as_overriden` not implemented!" + "{} Method `set_as_overriden` not implemented!".format(repr(self)) ) def hierarchical_style_update(self): raise NotImplementedError( - "Method `hierarchical_style_update` not implemented!" + "{} Method `hierarchical_style_update` not implemented!".format( + repr(self) + ) ) From 208075acfaf87bdb460a423edbc5984928bbe7a3 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Sep 2020 12:12:36 +0200 Subject: [PATCH 403/813] list and dict items skip right mouse release --- pype/tools/config_setting/config_setting/widgets/inputs.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index a9ca88c171..b5e0b2c5b6 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -932,6 +932,9 @@ class ListItem(QtWidgets.QWidget, ConfigObject): def child_overriden(self): return self.value_input.child_overriden + def mouseReleaseEvent(self, event): + return QtWidgets.QWidget.mouseReleaseEvent(self, event) + class ListWidget(QtWidgets.QWidget, InputObject): value_changed = QtCore.Signal(object) @@ -1256,6 +1259,9 @@ class ModifiableDictItem(QtWidgets.QWidget, ConfigObject): return {} return {key: value} + def mouseReleaseEvent(self, event): + return QtWidgets.QWidget.mouseReleaseEvent(self, event) + class ModifiableDict(QtWidgets.QWidget, InputObject): # Should be used only for dictionary with one datatype as value From 78c68beb3fd2babcf9f20dd5ae9f101c7377995d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Sep 2020 12:13:55 +0200 Subject: [PATCH 404/813] removed signal multiroot_changed --- .../config_setting/widgets/anatomy_inputs.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index c6c06a42ae..0b825b90e6 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -70,7 +70,6 @@ class AnatomyWidget(QtWidgets.QWidget, ConfigObject): self.label_widget = body_widget.label_widget - self.root_widget.multiroot_changed.connect(self._on_multiroot_change) self.root_widget.value_changed.connect(self._on_value_change) def any_parent_is_group(self): @@ -106,9 +105,6 @@ class AnatomyWidget(QtWidgets.QWidget, ConfigObject): def clear_value(self): raise TypeError("AnatomyWidget does not allow to use `clear_value`") - def _on_multiroot_change(self): - self.update_style() - def _on_value_change(self, item=None): if self.ignore_value_changes: return @@ -191,7 +187,6 @@ class AnatomyWidget(QtWidgets.QWidget, ConfigObject): class RootsWidget(QtWidgets.QWidget, ConfigObject): - multiroot_changed = QtCore.Signal() value_changed = QtCore.Signal(object) def __init__(self, parent): @@ -347,7 +342,7 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): self.singleroot_widget.setVisible(not is_multiroot) self.multiroot_widget.setVisible(is_multiroot) - self.multiroot_changed.emit() + self._on_value_change() @property def is_modified(self): From 8ec6d0cba5d8793cf2fe2086b696db7bf6c5944c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Sep 2020 12:14:22 +0200 Subject: [PATCH 405/813] removed label from root widgets --- .../config_setting/config_setting/widgets/anatomy_inputs.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index 0b825b90e6..e4c555138a 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -214,13 +214,11 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): path_widget_data = { "key": "roots", "multipath": False, - "multiplatform": True, - "label": "Roots" + "multiplatform": True } singleroot_widget = PathWidget(path_widget_data, self, as_widget=True) multiroot_data = { "key": "roots", - "label": "Roots", "object_type": "path-widget", "expandable": False, "input_modifiers": { From 04d8e6d2420139f3bbb43adb4cf082d16e836694 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Sep 2020 12:20:08 +0200 Subject: [PATCH 406/813] any_parent_is_group is real attribute not function --- .../config_setting/config_setting/widgets/anatomy_inputs.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index e4c555138a..2f1f59c31d 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -46,6 +46,8 @@ class AnatomyWidget(QtWidgets.QWidget, ConfigObject): self._child_state = None self._state = None + self.any_parent_is_group = False + self.root_widget = RootsWidget(self) self.templates_widget = TemplatesWidget(self) @@ -72,9 +74,6 @@ class AnatomyWidget(QtWidgets.QWidget, ConfigObject): self.root_widget.value_changed.connect(self._on_value_change) - def any_parent_is_group(self): - return False - def update_global_values(self, parent_values): self._state = None self._child_state = None From d0b05421f722397c76e8060ffb0c898742e8a489 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Sep 2020 12:21:00 +0200 Subject: [PATCH 407/813] roots widget is expandable --- .../config_setting/widgets/anatomy_inputs.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index 2f1f59c31d..6a7906d46e 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -226,11 +226,21 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): } multiroot_widget = ModifiableDict(multiroot_data, self, as_widget=True) - main_layout = QtWidgets.QVBoxLayout(self) - main_layout.addWidget(checkbox_widget) - main_layout.addWidget(singleroot_widget) - main_layout.addWidget(multiroot_widget) + body_widget = ExpandingWidget("Roots", self) + content_widget = QtWidgets.QWidget(body_widget) + contnet_layout = QtWidgets.QVBoxLayout(content_widget) + contnet_layout.addWidget(checkbox_widget) + contnet_layout.addWidget(singleroot_widget) + contnet_layout.addWidget(multiroot_widget) + + body_widget.set_content_widget(content_widget) + self.label_widget = body_widget.label_widget + + main_layout = QtWidgets.QVBoxLayout(self) + main_layout.addWidget(body_widget) + + self.multiroot_label = multiroot_label self.multiroot_checkbox = multiroot_checkbox self.singleroot_widget = singleroot_widget self.multiroot_widget = multiroot_widget From 4e0afbd300b48b0bfcdfd465b92f9e84fa4761e0 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Sep 2020 12:21:22 +0200 Subject: [PATCH 408/813] added mutliroot state for multiroot checkbox label --- .../config_setting/widgets/anatomy_inputs.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index 6a7906d46e..81a7357c4d 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -192,10 +192,13 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): super(RootsWidget, self).__init__(parent) self.setObjectName("RootsWidget") self._parent = parent - self._is_group = True self.key = "roots" - self.root_keys = None + self._state = None + self._multiroot_state = None + + self._is_group = True + self.any_parent_is_group = False self.global_is_multiroot = False self.was_multiroot = NOT_SET @@ -257,7 +260,7 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): def update_global_values(self, parent_values): self._state = None - self._child_state = None + self._multiroot_state = None if isinstance(parent_values, dict): value = parent_values.get(self.key, NOT_SET) @@ -285,7 +288,7 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): def apply_overrides(self, parent_values): # Make sure this is set to False self._state = None - self._child_state = None + self._multiroot_state = None self._is_modified = False value = NOT_SET From 41d92b03afb3e89610562493058ab99de73c426f Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Sep 2020 12:22:48 +0200 Subject: [PATCH 409/813] roots value change can ignore changes and do what should if item is not defined --- .../config_setting/widgets/anatomy_inputs.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index 81a7357c4d..c34418be87 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -326,7 +326,12 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): self.set_multiroot(self.is_multiroot) def _on_value_change(self, item=None): - if ( + if self.ignore_value_changes: + return + + if item is None: + pass + elif ( (self.is_multiroot and item != self.multiroot_widget) or (not self.is_multiroot and item != self.singleroot_widget) ): From 2d778ae37a43074f06be89be5e2bdbfb6770b94d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Sep 2020 12:24:01 +0200 Subject: [PATCH 410/813] discard changes sets right multiroot on discard changes --- .../config_setting/config_setting/widgets/anatomy_inputs.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index c34418be87..5817d67274 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -404,8 +404,9 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): self.multiroot_widget.remove_overrides() def discard_changes(self): - is_overriden = bool(self._is_overriden) - if is_overriden: + self._is_overriden = self._was_overriden + self._is_modified = False + if self._is_overriden: self.set_multiroot(self.was_multiroot) else: self.set_multiroot(self.global_is_multiroot) @@ -414,7 +415,6 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): self.multiroot_widget.discard_changes() self._is_modified = self.child_modified - self._is_overriden = self._was_overriden def item_value(self): if self.is_multiroot: From d35c2b62ff3edcd7a2ad0660fefc6dfdc697746c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Sep 2020 12:24:13 +0200 Subject: [PATCH 411/813] roots widget has update style --- .../config_setting/widgets/anatomy_inputs.py | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index 5817d67274..df5be5108d 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -321,6 +321,37 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): def hierarchical_style_update(self): self.singleroot_widget.hierarchical_style_update() self.multiroot_widget.hierarchical_style_update() + self.update_style() + + def update_style(self): + multiroot_state = self.style_state( + False, + self.is_overriden, + self.was_multiroot and not self.is_multiroot + ) + if multiroot_state != self._multiroot_state: + self.multiroot_label.setProperty("state", multiroot_state) + self.multiroot_label.style().polish(self.multiroot_label) + self._multiroot_state = multiroot_state + + state = self.style_state( + self.is_invalid, self.is_overriden, self.is_modified + ) + if self._state == state: + return + + if state: + child_state = "child-{}".format(state) + else: + child_state = "" + + self.setProperty("state", child_state) + self.style().polish(self) + + self.label_widget.setProperty("state", state) + self.label_widget.style().polish(self.label_widget) + + self._state = state def _on_multiroot_checkbox(self): self.set_multiroot(self.is_multiroot) From 9c1e18cc103959b29788f15784136d1ff7b32bd4 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Sep 2020 12:24:30 +0200 Subject: [PATCH 412/813] multiroot widget apply parent values --- .../config_setting/config_setting/widgets/anatomy_inputs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index df5be5108d..85f562eb8b 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -313,7 +313,7 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): if is_multiroot: self.singleroot_widget.apply_overrides(NOT_SET) - self.multiroot_widget.apply_overrides(value) + self.multiroot_widget.apply_overrides(parent_values) else: self.singleroot_widget.apply_overrides(value) self.multiroot_widget.apply_overrides(NOT_SET) From ec80578304e9f61affee1f63b4ff7b80ebe295e8 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Sep 2020 14:15:02 +0200 Subject: [PATCH 413/813] anatomy widget does not care about is overriden --- .../config_setting/widgets/anatomy_inputs.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index 85f562eb8b..e918c97bf8 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -108,9 +108,6 @@ class AnatomyWidget(QtWidgets.QWidget, ConfigObject): if self.ignore_value_changes: return - if self.is_overidable: - self._is_overriden = True - self.hierarchical_style_update() self.value_changed.emit(self) @@ -160,8 +157,6 @@ class AnatomyWidget(QtWidgets.QWidget, ConfigObject): ) def remove_overrides(self): - self._is_overriden = False - self.root_widget.remove_overrides() self.templates_widget.remove_overrides() @@ -169,8 +164,6 @@ class AnatomyWidget(QtWidgets.QWidget, ConfigObject): self.root_widget.discard_changes() self.templates_widget.discard_changes() - self._is_overriden = self._was_overriden - def overrides(self): if self.is_overriden: return self.config_value(), True @@ -305,9 +298,6 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): is_multiroot = True break - self._is_overriden = value is not NOT_SET - self._was_overriden = bool(self._is_overriden) - self.was_multiroot = is_multiroot self.set_multiroot(is_multiroot) From 964241286bc0d7ec4b884413268ddfcd7750a7b5 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Sep 2020 14:16:01 +0200 Subject: [PATCH 414/813] roots widget cares about is overriden and fixed multiroot state --- .../config_setting/config_setting/widgets/anatomy_inputs.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index e918c97bf8..a1a0879dc2 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -302,12 +302,16 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): self.set_multiroot(is_multiroot) if is_multiroot: + self._is_overriden = parent_values is not NOT_SET self.singleroot_widget.apply_overrides(NOT_SET) self.multiroot_widget.apply_overrides(parent_values) else: + self._is_overriden = value is not NOT_SET self.singleroot_widget.apply_overrides(value) self.multiroot_widget.apply_overrides(NOT_SET) + self._was_overriden = bool(self._is_overriden) + def hierarchical_style_update(self): self.singleroot_widget.hierarchical_style_update() self.multiroot_widget.hierarchical_style_update() @@ -317,7 +321,7 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): multiroot_state = self.style_state( False, self.is_overriden, - self.was_multiroot and not self.is_multiroot + self.was_multiroot != self.is_multiroot ) if multiroot_state != self._multiroot_state: self.multiroot_label.setProperty("state", multiroot_state) From 5f6687cd9687bcdba276d686ab8823cd8e800075 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Sep 2020 18:11:57 +0200 Subject: [PATCH 415/813] inputs can have different parent widget than input parent --- .../config_setting/widgets/inputs.py | 83 +++++++++++++------ 1 file changed, 58 insertions(+), 25 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index b5e0b2c5b6..ea40b6d25e 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -248,9 +248,12 @@ class BooleanWidget(QtWidgets.QWidget, InputObject): value_changed = QtCore.Signal(object) def __init__( - self, input_data, parent, as_widget=False, label_widget=None + self, input_data, parent, + as_widget=False, label_widget=None, parent_widget=None ): - super(BooleanWidget, self).__init__(parent) + if parent_widget is None: + parent_widget = parent + super(BooleanWidget, self).__init__(parent_widget) self._parent = parent self._as_widget = as_widget @@ -353,9 +356,12 @@ class NumberWidget(QtWidgets.QWidget, InputObject): input_modifiers = ("minimum", "maximum", "decimal") def __init__( - self, input_data, parent, as_widget=False, label_widget=None + self, input_data, parent, + as_widget=False, label_widget=None, parent_widget=None ): - super(NumberWidget, self).__init__(parent) + if parent_widget is None: + parent_widget = parent + super(NumberWidget, self).__init__(parent_widget) self._parent = parent self._as_widget = as_widget @@ -465,9 +471,12 @@ class TextWidget(QtWidgets.QWidget, InputObject): value_changed = QtCore.Signal(object) def __init__( - self, input_data, parent, as_widget=False, label_widget=None + self, input_data, parent, + as_widget=False, label_widget=None, parent_widget=None ): - super(TextWidget, self).__init__(parent) + if parent_widget is None: + parent_widget = parent + super(TextWidget, self).__init__(parent_widget) self._parent = parent self._as_widget = as_widget @@ -582,9 +591,12 @@ class PathInputWidget(QtWidgets.QWidget, InputObject): value_changed = QtCore.Signal(object) def __init__( - self, input_data, parent, as_widget=False, label_widget=None + self, input_data, parent, + as_widget=False, label_widget=None, parent_widget=None ): - super(PathInputWidget, self).__init__(parent) + if parent_widget is None: + parent_widget = parent + super(PathInputWidget, self).__init__(parent_widget) self._parent = parent self._as_widget = as_widget @@ -742,9 +754,12 @@ class RawJsonWidget(QtWidgets.QWidget, InputObject): value_changed = QtCore.Signal(object) def __init__( - self, input_data, parent, as_widget=False, label_widget=None + self, input_data, parent, + as_widget=False, label_widget=None, parent_widget=None ): - super(RawJsonWidget, self).__init__(parent) + if parent_widget is None: + parent_widget = parent + super(RawJsonWidget, self).__init__(parent_widget) self._parent = parent self._as_widget = as_widget @@ -940,9 +955,12 @@ class ListWidget(QtWidgets.QWidget, InputObject): value_changed = QtCore.Signal(object) def __init__( - self, input_data, parent, as_widget=False, label_widget=None + self, input_data, parent, + as_widget=False, label_widget=None, parent_widget=None ): - super(ListWidget, self).__init__(parent) + if parent_widget is None: + parent_widget = parent + super(ListWidget, self).__init__(parent_widget) self.setObjectName("ListWidget") self._parent = parent @@ -1269,9 +1287,12 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): value_changed = QtCore.Signal(object) def __init__( - self, input_data, parent, as_widget=False, label_widget=None + self, input_data, parent, + as_widget=False, label_widget=None, parent_widget=None ): - super(ModifiableDict, self).__init__(parent) + if parent_widget is None: + parent_widget = parent + super(ModifiableDict, self).__init__(parent_widget) self.setObjectName("ModifiableDict") self._parent = parent @@ -1483,14 +1504,17 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): value_changed = QtCore.Signal(object) def __init__( - self, input_data, parent, as_widget=False, label_widget=None + self, input_data, parent, + as_widget=False, label_widget=None, parent_widget=None ): if as_widget: raise TypeError("Can't use \"{}\" as widget item.".format( self.__class__.__name__ )) - super(DictWidget, self).__init__(parent) + if parent_widget is None: + parent_widget = parent + super(DictWidget, self).__init__(parent_widget) self.setObjectName("DictWidget") self._state = None @@ -1743,8 +1767,14 @@ class DictInvisible(QtWidgets.QWidget, ConfigObject): allow_actions = False def __init__( - self, input_data, parent, as_widget=False, label_widget=None + self, input_data, parent, + as_widget=False, label_widget=None, parent_widget=None ): + if parent_widget is None: + parent_widget = parent + super(DictInvisible, self).__init__(parent_widget) + self.setObjectName("DictInvisible") + self._parent = parent any_parent_is_group = parent.is_group @@ -1754,9 +1784,6 @@ class DictInvisible(QtWidgets.QWidget, ConfigObject): self.any_parent_is_group = any_parent_is_group self._is_group = input_data.get("is_group", False) - super(DictInvisible, self).__init__(parent) - self.setObjectName("DictInvisible") - self.setAttribute(QtCore.Qt.WA_StyledBackground) layout = QtWidgets.QVBoxLayout(self) @@ -1930,9 +1957,12 @@ class PathWidget(QtWidgets.QWidget, ConfigObject): } def __init__( - self, input_data, parent, as_widget=False, label_widget=None + self, input_data, parent, + as_widget=False, label_widget=None, parent_widget=None ): - super(PathWidget, self).__init__(parent) + if parent_widget is None: + parent_widget = parent + super(PathWidget, self).__init__(parent_widget) self._parent = parent self._state = None @@ -2214,8 +2244,13 @@ class DictFormWidget(QtWidgets.QWidget, ConfigObject): allow_actions = False def __init__( - self, input_data, parent, as_widget=False, label_widget=None + self, input_data, parent, + as_widget=False, label_widget=None, parent_widget=None ): + if parent_widget is None: + parent_widget = parent + super(DictFormWidget, self).__init__(parent_widget) + self._parent = parent any_parent_is_group = parent.is_group @@ -2226,8 +2261,6 @@ class DictFormWidget(QtWidgets.QWidget, ConfigObject): self._is_group = False - super(DictFormWidget, self).__init__(parent) - self.input_fields = [] self.content_layout = QtWidgets.QFormLayout(self) From 4b63222ac76bcbd6805168125905cb56a98cc276 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Sep 2020 18:20:15 +0200 Subject: [PATCH 416/813] fixed name of content layour in roots --- .../config_setting/widgets/anatomy_inputs.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index a1a0879dc2..eec6bf2a13 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -225,10 +225,10 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): body_widget = ExpandingWidget("Roots", self) content_widget = QtWidgets.QWidget(body_widget) - contnet_layout = QtWidgets.QVBoxLayout(content_widget) - contnet_layout.addWidget(checkbox_widget) - contnet_layout.addWidget(singleroot_widget) - contnet_layout.addWidget(multiroot_widget) + content_layout = QtWidgets.QVBoxLayout(content_widget) + content_layout.addWidget(checkbox_widget) + content_layout.addWidget(singleroot_widget) + content_layout.addWidget(multiroot_widget) body_widget.set_content_widget(content_widget) self.label_widget = body_widget.label_widget From f8d925f7121a2ebc211cd89e9c666acf3af03261 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Sep 2020 18:20:32 +0200 Subject: [PATCH 417/813] templates widget has something in --- .../config_setting/widgets/anatomy_inputs.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index eec6bf2a13..a3d3e74e48 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -456,6 +456,20 @@ class TemplatesWidget(QtWidgets.QWidget): def __init__(self, parent=None): super(TemplatesWidget, self).__init__(parent) + body_widget = ExpandingWidget("Templates", self) + content_widget = QtWidgets.QWidget(body_widget) + body_widget.set_content_widget(content_widget) + content_layout = QtWidgets.QVBoxLayout(content_widget) + + label = QtWidgets.QLabel("Nothing yet", content_widget) + content_layout.addWidget(label) + + layout = QtWidgets.QVBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(0) + + layout.addWidget(body_widget) + def update_global_values(self, values): pass From 35482ef9737d254ec62ca951ec5320a0b996765a Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Sep 2020 18:21:08 +0200 Subject: [PATCH 418/813] item_value o dictionary return all keys event if are empty --- pype/tools/config_setting/config_setting/widgets/inputs.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index ea40b6d25e..3b40775718 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1440,9 +1440,7 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): def item_value(self): output = {} for item in self.input_fields: - item_value = item.config_value() - if item_value: - output.update(item_value) + output.update(item.config_value()) return output def add_row(self, row=None, key=None, value=None, is_empty=False): From a4424f6f5099c624ab2bb91941877c346ed01888 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Sep 2020 18:28:37 +0200 Subject: [PATCH 419/813] dictionary item has key_vlaue instead of _key --- .../tools/config_setting/config_setting/widgets/inputs.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 3b40775718..ad01ea2ad9 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1206,16 +1206,16 @@ class ModifiableDictItem(QtWidgets.QWidget, ConfigObject): self.value_input.value_changed.connect(self._on_value_change) # TODO This doesn't make sence! - self.default_key = self._key() self.global_value = self.value_input.item_value() + self.origin_key = self.key_value() self.override_key = NOT_SET self.override_value = NOT_SET + def key_value(self): + return self.key_input.text() self.is_single = False - def _key(self): - return self.key_input.text() def _on_value_change(self, item=None): self.update_style() @@ -1245,7 +1245,7 @@ class ModifiableDictItem(QtWidgets.QWidget, ConfigObject): return self._parent.any_parent_is_group def is_key_modified(self): - return self._key() != self.default_key + return self.key_value() != self.origin_key def is_value_modified(self): return self.value_input.is_modified From 147f3f29d99f91e0786fff2549e1f7ee16b6f2be Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Sep 2020 18:29:31 +0200 Subject: [PATCH 420/813] removed unnecessary attributes --- .../config_setting/config_setting/widgets/inputs.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index ad01ea2ad9..9f45a2211c 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1166,6 +1166,9 @@ class ModifiableDictItem(QtWidgets.QWidget, ConfigObject): self._parent = config_parent + self.is_single = False + self.is_key_duplicated = False + layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(3) @@ -1205,16 +1208,11 @@ class ModifiableDictItem(QtWidgets.QWidget, ConfigObject): self.key_input.textChanged.connect(self._on_value_change) self.value_input.value_changed.connect(self._on_value_change) - # TODO This doesn't make sence! - self.global_value = self.value_input.item_value() self.origin_key = self.key_value() - self.override_key = NOT_SET - self.override_value = NOT_SET def key_value(self): return self.key_input.text() - self.is_single = False def _on_value_change(self, item=None): @@ -1273,8 +1271,6 @@ class ModifiableDictItem(QtWidgets.QWidget, ConfigObject): def config_value(self): key = self.key_input.text() value = self.value_input.item_value() - if not key: - return {} return {key: value} def mouseReleaseEvent(self, event): From a7d6eabe325daed58109f4b325adf737fa2b239f Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Sep 2020 18:29:46 +0200 Subject: [PATCH 421/813] dict item has set_values --- .../config_setting/config_setting/widgets/inputs.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 9f45a2211c..21ee1fc6e8 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1219,6 +1219,11 @@ class ModifiableDictItem(QtWidgets.QWidget, ConfigObject): self.update_style() self.value_changed.emit(self) + def set_values(self, key, value): + self.origin_key = key + self.key_input.setText(key) + self.value_input.update_global_values(value) + @property def is_group(self): return self._parent.is_group @@ -1470,9 +1475,7 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): # Set value if entered value is not None # else (when add button clicked) trigger `_on_value_change` if value is not None and key is not None: - item_widget.default_key = key - item_widget.key_input.setText(key) - item_widget.value_input.update_global_values(value) + item_widget.set_values(key, value) self.hierarchical_style_update() else: self._on_value_change() From 51b4bc4e7dfb24cc7eaf7ae4bdd3ea2b4efa7b03 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Sep 2020 18:30:02 +0200 Subject: [PATCH 422/813] basic logic of key is valid in dict item --- pype/tools/config_setting/config_setting/widgets/inputs.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 21ee1fc6e8..476fc0c69f 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1213,7 +1213,13 @@ class ModifiableDictItem(QtWidgets.QWidget, ConfigObject): def key_value(self): return self.key_input.text() + def is_key_valid(self): + if self.key_value() == "": + return False + if self.is_key_duplicated: + return False + return True def _on_value_change(self, item=None): self.update_style() From 0a4379557cb9a01195cc298fccc6b21c37bfd6d9 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Sep 2020 18:36:53 +0200 Subject: [PATCH 423/813] added duplication invalidation for dictionary item --- .../config_setting/widgets/inputs.py | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 476fc0c69f..1f21f35d6b 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1,5 +1,6 @@ import json import logging +import collections from Qt import QtWidgets, QtCore, QtGui from .widgets import ( AbstractConfigObject, @@ -1268,7 +1269,9 @@ class ModifiableDictItem(QtWidgets.QWidget, ConfigObject): self.update_style() def update_style(self): - if self.is_key_modified(): + if not self.is_key_valid(): + state = "invalid" + elif self.is_key_modified(): state = "modified" else: state = "" @@ -1401,6 +1404,23 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): self.remove_row(input_field) def _on_value_change(self, item=None): + fields_by_keys = collections.defaultdict(list) + for input_field in self.input_fields: + key = input_field.key_value() + fields_by_keys[key].append(input_field) + + any_invalid = False + for fields in fields_by_keys.values(): + if len(fields) == 1: + field = fields[0] + if field.is_key_duplicated: + field.is_key_duplicated = False + field.update_style() + else: + for field in fields: + field.is_key_duplicated = True + field.update_style() + if self.ignore_value_changes: return From ce7e11e12c95aef286ee6029059edcd29a6f7dc2 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Sep 2020 18:37:30 +0200 Subject: [PATCH 424/813] removed unused variable --- pype/tools/config_setting/config_setting/widgets/inputs.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 1f21f35d6b..4a2d955ae4 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1409,7 +1409,6 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): key = input_field.key_value() fields_by_keys[key].append(input_field) - any_invalid = False for fields in fields_by_keys.values(): if len(fields) == 1: field = fields[0] From 0053379aa37aa617a9cf52bffd4b5a3bc5e244f6 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Sep 2020 18:43:30 +0200 Subject: [PATCH 425/813] modifiable dict is validated in right way --- .../config_setting/widgets/inputs.py | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 4a2d955ae4..2d23085854 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1214,13 +1214,13 @@ class ModifiableDictItem(QtWidgets.QWidget, ConfigObject): def key_value(self): return self.key_input.text() - def is_key_valid(self): + def is_key_invalid(self): if self.key_value() == "": - return False + return True if self.is_key_duplicated: - return False - return True + return True + return False def _on_value_change(self, item=None): self.update_style() @@ -1268,8 +1268,12 @@ class ModifiableDictItem(QtWidgets.QWidget, ConfigObject): self.value_input.hierarchical_style_update() self.update_style() + @property + def is_invalid(self): + return self.is_key_invalid() or self.value_input.is_invalid + def update_style(self): - if not self.is_key_valid(): + if self.is_key_invalid(): state = "invalid" elif self.is_key_modified(): state = "modified" @@ -1426,7 +1430,7 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): if self.is_overidable: self._is_overriden = True - if self._is_invalid: + if self.is_invalid: self._is_modified = True elif self._is_overriden: self._is_modified = self.item_value() != self.override_value @@ -1520,6 +1524,17 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): self._on_value_change() self.parent().updateGeometry() + @property + def is_invalid(self): + return self._is_invalid or self.child_invalid + + @property + def child_invalid(self): + for input_field in self.input_fields: + if input_field.is_invalid: + return True + return False + # Dictionaries class DictWidget(QtWidgets.QWidget, ConfigObject): From 4d109fb4e2aca4457f245b9edcf226c365e5a3a8 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Sep 2020 19:08:06 +0200 Subject: [PATCH 426/813] roots widget at right place --- .../config_setting/config_setting/widgets/anatomy_inputs.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index a3d3e74e48..9e69d7234e 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -234,6 +234,7 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): self.label_widget = body_widget.label_widget main_layout = QtWidgets.QVBoxLayout(self) + main_layout.setContentsMargins(0, 0, 0, 0) main_layout.addWidget(body_widget) self.multiroot_label = multiroot_label From 5350e7a50592a53a2d9b6462647372629060f2cb Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Sep 2020 19:22:15 +0200 Subject: [PATCH 427/813] clickable widget does not override init --- pype/tools/config_setting/config_setting/widgets/widgets.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/widgets.py b/pype/tools/config_setting/config_setting/widgets/widgets.py index e547b8181a..dff966fa36 100644 --- a/pype/tools/config_setting/config_setting/widgets/widgets.py +++ b/pype/tools/config_setting/config_setting/widgets/widgets.py @@ -57,10 +57,6 @@ class PathInput(QtWidgets.QLineEdit): class ClickableWidget(QtWidgets.QLabel): clicked = QtCore.Signal() - def __init__(self, *args, **kwargs): - super(ClickableWidget, self).__init__(*args, **kwargs) - self.setObjectName("ExpandLabel") - def mouseReleaseEvent(self, event): if event.button() == QtCore.Qt.LeftButton: self.clicked.emit() From 8ef37d3dfa565c22f776cea73e257f0000d07e64 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 8 Sep 2020 10:46:07 +0200 Subject: [PATCH 428/813] defined child offset for nested hierarchy --- pype/tools/config_setting/config_setting/widgets/lib.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pype/tools/config_setting/config_setting/widgets/lib.py b/pype/tools/config_setting/config_setting/widgets/lib.py index 0d70885de7..08b0dfc3c4 100644 --- a/pype/tools/config_setting/config_setting/widgets/lib.py +++ b/pype/tools/config_setting/config_setting/widgets/lib.py @@ -14,6 +14,7 @@ class TypeToKlass: NOT_SET = type("NOT_SET", (), {"__bool__": lambda obj: False})() METADATA_KEY = type("METADATA_KEY", (), {}) OVERRIDE_VERSION = 1 +CHILD_OFFSET = 15 def convert_gui_data_to_overrides(data, first=True): From de9e7811e40f64093ee92435d93beb2b95b7509b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 8 Sep 2020 10:46:22 +0200 Subject: [PATCH 429/813] clickable widget is not qlabel but qwidget --- pype/tools/config_setting/config_setting/widgets/widgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/tools/config_setting/config_setting/widgets/widgets.py b/pype/tools/config_setting/config_setting/widgets/widgets.py index dff966fa36..a2e3e058b0 100644 --- a/pype/tools/config_setting/config_setting/widgets/widgets.py +++ b/pype/tools/config_setting/config_setting/widgets/widgets.py @@ -54,7 +54,7 @@ class PathInput(QtWidgets.QLineEdit): self.clear_end_path() -class ClickableWidget(QtWidgets.QLabel): +class ClickableWidget(QtWidgets.QWidget): clicked = QtCore.Signal() def mouseReleaseEvent(self, event): From 4829bbc473963187963392749634851d8283eab6 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 8 Sep 2020 10:46:48 +0200 Subject: [PATCH 430/813] expandable widget has one more widget for showing the left side line --- .../config_setting/widgets/widgets.py | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/widgets.py b/pype/tools/config_setting/config_setting/widgets/widgets.py index a2e3e058b0..19d9ad9d25 100644 --- a/pype/tools/config_setting/config_setting/widgets/widgets.py +++ b/pype/tools/config_setting/config_setting/widgets/widgets.py @@ -82,40 +82,41 @@ class ExpandingWidget(QtWidgets.QWidget): label_widget = QtWidgets.QLabel(label, parent=top_part) label_widget.setObjectName("DictLabel") - layout = QtWidgets.QHBoxLayout(top_part) - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(5) - layout.addWidget(button_toggle) - layout.addWidget(label_widget) - top_part.setLayout(layout) + side_line_widget = QtWidgets.QWidget(top_part) + side_line_widget.setObjectName("SideLineWidget") + side_line_layout = QtWidgets.QHBoxLayout(side_line_widget) + side_line_layout.setContentsMargins(5, 10, 0, 10) + side_line_layout.addWidget(button_toggle) + side_line_layout.addWidget(label_widget) + + top_part_layout = QtWidgets.QHBoxLayout(top_part) + top_part_layout.setContentsMargins(0, 0, 0, 0) + top_part_layout.addWidget(side_line_widget) self.setAttribute(QtCore.Qt.WA_StyledBackground) self.top_part = top_part + self.side_line_widget = side_line_widget self.button_toggle = button_toggle self.label_widget = label_widget self.top_part.clicked.connect(self._top_part_clicked) self.button_toggle.clicked.connect(self.toggle_content) + self.main_layout = QtWidgets.QVBoxLayout(self) + self.main_layout.setContentsMargins(0, 0, 0, 0) + self.main_layout.setSpacing(0) + self.main_layout.addWidget(self.top_part) + def hide_toolbox(self, hide_content=False): self.button_toggle.setArrowType(QtCore.Qt.NoArrow) self.toolbox_hidden = True self.content_widget.setVisible(not hide_content) self.parent().updateGeometry() - def set_content_widget(self, content_widget, margins=None): - main_layout = QtWidgets.QVBoxLayout(self) - if margins is None: - margins = (4, 4, 0, 4) - main_layout.setContentsMargins(*margins) - + def set_content_widget(self, content_widget): content_widget.setVisible(False) - - main_layout.addWidget(self.top_part) - main_layout.addWidget(content_widget) - self.setLayout(main_layout) - + self.main_layout.addWidget(content_widget) self.content_widget = content_widget def _top_part_clicked(self): From 58272c472bc1921127d13339c9d96de2ed220c26 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 8 Sep 2020 10:47:37 +0200 Subject: [PATCH 431/813] modifications to match new expandable widget content properties --- .../config_setting/widgets/inputs.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 2d23085854..4347b1336d 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -8,7 +8,7 @@ from .widgets import ( NumberSpinBox, PathInput ) -from .lib import NOT_SET, METADATA_KEY, TypeToKlass +from .lib import NOT_SET, METADATA_KEY, TypeToKlass, CHILD_OFFSET class ConfigObject(AbstractConfigObject): @@ -1331,12 +1331,12 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): self.key = input_data["key"] main_layout = QtWidgets.QHBoxLayout(self) - main_layout.setContentsMargins(5, 5, 0, 5) + main_layout.setContentsMargins(0, 0, 0, 0) main_layout.setSpacing(0) content_widget = QtWidgets.QWidget(self) content_layout = QtWidgets.QVBoxLayout(content_widget) - content_layout.setContentsMargins(3, 3, 0, 3) + content_layout.setContentsMargins(CHILD_OFFSET, 3, 0, 3) if as_widget: main_layout.addWidget(content_widget) @@ -1572,7 +1572,7 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): self.key = input_data["key"] main_layout = QtWidgets.QHBoxLayout(self) - main_layout.setContentsMargins(5, 5, 0, 5) + main_layout.setContentsMargins(0, 0, 0, 0) main_layout.setSpacing(0) body_widget = ExpandingWidget(input_data["label"], self) @@ -1581,7 +1581,7 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): content_widget = QtWidgets.QWidget(body_widget) content_layout = QtWidgets.QVBoxLayout(content_widget) - content_layout.setContentsMargins(3, 3, 0, 3) + content_layout.setContentsMargins(CHILD_OFFSET, 3, 0, 3) body_widget.set_content_widget(content_widget) @@ -1725,8 +1725,10 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): child_state = "child-{}".format(child_state) if child_state != self._child_state: - self.setProperty("state", child_state) - self.style().polish(self) + self.body_widget.side_line_widget.setProperty("state", child_state) + self.body_widget.side_line_widget.style().polish( + self.body_widget.side_line_widget + ) self._child_state = child_state state = self.style_state( @@ -2300,6 +2302,7 @@ class DictFormWidget(QtWidgets.QWidget, ConfigObject): self.input_fields = [] self.content_layout = QtWidgets.QFormLayout(self) + self.content_layout.setContentsMargins(0, 0, 0, 0) for child_data in input_data.get("children", []): self.add_children_gui(child_data) From 3a566b306f1857d86a02dff264862434bc9491eb Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 8 Sep 2020 10:47:51 +0200 Subject: [PATCH 432/813] only SideLineWidget has changing border colors --- .../config_setting/style/style.css | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/pype/tools/config_setting/config_setting/style/style.css b/pype/tools/config_setting/config_setting/style/style.css index 638bf1c6fb..9051130344 100644 --- a/pype/tools/config_setting/config_setting/style/style.css +++ b/pype/tools/config_setting/config_setting/style/style.css @@ -123,7 +123,7 @@ QPushButton[btn-type="expand-toggle"] { #DictLabel { font-weight: bold; } -#ModifiableDict, #DictWidget, #AnatomyWidget { +#SideLineWidget { border-style: solid; border-color: #455c6e; border-left-width: 3px; @@ -131,36 +131,36 @@ QPushButton[btn-type="expand-toggle"] { border-right-width: 0px; border-top-width: 0px; } -#ModifiableDict:hover, #DictWidget:hover, #AnatomyWidget:hover { +#SideLineWidget:hover { border-color: #62839d; } -#ModifiableDict[state="child-modified"], #DictWidget[state="child-modified"], #AnatomyWidget[state="child-modified"] { +#SideLineWidget[state="child-modified"]{ border-color: #106aa2; } -#ModifiableDict[state="child-modified"]:hover, #DictWidget[state="child-modified"]:hover, #AnatomyWidget[state="child-modified"]:hover { +#SideLineWidget[state="child-modified"]:hover{ border-color: #137cbd; } -#ModifiableDict[state="child-invalid"], #DictWidget[state="child-invalid"], #AnatomyWidget[state="child-invalid"] { +#SideLineWidget[state="child-invalid"]{ border-color: #ad2e2e; } -#ModifiableDict[state="child-invalid"]:hover, #DictWidget[state="child-invalid"]:hover, #AnatomyWidget[state="child-invalid"]:hover { +#SideLineWidget[state="child-invalid"]:hover{ border-color: #c93636; } -#ModifiableDict[state="child-overriden"], #DictWidget[state="child-overriden"], #AnatomyWidget[state="child-overriden"] { +#SideLineWidget[state="child-overriden"]{ border-color: #e67300; } -#ModifiableDict[state="child-overriden"]:hover, #DictWidget[state="child-overriden"]:hover, #AnatomyWidget[state="child-overriden"]:hover { +#SideLineWidget[state="child-overriden"]:hover { border-color: #ff8c1a; } -#ModifiableDict[state="child-overriden-modified"], #DictWidget[state="child-overriden-modified"], #AnatomyWidget[state="child-modified"] { +#SideLineWidget[state="child-overriden-modified"] { border-color: #106aa2; } -#ModifiableDict[state="child-overriden-modified"]:hover, #DictWidget[state="child-overriden-modified"]:hover, #AnatomyWidget[state="child-modified"]:hover { +#SideLineWidget[state="child-overriden-modified"]:hover { border-color: #137cbd; } From bafc82364df22bc9ab8e684232d1adfacb6b5849 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 8 Sep 2020 10:54:44 +0200 Subject: [PATCH 433/813] top part of expandable widget is not used elsewhere --- pype/tools/config_setting/config_setting/widgets/inputs.py | 2 +- pype/tools/config_setting/config_setting/widgets/widgets.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 4347b1336d..a66f121b3d 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1631,7 +1631,7 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): ) item.value_changed.connect(self._on_value_change) - self.body_widget.top_part.layout().addWidget(item) + self.body_widget.side_line_layout.addWidget(item) self.checkbox_widget = item self.input_fields.append(item) return item diff --git a/pype/tools/config_setting/config_setting/widgets/widgets.py b/pype/tools/config_setting/config_setting/widgets/widgets.py index 19d9ad9d25..8291f56e13 100644 --- a/pype/tools/config_setting/config_setting/widgets/widgets.py +++ b/pype/tools/config_setting/config_setting/widgets/widgets.py @@ -95,18 +95,18 @@ class ExpandingWidget(QtWidgets.QWidget): self.setAttribute(QtCore.Qt.WA_StyledBackground) - self.top_part = top_part self.side_line_widget = side_line_widget + self.side_line_layout = side_line_layout self.button_toggle = button_toggle self.label_widget = label_widget - self.top_part.clicked.connect(self._top_part_clicked) + top_part.clicked.connect(self._top_part_clicked) self.button_toggle.clicked.connect(self.toggle_content) self.main_layout = QtWidgets.QVBoxLayout(self) self.main_layout.setContentsMargins(0, 0, 0, 0) self.main_layout.setSpacing(0) - self.main_layout.addWidget(self.top_part) + self.main_layout.addWidget(top_part) def hide_toolbox(self, hide_content=False): self.button_toggle.setArrowType(QtCore.Qt.NoArrow) From e453b1c816084b957244cc5576e932d48aee6e6e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 8 Sep 2020 10:54:57 +0200 Subject: [PATCH 434/813] add bg color to dict parts --- pype/tools/config_setting/config_setting/style/style.css | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pype/tools/config_setting/config_setting/style/style.css b/pype/tools/config_setting/config_setting/style/style.css index 9051130344..3c8cc59182 100644 --- a/pype/tools/config_setting/config_setting/style/style.css +++ b/pype/tools/config_setting/config_setting/style/style.css @@ -123,7 +123,9 @@ QPushButton[btn-type="expand-toggle"] { #DictLabel { font-weight: bold; } + #SideLineWidget { + background-color: #31424e; border-style: solid; border-color: #455c6e; border-left-width: 3px; @@ -131,11 +133,11 @@ QPushButton[btn-type="expand-toggle"] { border-right-width: 0px; border-top-width: 0px; } + #SideLineWidget:hover { border-color: #62839d; } - #SideLineWidget[state="child-modified"]{ border-color: #106aa2; } From 1bc174be101002859051445b6013f5415137cdf0 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 8 Sep 2020 09:58:25 +0100 Subject: [PATCH 435/813] Fix comment tag collection and integration. --- .../publish/integrate_hierarchy_ftrack.py | 21 +++++++++++++++++++ .../publish/collect_hierarchy_context.py | 3 +-- .../nukestudio/publish/collect_shots.py | 5 +++-- .../publish/collect_tag_comments.py | 2 +- 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/pype/plugins/ftrack/publish/integrate_hierarchy_ftrack.py b/pype/plugins/ftrack/publish/integrate_hierarchy_ftrack.py index cc569ce2d1..c4d8f2f705 100644 --- a/pype/plugins/ftrack/publish/integrate_hierarchy_ftrack.py +++ b/pype/plugins/ftrack/publish/integrate_hierarchy_ftrack.py @@ -167,6 +167,27 @@ class IntegrateHierarchyToFtrack(pyblish.api.ContextPlugin): self.session.rollback() six.reraise(tp, value, tb) + # Create notes. + user = self.session.query( + "User where username is \"{}\"".format(self.session.api_user) + ).first() + if user: + for comment in entity_data.get("comments", []): + entity.create_note(comment, user) + else: + self.log.warning( + "Was not able to query current User {}".format( + self.session.api_user + ) + ) + try: + self.session.commit() + except Exception: + tp, value, tb = sys.exc_info() + self.session.rollback() + six.reraise(tp, value, tb) + + # Import children. if 'childs' in entity_data: self.import_to_ftrack( entity_data['childs'], entity) diff --git a/pype/plugins/nukestudio/publish/collect_hierarchy_context.py b/pype/plugins/nukestudio/publish/collect_hierarchy_context.py index a41e987bdb..930efd618e 100644 --- a/pype/plugins/nukestudio/publish/collect_hierarchy_context.py +++ b/pype/plugins/nukestudio/publish/collect_hierarchy_context.py @@ -273,8 +273,6 @@ class CollectHierarchyContext(pyblish.api.ContextPlugin): instance.data["clipOut"] - instance.data["clipIn"]) - - self.log.debug( "__ instance.data[parents]: {}".format( instance.data["parents"] @@ -319,6 +317,7 @@ class CollectHierarchyContext(pyblish.api.ContextPlugin): }) in_info['tasks'] = instance.data['tasks'] + in_info["comments"] = instance.data.get("comments", []) parents = instance.data.get('parents', []) self.log.debug("__ in_info: {}".format(in_info)) diff --git a/pype/plugins/nukestudio/publish/collect_shots.py b/pype/plugins/nukestudio/publish/collect_shots.py index 455e25bf82..7055167143 100644 --- a/pype/plugins/nukestudio/publish/collect_shots.py +++ b/pype/plugins/nukestudio/publish/collect_shots.py @@ -40,11 +40,12 @@ class CollectShots(api.InstancePlugin): data["name"] = data["subset"] + "_" + data["asset"] data["label"] = ( - "{} - {} - tasks:{} - assetbuilds:{}".format( + "{} - {} - tasks: {} - assetbuilds: {} - comments: {}".format( data["asset"], data["subset"], data["tasks"], - [x["name"] for x in data.get("assetbuilds", [])] + [x["name"] for x in data.get("assetbuilds", [])], + len(data.get("comments", [])) ) ) diff --git a/pype/plugins/nukestudio/publish/collect_tag_comments.py b/pype/plugins/nukestudio/publish/collect_tag_comments.py index 1ec98e3d3b..e14e53d439 100644 --- a/pype/plugins/nukestudio/publish/collect_tag_comments.py +++ b/pype/plugins/nukestudio/publish/collect_tag_comments.py @@ -17,7 +17,7 @@ class CollectClipTagComments(api.InstancePlugin): for tag in instance.data["tags"]: if tag["name"].lower() == "comment": instance.data["comments"].append( - tag.metadata().dict()["tag.note"] + tag["metadata"]["tag.note"] ) # Find tags on the source clip. From 19dd51fb98d53f19e010d13681fc140981604241 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 8 Sep 2020 11:48:56 +0200 Subject: [PATCH 436/813] set checkbox spacing --- pype/tools/config_setting/config_setting/style/style.css | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/pype/tools/config_setting/config_setting/style/style.css b/pype/tools/config_setting/config_setting/style/style.css index 3c8cc59182..079d2f697e 100644 --- a/pype/tools/config_setting/config_setting/style/style.css +++ b/pype/tools/config_setting/config_setting/style/style.css @@ -19,12 +19,11 @@ QMenu::item:selected { border-left-color: #61839e; background-color: #222d37; } - -QCheckBox::indicator { -} -QCheckBox::indicator:focus { - color: #ff0000; +QCheckBox { + spacing: 0px; } +QCheckBox::indicator {} +QCheckBox::indicator:focus {} QLineEdit, QSpinBox, QDoubleSpinBox, QPlainTextEdit, QTextEdit { border: 1px solid #aaaaaa; From 842aeaf6de0d2d75d80b3af06a08190ea706bb28 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 8 Sep 2020 11:53:22 +0200 Subject: [PATCH 437/813] expanding widget has more widgets --- .../config_setting/widgets/widgets.py | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/widgets.py b/pype/tools/config_setting/config_setting/widgets/widgets.py index 8291f56e13..31c7df4704 100644 --- a/pype/tools/config_setting/config_setting/widgets/widgets.py +++ b/pype/tools/config_setting/config_setting/widgets/widgets.py @@ -95,6 +95,10 @@ class ExpandingWidget(QtWidgets.QWidget): self.setAttribute(QtCore.Qt.WA_StyledBackground) + self.top_part_ending = None + self.after_label_layout = None + self.end_of_layout = None + self.side_line_widget = side_line_widget self.side_line_layout = side_line_layout self.button_toggle = button_toggle @@ -137,6 +141,47 @@ class ExpandingWidget(QtWidgets.QWidget): self.content_widget.setVisible(checked) self.parent().updateGeometry() + def add_widget_after_label(self, widget): + self._add_side_widget_subwidgets() + self.after_label_layout.addWidget(widget) + + def _add_side_widget_subwidgets(self): + if self.top_part_ending is not None: + return + + top_part_ending = QtWidgets.QWidget(self.side_line_widget) + top_part_ending.setAttribute(QtCore.Qt.WA_TranslucentBackground) + + top_part_ending_layout = QtWidgets.QHBoxLayout(top_part_ending) + top_part_ending_layout.setContentsMargins(0, 0, 0, 0) + top_part_ending_layout.setSpacing(0) + top_part_ending_layout.setAlignment(QtCore.Qt.AlignVCenter) + + after_label_widget = QtWidgets.QWidget(top_part_ending) + spacer_item = QtWidgets.QWidget(top_part_ending) + end_of_widget = QtWidgets.QWidget(top_part_ending) + + self.after_label_layout = QtWidgets.QVBoxLayout(after_label_widget) + self.after_label_layout.setContentsMargins(0, 0, 0, 0) + + self.end_of_layout = QtWidgets.QVBoxLayout(end_of_widget) + self.end_of_layout.setContentsMargins(0, 0, 0, 0) + + spacer_layout = QtWidgets.QVBoxLayout(spacer_item) + spacer_layout.setContentsMargins(0, 0, 0, 0) + + top_part_ending_layout.addWidget(after_label_widget, 0) + top_part_ending_layout.addWidget(spacer_item, 1) + top_part_ending_layout.addWidget(end_of_widget, 0) + + top_part_ending.setAttribute(QtCore.Qt.WA_TranslucentBackground) + after_label_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground) + spacer_item.setAttribute(QtCore.Qt.WA_TranslucentBackground) + end_of_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground) + + self.top_part_ending = top_part_ending + self.side_line_layout.addWidget(top_part_ending) + def resizeEvent(self, event): super(ExpandingWidget, self).resizeEvent(event) self.content_widget.updateGeometry() From 47da6e688449b5c68eeaa179da618ba38cc2526c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 8 Sep 2020 11:54:13 +0200 Subject: [PATCH 438/813] dict widget use different way of adding checkbox to top --- pype/tools/config_setting/config_setting/widgets/inputs.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index a66f121b3d..f188471df8 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -282,7 +282,6 @@ class BooleanWidget(QtWidgets.QWidget, InputObject): self.label_widget = label_widget self.checkbox = QtWidgets.QCheckBox(self) - self.checkbox.setAttribute(QtCore.Qt.WA_StyledBackground) layout.addWidget(self.checkbox, 1) self.setFocusProxy(self.checkbox) @@ -1631,7 +1630,7 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): ) item.value_changed.connect(self._on_value_change) - self.body_widget.side_line_layout.addWidget(item) + self.body_widget.add_widget_after_label(item) self.checkbox_widget = item self.input_fields.append(item) return item From 37f901ad0979ad0875fc65f68e2bc406d74ec8d9 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 8 Sep 2020 11:57:17 +0200 Subject: [PATCH 439/813] dict widget has right margins --- pype/tools/config_setting/config_setting/widgets/inputs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index f188471df8..bed0e60339 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1580,7 +1580,7 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): content_widget = QtWidgets.QWidget(body_widget) content_layout = QtWidgets.QVBoxLayout(content_widget) - content_layout.setContentsMargins(CHILD_OFFSET, 3, 0, 3) + content_layout.setContentsMargins(CHILD_OFFSET, 5, 0, 0) body_widget.set_content_widget(content_widget) From 03470eb75d9e46f62a35a443218ef3281fc7632b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 8 Sep 2020 12:28:51 +0200 Subject: [PATCH 440/813] changed styled background to transparent in most of cases --- .../config_setting/config_setting/widgets/inputs.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index bed0e60339..f175cb159a 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -277,7 +277,7 @@ class BooleanWidget(QtWidgets.QWidget, InputObject): if not label_widget: label = input_data["label"] label_widget = QtWidgets.QLabel(label) - label_widget.setAttribute(QtCore.Qt.WA_StyledBackground) + label_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground) layout.addWidget(label_widget, 0) self.label_widget = label_widget @@ -993,7 +993,7 @@ class ListWidget(QtWidgets.QWidget, InputObject): self.label_widget = label_widget inputs_widget = QtWidgets.QWidget(self) - inputs_widget.setAttribute(QtCore.Qt.WA_StyledBackground) + inputs_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground) layout.addWidget(inputs_widget) inputs_layout = QtWidgets.QVBoxLayout(inputs_widget) @@ -1358,7 +1358,7 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): self.content_widget = content_widget self.content_layout = content_layout - self.setAttribute(QtCore.Qt.WA_StyledBackground) + self.setAttribute(QtCore.Qt.WA_TranslucentBackground) self.object_type = input_data["object_type"] self.default_value = input_data.get("default", NOT_SET) @@ -1590,7 +1590,6 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): self.label_widget = body_widget.label_widget - self.setAttribute(QtCore.Qt.WA_StyledBackground) self.checkbox_widget = None self.checkbox_key = input_data.get("checkbox_key") @@ -1822,7 +1821,7 @@ class DictInvisible(QtWidgets.QWidget, ConfigObject): self.any_parent_is_group = any_parent_is_group self._is_group = input_data.get("is_group", False) - self.setAttribute(QtCore.Qt.WA_StyledBackground) + self.setAttribute(QtCore.Qt.WA_TranslucentBackground) layout = QtWidgets.QVBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) @@ -2041,7 +2040,7 @@ class PathWidget(QtWidgets.QWidget, ConfigObject): if not label_widget: label = input_data["label"] label_widget = QtWidgets.QLabel(label) - label_widget.setAttribute(QtCore.Qt.WA_StyledBackground) + label_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground) layout.addWidget(label_widget, 0) self.label_widget = label_widget From 7794f5a20dcbd593d5b28551ef39e264f976ea4c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 8 Sep 2020 12:29:08 +0200 Subject: [PATCH 441/813] chceckbox widget has spacer at the end --- pype/tools/config_setting/config_setting/widgets/inputs.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index f175cb159a..6fb1f553bd 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -282,7 +282,12 @@ class BooleanWidget(QtWidgets.QWidget, InputObject): self.label_widget = label_widget self.checkbox = QtWidgets.QCheckBox(self) - layout.addWidget(self.checkbox, 1) + spacer = QtWidgets.QWidget(self) + layout.addWidget(self.checkbox, 0) + layout.addWidget(spacer, 1) + + spacer.setAttribute(QtCore.Qt.WA_TranslucentBackground) + self.setFocusProxy(self.checkbox) self.checkbox.stateChanged.connect(self._on_value_change) From f21c9426aaa4bdf6cc14f687694b5e402f1773bf Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 8 Sep 2020 12:29:25 +0200 Subject: [PATCH 442/813] expanding widget is transparent too --- pype/tools/config_setting/config_setting/widgets/widgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/tools/config_setting/config_setting/widgets/widgets.py b/pype/tools/config_setting/config_setting/widgets/widgets.py index 31c7df4704..a76f4f6f35 100644 --- a/pype/tools/config_setting/config_setting/widgets/widgets.py +++ b/pype/tools/config_setting/config_setting/widgets/widgets.py @@ -93,7 +93,7 @@ class ExpandingWidget(QtWidgets.QWidget): top_part_layout.setContentsMargins(0, 0, 0, 0) top_part_layout.addWidget(side_line_widget) - self.setAttribute(QtCore.Qt.WA_StyledBackground) + self.setAttribute(QtCore.Qt.WA_TranslucentBackground) self.top_part_ending = None self.after_label_layout = None From 5224572d23284f5a7bc26c45e19a6ed852318673 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 8 Sep 2020 12:30:23 +0200 Subject: [PATCH 443/813] content widget has darker and darker background --- pype/tools/config_setting/config_setting/style/style.css | 4 ++++ pype/tools/config_setting/config_setting/widgets/inputs.py | 1 + 2 files changed, 5 insertions(+) diff --git a/pype/tools/config_setting/config_setting/style/style.css b/pype/tools/config_setting/config_setting/style/style.css index 079d2f697e..f238246063 100644 --- a/pype/tools/config_setting/config_setting/style/style.css +++ b/pype/tools/config_setting/config_setting/style/style.css @@ -123,6 +123,10 @@ QPushButton[btn-type="expand-toggle"] { font-weight: bold; } +#ContentWidget { + background-color: rgba(19, 26, 32, 20%); +} + #SideLineWidget { background-color: #31424e; border-style: solid; diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 6fb1f553bd..221d02ca65 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1584,6 +1584,7 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): main_layout.addWidget(body_widget) content_widget = QtWidgets.QWidget(body_widget) + content_widget.setObjectName("ContentWidget") content_layout = QtWidgets.QVBoxLayout(content_widget) content_layout.setContentsMargins(CHILD_OFFSET, 5, 0, 0) From 53bbafcb244d9aa9647723486bbe365bfeaa2750 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 8 Sep 2020 13:55:51 +0200 Subject: [PATCH 444/813] ContentWidget is transparent by default --- pype/tools/config_setting/config_setting/style/style.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pype/tools/config_setting/config_setting/style/style.css b/pype/tools/config_setting/config_setting/style/style.css index f238246063..af61655a36 100644 --- a/pype/tools/config_setting/config_setting/style/style.css +++ b/pype/tools/config_setting/config_setting/style/style.css @@ -124,6 +124,9 @@ QPushButton[btn-type="expand-toggle"] { } #ContentWidget { + background-color: transparent; +} +#ContentWidget[content_state="hightlighted"] { background-color: rgba(19, 26, 32, 20%); } From 1dabf20d363d7f6331c933a9525e6571d61ebd89 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 8 Sep 2020 13:56:15 +0200 Subject: [PATCH 445/813] dict widget can have set if should highlight background --- .../config_setting/config_setting/widgets/inputs.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 221d02ca65..bd046e32dd 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1571,6 +1571,11 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): self._is_group = input_data.get("is_group", False) self._is_nullable = input_data.get("is_nullable", False) + if input_data.get("highlight_content", False): + content_state = "hightlighted" + else: + content_state = "" + self.input_fields = [] self.key = input_data["key"] @@ -1585,6 +1590,7 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): content_widget = QtWidgets.QWidget(body_widget) content_widget.setObjectName("ContentWidget") + content_widget.setProperty("content_state", content_state) content_layout = QtWidgets.QVBoxLayout(content_widget) content_layout.setContentsMargins(CHILD_OFFSET, 5, 0, 0) @@ -1596,7 +1602,6 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): self.label_widget = body_widget.label_widget - self.checkbox_widget = None self.checkbox_key = input_data.get("checkbox_key") @@ -2311,6 +2316,8 @@ class DictFormWidget(QtWidgets.QWidget, ConfigObject): for child_data in input_data.get("children", []): self.add_children_gui(child_data) + self.setAttribute(QtCore.Qt.WA_TranslucentBackground) + def add_children_gui(self, child_configuration): item_type = child_configuration["type"] # Pop label to not be set in child From 6efbecff8f49eab23d5a6d5ee84271519320290b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 8 Sep 2020 13:59:54 +0200 Subject: [PATCH 446/813] added bottom margin to content when should highligh content --- pype/tools/config_setting/config_setting/widgets/inputs.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index bd046e32dd..908fe8e070 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1573,8 +1573,10 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): if input_data.get("highlight_content", False): content_state = "hightlighted" + bottom_margin = 5 else: content_state = "" + bottom_margin = 0 self.input_fields = [] @@ -1592,7 +1594,7 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): content_widget.setObjectName("ContentWidget") content_widget.setProperty("content_state", content_state) content_layout = QtWidgets.QVBoxLayout(content_widget) - content_layout.setContentsMargins(CHILD_OFFSET, 5, 0, 0) + content_layout.setContentsMargins(CHILD_OFFSET, 5, 0, bottom_margin) body_widget.set_content_widget(content_widget) From 967e312df0861751347e3b0b2d2ad2ab4ca20941 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 8 Sep 2020 14:11:48 +0200 Subject: [PATCH 447/813] AnatomyWIdget is using expanding widget --- .../config_setting/widgets/anatomy_inputs.py | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index 9e69d7234e..befd928463 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -1,7 +1,7 @@ from Qt import QtWidgets, QtCore from .widgets import ExpandingWidget from .inputs import ConfigObject, ModifiableDict, PathWidget -from .lib import NOT_SET, TypeToKlass +from .lib import NOT_SET, TypeToKlass, CHILD_OFFSET class AnatomyWidget(QtWidgets.QWidget, ConfigObject): @@ -56,13 +56,13 @@ class AnatomyWidget(QtWidgets.QWidget, ConfigObject): body_widget = ExpandingWidget("Anatomy", self) layout = QtWidgets.QVBoxLayout(self) - layout.setContentsMargins(5, 5, 0, 5) + layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) layout.addWidget(body_widget) content_widget = QtWidgets.QWidget(body_widget) content_layout = QtWidgets.QVBoxLayout(content_widget) - content_layout.setContentsMargins(0, 0, 0, 0) + content_layout.setContentsMargins(CHILD_OFFSET, 5, 0, 0) content_layout.setSpacing(5) content_layout.addWidget(self.root_widget) @@ -206,12 +206,18 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): checkbox_layout.addWidget(multiroot_label, 0) checkbox_layout.addWidget(multiroot_checkbox, 1) + body_widget = ExpandingWidget("Roots", self) + content_widget = QtWidgets.QWidget(body_widget) + path_widget_data = { "key": "roots", "multipath": False, "multiplatform": True } - singleroot_widget = PathWidget(path_widget_data, self, as_widget=True) + singleroot_widget = PathWidget( + path_widget_data, self, + as_widget=True, parent_widget=content_widget + ) multiroot_data = { "key": "roots", "object_type": "path-widget", @@ -220,12 +226,13 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): "multiplatform": True } } - multiroot_widget = ModifiableDict(multiroot_data, self, as_widget=True) + multiroot_widget = ModifiableDict( + multiroot_data, self, + as_widget=True, parent_widget=content_widget + ) - body_widget = ExpandingWidget("Roots", self) - - content_widget = QtWidgets.QWidget(body_widget) content_layout = QtWidgets.QVBoxLayout(content_widget) + content_layout.setContentsMargins(0, 0, 0, 0) content_layout.addWidget(checkbox_widget) content_layout.addWidget(singleroot_widget) content_layout.addWidget(multiroot_widget) From 4e03a7c6a55a8884b2ed62b64bcadfa11d017a65 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 8 Sep 2020 14:12:10 +0200 Subject: [PATCH 448/813] lowered bg color increasing --- pype/tools/config_setting/config_setting/style/style.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/tools/config_setting/config_setting/style/style.css b/pype/tools/config_setting/config_setting/style/style.css index af61655a36..fe3bba366a 100644 --- a/pype/tools/config_setting/config_setting/style/style.css +++ b/pype/tools/config_setting/config_setting/style/style.css @@ -127,7 +127,7 @@ QPushButton[btn-type="expand-toggle"] { background-color: transparent; } #ContentWidget[content_state="hightlighted"] { - background-color: rgba(19, 26, 32, 20%); + background-color: rgba(19, 26, 32, 15%); } #SideLineWidget { From 99899a35ea7caca510f883de70ef9452d02b2299 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 8 Sep 2020 14:18:40 +0200 Subject: [PATCH 449/813] fix style updates in list --- .../config_setting/config_setting/widgets/inputs.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 908fe8e070..18a6ef690b 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -952,6 +952,9 @@ class ListItem(QtWidgets.QWidget, ConfigObject): def child_overriden(self): return self.value_input.child_overriden + def hierarchical_style_update(self): + self.value_input.hierarchical_style_update() + def mouseReleaseEvent(self, event): return QtWidgets.QWidget.mouseReleaseEvent(self, event) @@ -1047,6 +1050,7 @@ class ListWidget(QtWidgets.QWidget, InputObject): self.start_value = self.item_value() self._is_modified = self.global_value != self.start_value + self.hierarchical_style_update() def set_value(self, value): previous_inputs = tuple(self.input_fields) @@ -1102,7 +1106,6 @@ class ListWidget(QtWidgets.QWidget, InputObject): # else (when add button clicked) trigger `_on_value_change` if value is not None: item_widget.value_input.update_global_values(value) - self.hierarchical_style_update() else: self._on_value_change() self.updateGeometry() @@ -1143,6 +1146,11 @@ class ListWidget(QtWidgets.QWidget, InputObject): self.set_value(value) + def hierarchical_style_update(self): + for input_field in self.input_fields: + input_field.hierarchical_style_update() + self.update_style() + def update_style(self): state = self.style_state( self.is_invalid, self.is_overriden, self.is_modified From 6c37ede07cb9a12a99f4282cc566d43632ebca47 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 8 Sep 2020 14:40:08 +0200 Subject: [PATCH 450/813] fix raw json in-validation --- pype/tools/config_setting/config_setting/widgets/inputs.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 18a6ef690b..a08ad88015 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -820,10 +820,11 @@ class RawJsonWidget(QtWidgets.QWidget, InputObject): elif self.default_value is not NOT_SET: self.set_value(self.default_value) + self._is_invalid = self.text_input.has_invalid_value() + self.global_value = value self.start_value = self.item_value() - self._is_invalid = self.text_input.has_invalid_value() self._is_modified = self.global_value != self.start_value def set_value(self, value): From 1ceb51a26b3659b95cb517f1a971eb99d6de07ae Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 8 Sep 2020 14:43:17 +0200 Subject: [PATCH 451/813] added simple implementation of Templates --- .../config_setting/widgets/anatomy_inputs.py | 56 +++++++++++++------ 1 file changed, 40 insertions(+), 16 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index befd928463..7705efd91c 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -1,6 +1,6 @@ from Qt import QtWidgets, QtCore from .widgets import ExpandingWidget -from .inputs import ConfigObject, ModifiableDict, PathWidget +from .inputs import ConfigObject, ModifiableDict, PathWidget, RawJsonWidget from .lib import NOT_SET, TypeToKlass, CHILD_OFFSET @@ -172,6 +172,7 @@ class AnatomyWidget(QtWidgets.QWidget, ConfigObject): def item_value(self): output = {} output.update(self.root_widget.config_value()) + output.update(self.templates_widget.config_value()) return output def config_value(self): @@ -459,18 +460,30 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): return {self.key: self.item_value()} -# TODO implement -class TemplatesWidget(QtWidgets.QWidget): - def __init__(self, parent=None): +class TemplatesWidget(QtWidgets.QWidget, ConfigObject): + def __init__(self, parent): super(TemplatesWidget, self).__init__(parent) + self._parent = parent + + self._is_group = True + self.any_parent_is_group = False + self.key = "templates" + body_widget = ExpandingWidget("Templates", self) content_widget = QtWidgets.QWidget(body_widget) body_widget.set_content_widget(content_widget) content_layout = QtWidgets.QVBoxLayout(content_widget) - label = QtWidgets.QLabel("Nothing yet", content_widget) - content_layout.addWidget(label) + template_input_data = { + "key": self.key + } + self.label_widget = body_widget.label_widget + self.value_input = RawJsonWidget( + template_input_data, self, + label_widget=self.label_widget + ) + content_layout.addWidget(self.value_input) layout = QtWidgets.QVBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) @@ -479,39 +492,50 @@ class TemplatesWidget(QtWidgets.QWidget): layout.addWidget(body_widget) def update_global_values(self, values): - pass + self.value_input.update_global_values(values) def apply_overrides(self, parent_values): - pass + self.value_input.apply_overrides(parent_values) def hierarchical_style_update(self): - pass + self.value_input.hierarchical_style_update() @property def is_modified(self): - return False + return self.value_input.is_modified @property def is_overriden(self): - return False + return self._is_overriden @property def child_modified(self): - return False + return self.value_input.child_modified @property def child_overriden(self): - return False + return self.value_input.child_overriden @property def child_invalid(self): - return False + return self.value_input.child_invalid def remove_overrides(self): - pass + print("* `remove_overrides` NOT IMPLEMENTED") def discard_changes(self): - pass + print("* `discard_changes` NOT IMPLEMENTED") + + def overrides(self): + if not self.is_overriden: + return NOT_SET, False + return self.config_value(), True + + def item_value(self): + return self.value_input.item_value() + + def config_value(self): + return self.value_input.config_value() TypeToKlass.types["anatomy"] = AnatomyWidget From 80ef4bf876ccf60d0b7c1093991321d002a1d6ef Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 8 Sep 2020 14:49:13 +0200 Subject: [PATCH 452/813] implemented set_as_overriden for path widget --- pype/tools/config_setting/config_setting/widgets/inputs.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index a08ad88015..155bcbb74d 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -2243,6 +2243,9 @@ class PathWidget(QtWidgets.QWidget, ConfigObject): self._is_modified = self.child_modified + def set_as_overriden(self): + self._is_overriden = True + @property def child_modified(self): for input_field in self.input_fields: From d7e2b07fcc38bbdf025a9d5db529a6bb0c5011d6 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 8 Sep 2020 14:57:53 +0200 Subject: [PATCH 453/813] few minor changes about attribute values in anatomy iwdgets --- .../config_setting/widgets/anatomy_inputs.py | 30 ++++++++----------- 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index 7705efd91c..688b4bf715 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -131,10 +131,6 @@ class AnatomyWidget(QtWidgets.QWidget, ConfigObject): self.templates_widget.hierarchical_style_update() self.update_style() - @property - def is_modified(self): - return self._is_modified or self.child_modified - @property def child_modified(self): return ( @@ -363,9 +359,7 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): if self.ignore_value_changes: return - if item is None: - pass - elif ( + if item is not None and ( (self.is_multiroot and item != self.multiroot_widget) or (not self.is_multiroot and item != self.singleroot_widget) ): @@ -393,14 +387,6 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): self._on_value_change() - @property - def is_modified(self): - return self._is_modified or self.child_modified - - @property - def is_overriden(self): - return self._is_overriden - @property def child_modified(self): if self.is_multiroot: @@ -450,6 +436,11 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): self._is_modified = self.child_modified + def set_as_overriden(self): + self._is_overriden = self._was_overriden + self.singleroot_widget.set_as_overriden() + self.multiroot_widget.set_as_overriden() + def item_value(self): if self.is_multiroot: return self.multiroot_widget.item_value() @@ -521,13 +512,16 @@ class TemplatesWidget(QtWidgets.QWidget, ConfigObject): return self.value_input.child_invalid def remove_overrides(self): - print("* `remove_overrides` NOT IMPLEMENTED") + self.value_input.remove_overrides() def discard_changes(self): - print("* `discard_changes` NOT IMPLEMENTED") + self.value_input.discard_changes() + + def set_as_overriden(self): + self.value_input.set_as_overriden() def overrides(self): - if not self.is_overriden: + if not self.child_overriden: return NOT_SET, False return self.config_value(), True From 427a6109b7ad5d1b7591b60a50e9ba6d6df78dd3 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 8 Sep 2020 15:09:42 +0200 Subject: [PATCH 454/813] removed all io_nonsingleton files from pype --- .../adobe_communicator/lib/io_nonsingleton.py | 460 ------------------ pype/modules/ftrack/lib/io_nonsingleton.py | 460 ------------------ 2 files changed, 920 deletions(-) delete mode 100644 pype/modules/adobe_communicator/lib/io_nonsingleton.py delete mode 100644 pype/modules/ftrack/lib/io_nonsingleton.py diff --git a/pype/modules/adobe_communicator/lib/io_nonsingleton.py b/pype/modules/adobe_communicator/lib/io_nonsingleton.py deleted file mode 100644 index da37c657c6..0000000000 --- a/pype/modules/adobe_communicator/lib/io_nonsingleton.py +++ /dev/null @@ -1,460 +0,0 @@ -""" -Wrapper around interactions with the database - -Copy of io module in avalon-core. - - In this case not working as singleton with api.Session! -""" - -import os -import time -import errno -import shutil -import logging -import tempfile -import functools -import contextlib - -from avalon import schema -from avalon.vendor import requests -from avalon.io import extract_port_from_url - -# Third-party dependencies -import pymongo - - -def auto_reconnect(func): - """Handling auto reconnect in 3 retry times""" - @functools.wraps(func) - def decorated(*args, **kwargs): - object = args[0] - for retry in range(3): - try: - return func(*args, **kwargs) - except pymongo.errors.AutoReconnect: - object.log.error("Reconnecting..") - time.sleep(0.1) - else: - raise - - return decorated - - -class DbConnector(object): - - log = logging.getLogger(__name__) - - def __init__(self): - self.Session = {} - self._mongo_client = None - self._sentry_client = None - self._sentry_logging_handler = None - self._database = None - self._is_installed = False - - def __getitem__(self, key): - # gives direct access to collection withou setting `active_table` - return self._database[key] - - def __getattribute__(self, attr): - # not all methods of PyMongo database are implemented with this it is - # possible to use them too - try: - return super(DbConnector, self).__getattribute__(attr) - except AttributeError: - cur_proj = self.Session["AVALON_PROJECT"] - return self._database[cur_proj].__getattribute__(attr) - - def install(self): - """Establish a persistent connection to the database""" - if self._is_installed: - return - - logging.basicConfig() - self.Session.update(self._from_environment()) - - timeout = int(self.Session["AVALON_TIMEOUT"]) - mongo_url = self.Session["AVALON_MONGO"] - kwargs = { - "host": mongo_url, - "serverSelectionTimeoutMS": timeout - } - - port = extract_port_from_url(mongo_url) - if port is not None: - kwargs["port"] = int(port) - - self._mongo_client = pymongo.MongoClient(**kwargs) - - for retry in range(3): - try: - t1 = time.time() - self._mongo_client.server_info() - - except Exception: - self.log.error("Retrying..") - time.sleep(1) - timeout *= 1.5 - - else: - break - - else: - raise IOError( - "ERROR: Couldn't connect to %s in " - "less than %.3f ms" % (self.Session["AVALON_MONGO"], timeout)) - - self.log.info("Connected to %s, delay %.3f s" % ( - self.Session["AVALON_MONGO"], time.time() - t1)) - - self._install_sentry() - - self._database = self._mongo_client[self.Session["AVALON_DB"]] - self._is_installed = True - - def _install_sentry(self): - if "AVALON_SENTRY" not in self.Session: - return - - try: - from raven import Client - from raven.handlers.logging import SentryHandler - from raven.conf import setup_logging - except ImportError: - # Note: There was a Sentry address in this Session - return self.log.warning("Sentry disabled, raven not installed") - - client = Client(self.Session["AVALON_SENTRY"]) - - # Transmit log messages to Sentry - handler = SentryHandler(client) - handler.setLevel(logging.WARNING) - - setup_logging(handler) - - self._sentry_client = client - self._sentry_logging_handler = handler - self.log.info( - "Connected to Sentry @ %s" % self.Session["AVALON_SENTRY"] - ) - - def _from_environment(self): - Session = { - item[0]: os.getenv(item[0], item[1]) - for item in ( - # Root directory of projects on disk - ("AVALON_PROJECTS", None), - - # Name of current Project - ("AVALON_PROJECT", ""), - - # Name of current Asset - ("AVALON_ASSET", ""), - - # Name of current silo - ("AVALON_SILO", ""), - - # Name of current task - ("AVALON_TASK", None), - - # Name of current app - ("AVALON_APP", None), - - # Path to working directory - ("AVALON_WORKDIR", None), - - # Name of current Config - # TODO(marcus): Establish a suitable default config - ("AVALON_CONFIG", "no_config"), - - # Name of Avalon in graphical user interfaces - # Use this to customise the visual appearance of Avalon - # to better integrate with your surrounding pipeline - ("AVALON_LABEL", "Avalon"), - - # Used during any connections to the outside world - ("AVALON_TIMEOUT", "1000"), - - # Address to Asset Database - ("AVALON_MONGO", "mongodb://localhost:27017"), - - # Name of database used in MongoDB - ("AVALON_DB", "avalon"), - - # Address to Sentry - ("AVALON_SENTRY", None), - - # Address to Deadline Web Service - # E.g. http://192.167.0.1:8082 - ("AVALON_DEADLINE", None), - - # Enable features not necessarily stable. The user's own risk - ("AVALON_EARLY_ADOPTER", None), - - # Address of central asset repository, contains - # the following interface: - # /upload - # /download - # /manager (optional) - ("AVALON_LOCATION", "http://127.0.0.1"), - - # Boolean of whether to upload published material - # to central asset repository - ("AVALON_UPLOAD", None), - - # Generic username and password - ("AVALON_USERNAME", "avalon"), - ("AVALON_PASSWORD", "secret"), - - # Unique identifier for instances in working files - ("AVALON_INSTANCE_ID", "avalon.instance"), - ("AVALON_CONTAINER_ID", "avalon.container"), - - # Enable debugging - ("AVALON_DEBUG", None), - - ) if os.getenv(item[0], item[1]) is not None - } - - Session["schema"] = "avalon-core:session-2.0" - try: - schema.validate(Session) - except schema.ValidationError as e: - # TODO(marcus): Make this mandatory - self.log.warning(e) - - return Session - - def uninstall(self): - """Close any connection to the database""" - try: - self._mongo_client.close() - except AttributeError: - pass - - self._mongo_client = None - self._database = None - self._is_installed = False - - def active_project(self): - """Return the name of the active project""" - return self.Session["AVALON_PROJECT"] - - def activate_project(self, project_name): - self.Session["AVALON_PROJECT"] = project_name - - def projects(self): - """List available projects - - Returns: - list of project documents - - """ - - collection_names = self.collections() - for project in collection_names: - if project in ("system.indexes",): - continue - - # Each collection will have exactly one project document - document = self.find_project(project) - - if document is not None: - yield document - - def locate(self, path): - """Traverse a hierarchy from top-to-bottom - - Example: - representation = locate(["hulk", "Bruce", "modelDefault", 1, "ma"]) - - Returns: - representation (ObjectId) - - """ - - components = zip( - ("project", "asset", "subset", "version", "representation"), - path - ) - - parent = None - for type_, name in components: - latest = (type_ == "version") and name in (None, -1) - - try: - if latest: - parent = self.find_one( - filter={ - "type": type_, - "parent": parent - }, - projection={"_id": 1}, - sort=[("name", -1)] - )["_id"] - else: - parent = self.find_one( - filter={ - "type": type_, - "name": name, - "parent": parent - }, - projection={"_id": 1}, - )["_id"] - - except TypeError: - return None - - return parent - - @auto_reconnect - def collections(self): - return self._database.collection_names() - - @auto_reconnect - def find_project(self, project): - return self._database[project].find_one({"type": "project"}) - - @auto_reconnect - def insert_one(self, item): - assert isinstance(item, dict), "item must be of type " - schema.validate(item) - return self._database[self.Session["AVALON_PROJECT"]].insert_one(item) - - @auto_reconnect - def insert_many(self, items, ordered=True): - # check if all items are valid - assert isinstance(items, list), "`items` must be of type " - for item in items: - assert isinstance(item, dict), "`item` must be of type " - schema.validate(item) - - return self._database[self.Session["AVALON_PROJECT"]].insert_many( - items, - ordered=ordered) - - @auto_reconnect - def find(self, filter, projection=None, sort=None): - return self._database[self.Session["AVALON_PROJECT"]].find( - filter=filter, - projection=projection, - sort=sort - ) - - @auto_reconnect - def find_one(self, filter, projection=None, sort=None): - assert isinstance(filter, dict), "filter must be " - - return self._database[self.Session["AVALON_PROJECT"]].find_one( - filter=filter, - projection=projection, - sort=sort - ) - - @auto_reconnect - def save(self, *args, **kwargs): - return self._database[self.Session["AVALON_PROJECT"]].save( - *args, **kwargs) - - @auto_reconnect - def replace_one(self, filter, replacement): - return self._database[self.Session["AVALON_PROJECT"]].replace_one( - filter, replacement) - - @auto_reconnect - def update_many(self, filter, update): - return self._database[self.Session["AVALON_PROJECT"]].update_many( - filter, update) - - @auto_reconnect - def distinct(self, *args, **kwargs): - return self._database[self.Session["AVALON_PROJECT"]].distinct( - *args, **kwargs) - - @auto_reconnect - def drop(self, *args, **kwargs): - return self._database[self.Session["AVALON_PROJECT"]].drop( - *args, **kwargs) - - @auto_reconnect - def delete_many(self, *args, **kwargs): - return self._database[self.Session["AVALON_PROJECT"]].delete_many( - *args, **kwargs) - - def parenthood(self, document): - assert document is not None, "This is a bug" - - parents = list() - - while document.get("parent") is not None: - document = self.find_one({"_id": document["parent"]}) - - if document is None: - break - - if document.get("type") == "master_version": - _document = self.find_one({"_id": document["version_id"]}) - document["data"] = _document["data"] - - parents.append(document) - - return parents - - @contextlib.contextmanager - def tempdir(self): - tempdir = tempfile.mkdtemp() - try: - yield tempdir - finally: - shutil.rmtree(tempdir) - - def download(self, src, dst): - """Download `src` to `dst` - - Arguments: - src (str): URL to source file - dst (str): Absolute path to destination file - - Yields tuple (progress, error): - progress (int): Between 0-100 - error (Exception): Any exception raised when first making connection - - """ - - try: - response = requests.get( - src, - stream=True, - auth=requests.auth.HTTPBasicAuth( - self.Session["AVALON_USERNAME"], - self.Session["AVALON_PASSWORD"] - ) - ) - except requests.ConnectionError as e: - yield None, e - return - - with self.tempdir() as dirname: - tmp = os.path.join(dirname, os.path.basename(src)) - - with open(tmp, "wb") as f: - total_length = response.headers.get("content-length") - - if total_length is None: # no content length header - f.write(response.content) - else: - downloaded = 0 - total_length = int(total_length) - for data in response.iter_content(chunk_size=4096): - downloaded += len(data) - f.write(data) - - yield int(100.0 * downloaded / total_length), None - - try: - os.makedirs(os.path.dirname(dst)) - except OSError as e: - # An already existing destination directory is fine. - if e.errno != errno.EEXIST: - raise - - shutil.copy(tmp, dst) diff --git a/pype/modules/ftrack/lib/io_nonsingleton.py b/pype/modules/ftrack/lib/io_nonsingleton.py deleted file mode 100644 index da37c657c6..0000000000 --- a/pype/modules/ftrack/lib/io_nonsingleton.py +++ /dev/null @@ -1,460 +0,0 @@ -""" -Wrapper around interactions with the database - -Copy of io module in avalon-core. - - In this case not working as singleton with api.Session! -""" - -import os -import time -import errno -import shutil -import logging -import tempfile -import functools -import contextlib - -from avalon import schema -from avalon.vendor import requests -from avalon.io import extract_port_from_url - -# Third-party dependencies -import pymongo - - -def auto_reconnect(func): - """Handling auto reconnect in 3 retry times""" - @functools.wraps(func) - def decorated(*args, **kwargs): - object = args[0] - for retry in range(3): - try: - return func(*args, **kwargs) - except pymongo.errors.AutoReconnect: - object.log.error("Reconnecting..") - time.sleep(0.1) - else: - raise - - return decorated - - -class DbConnector(object): - - log = logging.getLogger(__name__) - - def __init__(self): - self.Session = {} - self._mongo_client = None - self._sentry_client = None - self._sentry_logging_handler = None - self._database = None - self._is_installed = False - - def __getitem__(self, key): - # gives direct access to collection withou setting `active_table` - return self._database[key] - - def __getattribute__(self, attr): - # not all methods of PyMongo database are implemented with this it is - # possible to use them too - try: - return super(DbConnector, self).__getattribute__(attr) - except AttributeError: - cur_proj = self.Session["AVALON_PROJECT"] - return self._database[cur_proj].__getattribute__(attr) - - def install(self): - """Establish a persistent connection to the database""" - if self._is_installed: - return - - logging.basicConfig() - self.Session.update(self._from_environment()) - - timeout = int(self.Session["AVALON_TIMEOUT"]) - mongo_url = self.Session["AVALON_MONGO"] - kwargs = { - "host": mongo_url, - "serverSelectionTimeoutMS": timeout - } - - port = extract_port_from_url(mongo_url) - if port is not None: - kwargs["port"] = int(port) - - self._mongo_client = pymongo.MongoClient(**kwargs) - - for retry in range(3): - try: - t1 = time.time() - self._mongo_client.server_info() - - except Exception: - self.log.error("Retrying..") - time.sleep(1) - timeout *= 1.5 - - else: - break - - else: - raise IOError( - "ERROR: Couldn't connect to %s in " - "less than %.3f ms" % (self.Session["AVALON_MONGO"], timeout)) - - self.log.info("Connected to %s, delay %.3f s" % ( - self.Session["AVALON_MONGO"], time.time() - t1)) - - self._install_sentry() - - self._database = self._mongo_client[self.Session["AVALON_DB"]] - self._is_installed = True - - def _install_sentry(self): - if "AVALON_SENTRY" not in self.Session: - return - - try: - from raven import Client - from raven.handlers.logging import SentryHandler - from raven.conf import setup_logging - except ImportError: - # Note: There was a Sentry address in this Session - return self.log.warning("Sentry disabled, raven not installed") - - client = Client(self.Session["AVALON_SENTRY"]) - - # Transmit log messages to Sentry - handler = SentryHandler(client) - handler.setLevel(logging.WARNING) - - setup_logging(handler) - - self._sentry_client = client - self._sentry_logging_handler = handler - self.log.info( - "Connected to Sentry @ %s" % self.Session["AVALON_SENTRY"] - ) - - def _from_environment(self): - Session = { - item[0]: os.getenv(item[0], item[1]) - for item in ( - # Root directory of projects on disk - ("AVALON_PROJECTS", None), - - # Name of current Project - ("AVALON_PROJECT", ""), - - # Name of current Asset - ("AVALON_ASSET", ""), - - # Name of current silo - ("AVALON_SILO", ""), - - # Name of current task - ("AVALON_TASK", None), - - # Name of current app - ("AVALON_APP", None), - - # Path to working directory - ("AVALON_WORKDIR", None), - - # Name of current Config - # TODO(marcus): Establish a suitable default config - ("AVALON_CONFIG", "no_config"), - - # Name of Avalon in graphical user interfaces - # Use this to customise the visual appearance of Avalon - # to better integrate with your surrounding pipeline - ("AVALON_LABEL", "Avalon"), - - # Used during any connections to the outside world - ("AVALON_TIMEOUT", "1000"), - - # Address to Asset Database - ("AVALON_MONGO", "mongodb://localhost:27017"), - - # Name of database used in MongoDB - ("AVALON_DB", "avalon"), - - # Address to Sentry - ("AVALON_SENTRY", None), - - # Address to Deadline Web Service - # E.g. http://192.167.0.1:8082 - ("AVALON_DEADLINE", None), - - # Enable features not necessarily stable. The user's own risk - ("AVALON_EARLY_ADOPTER", None), - - # Address of central asset repository, contains - # the following interface: - # /upload - # /download - # /manager (optional) - ("AVALON_LOCATION", "http://127.0.0.1"), - - # Boolean of whether to upload published material - # to central asset repository - ("AVALON_UPLOAD", None), - - # Generic username and password - ("AVALON_USERNAME", "avalon"), - ("AVALON_PASSWORD", "secret"), - - # Unique identifier for instances in working files - ("AVALON_INSTANCE_ID", "avalon.instance"), - ("AVALON_CONTAINER_ID", "avalon.container"), - - # Enable debugging - ("AVALON_DEBUG", None), - - ) if os.getenv(item[0], item[1]) is not None - } - - Session["schema"] = "avalon-core:session-2.0" - try: - schema.validate(Session) - except schema.ValidationError as e: - # TODO(marcus): Make this mandatory - self.log.warning(e) - - return Session - - def uninstall(self): - """Close any connection to the database""" - try: - self._mongo_client.close() - except AttributeError: - pass - - self._mongo_client = None - self._database = None - self._is_installed = False - - def active_project(self): - """Return the name of the active project""" - return self.Session["AVALON_PROJECT"] - - def activate_project(self, project_name): - self.Session["AVALON_PROJECT"] = project_name - - def projects(self): - """List available projects - - Returns: - list of project documents - - """ - - collection_names = self.collections() - for project in collection_names: - if project in ("system.indexes",): - continue - - # Each collection will have exactly one project document - document = self.find_project(project) - - if document is not None: - yield document - - def locate(self, path): - """Traverse a hierarchy from top-to-bottom - - Example: - representation = locate(["hulk", "Bruce", "modelDefault", 1, "ma"]) - - Returns: - representation (ObjectId) - - """ - - components = zip( - ("project", "asset", "subset", "version", "representation"), - path - ) - - parent = None - for type_, name in components: - latest = (type_ == "version") and name in (None, -1) - - try: - if latest: - parent = self.find_one( - filter={ - "type": type_, - "parent": parent - }, - projection={"_id": 1}, - sort=[("name", -1)] - )["_id"] - else: - parent = self.find_one( - filter={ - "type": type_, - "name": name, - "parent": parent - }, - projection={"_id": 1}, - )["_id"] - - except TypeError: - return None - - return parent - - @auto_reconnect - def collections(self): - return self._database.collection_names() - - @auto_reconnect - def find_project(self, project): - return self._database[project].find_one({"type": "project"}) - - @auto_reconnect - def insert_one(self, item): - assert isinstance(item, dict), "item must be of type " - schema.validate(item) - return self._database[self.Session["AVALON_PROJECT"]].insert_one(item) - - @auto_reconnect - def insert_many(self, items, ordered=True): - # check if all items are valid - assert isinstance(items, list), "`items` must be of type " - for item in items: - assert isinstance(item, dict), "`item` must be of type " - schema.validate(item) - - return self._database[self.Session["AVALON_PROJECT"]].insert_many( - items, - ordered=ordered) - - @auto_reconnect - def find(self, filter, projection=None, sort=None): - return self._database[self.Session["AVALON_PROJECT"]].find( - filter=filter, - projection=projection, - sort=sort - ) - - @auto_reconnect - def find_one(self, filter, projection=None, sort=None): - assert isinstance(filter, dict), "filter must be " - - return self._database[self.Session["AVALON_PROJECT"]].find_one( - filter=filter, - projection=projection, - sort=sort - ) - - @auto_reconnect - def save(self, *args, **kwargs): - return self._database[self.Session["AVALON_PROJECT"]].save( - *args, **kwargs) - - @auto_reconnect - def replace_one(self, filter, replacement): - return self._database[self.Session["AVALON_PROJECT"]].replace_one( - filter, replacement) - - @auto_reconnect - def update_many(self, filter, update): - return self._database[self.Session["AVALON_PROJECT"]].update_many( - filter, update) - - @auto_reconnect - def distinct(self, *args, **kwargs): - return self._database[self.Session["AVALON_PROJECT"]].distinct( - *args, **kwargs) - - @auto_reconnect - def drop(self, *args, **kwargs): - return self._database[self.Session["AVALON_PROJECT"]].drop( - *args, **kwargs) - - @auto_reconnect - def delete_many(self, *args, **kwargs): - return self._database[self.Session["AVALON_PROJECT"]].delete_many( - *args, **kwargs) - - def parenthood(self, document): - assert document is not None, "This is a bug" - - parents = list() - - while document.get("parent") is not None: - document = self.find_one({"_id": document["parent"]}) - - if document is None: - break - - if document.get("type") == "master_version": - _document = self.find_one({"_id": document["version_id"]}) - document["data"] = _document["data"] - - parents.append(document) - - return parents - - @contextlib.contextmanager - def tempdir(self): - tempdir = tempfile.mkdtemp() - try: - yield tempdir - finally: - shutil.rmtree(tempdir) - - def download(self, src, dst): - """Download `src` to `dst` - - Arguments: - src (str): URL to source file - dst (str): Absolute path to destination file - - Yields tuple (progress, error): - progress (int): Between 0-100 - error (Exception): Any exception raised when first making connection - - """ - - try: - response = requests.get( - src, - stream=True, - auth=requests.auth.HTTPBasicAuth( - self.Session["AVALON_USERNAME"], - self.Session["AVALON_PASSWORD"] - ) - ) - except requests.ConnectionError as e: - yield None, e - return - - with self.tempdir() as dirname: - tmp = os.path.join(dirname, os.path.basename(src)) - - with open(tmp, "wb") as f: - total_length = response.headers.get("content-length") - - if total_length is None: # no content length header - f.write(response.content) - else: - downloaded = 0 - total_length = int(total_length) - for data in response.iter_content(chunk_size=4096): - downloaded += len(data) - f.write(data) - - yield int(100.0 * downloaded / total_length), None - - try: - os.makedirs(os.path.dirname(dst)) - except OSError as e: - # An already existing destination directory is fine. - if e.errno != errno.EEXIST: - raise - - shutil.copy(tmp, dst) From c68423279c0c1b7dd5a9af4be3d3f42b5d0561da Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 8 Sep 2020 15:10:34 +0200 Subject: [PATCH 455/813] everywhere where io_nonsingleton was used is used AvalonmongoDB now --- pype/modules/adobe_communicator/lib/__init__.py | 2 -- pype/modules/adobe_communicator/lib/rest_api.py | 4 ++-- pype/modules/avalon_apps/rest_api.py | 4 ++-- pype/modules/ftrack/actions/action_delete_asset.py | 4 ++-- pype/modules/ftrack/actions/action_delete_old_versions.py | 4 ++-- pype/modules/ftrack/actions/action_delivery.py | 4 ++-- .../ftrack/actions/action_store_thumbnails_to_avalon.py | 4 ++-- pype/modules/ftrack/events/event_sync_to_avalon.py | 4 ++-- pype/modules/ftrack/events/event_user_assigment.py | 4 ++-- pype/modules/ftrack/lib/avalon_sync.py | 4 ++-- pype/tools/launcher/window.py | 4 ++-- pype/tools/standalonepublish/app.py | 4 ++-- 12 files changed, 22 insertions(+), 24 deletions(-) diff --git a/pype/modules/adobe_communicator/lib/__init__.py b/pype/modules/adobe_communicator/lib/__init__.py index 23aee81275..f918e49a60 100644 --- a/pype/modules/adobe_communicator/lib/__init__.py +++ b/pype/modules/adobe_communicator/lib/__init__.py @@ -1,8 +1,6 @@ -from .io_nonsingleton import DbConnector from .rest_api import AdobeRestApi, PUBLISH_PATHS __all__ = [ "PUBLISH_PATHS", - "DbConnector", "AdobeRestApi" ] diff --git a/pype/modules/adobe_communicator/lib/rest_api.py b/pype/modules/adobe_communicator/lib/rest_api.py index 86739e4d80..35094d10dc 100644 --- a/pype/modules/adobe_communicator/lib/rest_api.py +++ b/pype/modules/adobe_communicator/lib/rest_api.py @@ -2,7 +2,7 @@ import os import sys import copy from pype.modules.rest_api import RestApi, route, abort, CallbackResult -from .io_nonsingleton import DbConnector +from avalon.api import AvalonMongoDB from pype.api import config, execute, Logger log = Logger().get_logger("AdobeCommunicator") @@ -14,7 +14,7 @@ PUBLISH_PATHS = [] class AdobeRestApi(RestApi): - dbcon = DbConnector() + dbcon = AvalonMongoDB() def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) diff --git a/pype/modules/avalon_apps/rest_api.py b/pype/modules/avalon_apps/rest_api.py index 1cb9e544a7..2408e56bbc 100644 --- a/pype/modules/avalon_apps/rest_api.py +++ b/pype/modules/avalon_apps/rest_api.py @@ -4,14 +4,14 @@ import json import bson import bson.json_util from pype.modules.rest_api import RestApi, abort, CallbackResult -from pype.modules.ftrack.lib.io_nonsingleton import DbConnector +from avalon.api import AvalonMongoDB class AvalonRestApi(RestApi): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self.dbcon = DbConnector() + self.dbcon = AvalonMongoDB() self.dbcon.install() @RestApi.route("/projects/", url_prefix="/avalon", methods="GET") diff --git a/pype/modules/ftrack/actions/action_delete_asset.py b/pype/modules/ftrack/actions/action_delete_asset.py index 27394770e1..7d2dac3320 100644 --- a/pype/modules/ftrack/actions/action_delete_asset.py +++ b/pype/modules/ftrack/actions/action_delete_asset.py @@ -5,7 +5,7 @@ from queue import Queue from bson.objectid import ObjectId from pype.modules.ftrack.lib import BaseAction, statics_icon -from pype.modules.ftrack.lib.io_nonsingleton import DbConnector +from avalon.api import AvalonMongoDB class DeleteAssetSubset(BaseAction): @@ -21,7 +21,7 @@ class DeleteAssetSubset(BaseAction): #: roles that are allowed to register this action role_list = ["Pypeclub", "Administrator", "Project Manager"] #: Db connection - dbcon = DbConnector() + dbcon = AvalonMongoDB() splitter = {"type": "label", "value": "---"} action_data_by_id = {} diff --git a/pype/modules/ftrack/actions/action_delete_old_versions.py b/pype/modules/ftrack/actions/action_delete_old_versions.py index 6a4c5a0cae..b55f091fdc 100644 --- a/pype/modules/ftrack/actions/action_delete_old_versions.py +++ b/pype/modules/ftrack/actions/action_delete_old_versions.py @@ -6,7 +6,7 @@ import clique from pymongo import UpdateOne from pype.modules.ftrack.lib import BaseAction, statics_icon -from pype.modules.ftrack.lib.io_nonsingleton import DbConnector +from avalon.api import AvalonMongoDB from pype.api import Anatomy import avalon.pipeline @@ -24,7 +24,7 @@ class DeleteOldVersions(BaseAction): role_list = ["Pypeclub", "Project Manager", "Administrator"] icon = statics_icon("ftrack", "action_icons", "PypeAdmin.svg") - dbcon = DbConnector() + dbcon = AvalonMongoDB() inteface_title = "Choose your preferences" splitter_item = {"type": "label", "value": "---"} diff --git a/pype/modules/ftrack/actions/action_delivery.py b/pype/modules/ftrack/actions/action_delivery.py index cf80fd77ff..8812ce9bc7 100644 --- a/pype/modules/ftrack/actions/action_delivery.py +++ b/pype/modules/ftrack/actions/action_delivery.py @@ -13,7 +13,7 @@ from avalon.vendor import filelink from pype.api import Anatomy, config from pype.modules.ftrack.lib import BaseAction, statics_icon from pype.modules.ftrack.lib.avalon_sync import CUST_ATTR_ID_KEY -from pype.modules.ftrack.lib.io_nonsingleton import DbConnector +from avalon.api import AvalonMongoDB class Delivery(BaseAction): @@ -24,7 +24,7 @@ class Delivery(BaseAction): role_list = ["Pypeclub", "Administrator", "Project manager"] icon = statics_icon("ftrack", "action_icons", "Delivery.svg") - db_con = DbConnector() + db_con = AvalonMongoDB() def discover(self, session, entities, event): for entity in entities: diff --git a/pype/modules/ftrack/actions/action_store_thumbnails_to_avalon.py b/pype/modules/ftrack/actions/action_store_thumbnails_to_avalon.py index 94ca503233..36f7175768 100644 --- a/pype/modules/ftrack/actions/action_store_thumbnails_to_avalon.py +++ b/pype/modules/ftrack/actions/action_store_thumbnails_to_avalon.py @@ -6,7 +6,7 @@ import json from bson.objectid import ObjectId from pype.modules.ftrack.lib import BaseAction, statics_icon from pype.api import Anatomy -from pype.modules.ftrack.lib.io_nonsingleton import DbConnector +from avalon.api import AvalonMongoDB from pype.modules.ftrack.lib.avalon_sync import CUST_ATTR_ID_KEY @@ -25,7 +25,7 @@ class StoreThumbnailsToAvalon(BaseAction): icon = statics_icon("ftrack", "action_icons", "PypeAdmin.svg") thumbnail_key = "AVALON_THUMBNAIL_ROOT" - db_con = DbConnector() + db_con = AvalonMongoDB() def discover(self, session, entities, event): for entity in entities: diff --git a/pype/modules/ftrack/events/event_sync_to_avalon.py b/pype/modules/ftrack/events/event_sync_to_avalon.py index efcb74a608..314871f5b3 100644 --- a/pype/modules/ftrack/events/event_sync_to_avalon.py +++ b/pype/modules/ftrack/events/event_sync_to_avalon.py @@ -19,12 +19,12 @@ from pype.modules.ftrack.lib.avalon_sync import ( import ftrack_api from pype.modules.ftrack import BaseEvent -from pype.modules.ftrack.lib.io_nonsingleton import DbConnector +from avalon.api import AvalonMongoDB class SyncToAvalonEvent(BaseEvent): - dbcon = DbConnector() + dbcon = AvalonMongoDB() interest_entTypes = ["show", "task"] ignore_ent_types = ["Milestone"] diff --git a/pype/modules/ftrack/events/event_user_assigment.py b/pype/modules/ftrack/events/event_user_assigment.py index d1b3439c8f..19a67b745f 100644 --- a/pype/modules/ftrack/events/event_user_assigment.py +++ b/pype/modules/ftrack/events/event_user_assigment.py @@ -4,7 +4,7 @@ import subprocess from pype.modules.ftrack import BaseEvent from pype.modules.ftrack.lib.avalon_sync import CUST_ATTR_ID_KEY -from pype.modules.ftrack.lib.io_nonsingleton import DbConnector +from avalon.api import AvalonMongoDB from bson.objectid import ObjectId @@ -37,7 +37,7 @@ class UserAssigmentEvent(BaseEvent): 3) path to publish files of task user was (de)assigned to """ - db_con = DbConnector() + db_con = AvalonMongoDB() def error(self, *err): for e in err: diff --git a/pype/modules/ftrack/lib/avalon_sync.py b/pype/modules/ftrack/lib/avalon_sync.py index 4bab1676d4..65a59452da 100644 --- a/pype/modules/ftrack/lib/avalon_sync.py +++ b/pype/modules/ftrack/lib/avalon_sync.py @@ -5,7 +5,7 @@ import json import collections import copy -from pype.modules.ftrack.lib.io_nonsingleton import DbConnector +from avalon.api import AvalonMongoDB import avalon import avalon.api @@ -240,7 +240,7 @@ def get_hierarchical_attributes(session, entity, attr_names, attr_defaults={}): class SyncEntitiesFactory: - dbcon = DbConnector() + dbcon = AvalonMongoDB() project_query = ( "select full_name, name, custom_attributes" diff --git a/pype/tools/launcher/window.py b/pype/tools/launcher/window.py index 13b4abee6e..7c680a927b 100644 --- a/pype/tools/launcher/window.py +++ b/pype/tools/launcher/window.py @@ -4,7 +4,7 @@ import logging from Qt import QtWidgets, QtCore, QtGui from avalon import style -from pype.modules.ftrack.lib.io_nonsingleton import DbConnector +from avalon.api import AvalonMongoDB from pype.api import resources from avalon.tools import lib as tools_lib @@ -251,7 +251,7 @@ class LauncherWindow(QtWidgets.QDialog): self.log = logging.getLogger( ".".join([__name__, self.__class__.__name__]) ) - self.dbcon = DbConnector() + self.dbcon = AvalonMongoDB() self.setWindowTitle("Launcher") self.setFocusPolicy(QtCore.Qt.StrongFocus) diff --git a/pype/tools/standalonepublish/app.py b/pype/tools/standalonepublish/app.py index d139366a1c..feba46987f 100644 --- a/pype/tools/standalonepublish/app.py +++ b/pype/tools/standalonepublish/app.py @@ -1,7 +1,7 @@ from bson.objectid import ObjectId from Qt import QtWidgets, QtCore from widgets import AssetWidget, FamilyWidget, ComponentsWidget, ShadowWidget -from avalon.tools.libraryloader.io_nonsingleton import DbConnector +from avalon.api import AvalonMongoDB class Window(QtWidgets.QDialog): @@ -10,7 +10,7 @@ class Window(QtWidgets.QDialog): :param parent: Main widget that cares about all GUIs :type parent: QtWidgets.QMainWindow """ - _db = DbConnector() + _db = AvalonMongoDB() _jobs = {} valid_family = False valid_components = False From 019ed41e23ac3b9de57e865cf8645219fdb5a5f7 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 8 Sep 2020 16:08:54 +0200 Subject: [PATCH 456/813] overrides for anatomy are saving data --- .../config_setting/config_setting/widgets/anatomy_inputs.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index 688b4bf715..b0aed18e01 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -161,9 +161,7 @@ class AnatomyWidget(QtWidgets.QWidget, ConfigObject): self.templates_widget.discard_changes() def overrides(self): - if self.is_overriden: - return self.config_value(), True - return {self.key: {}}, True + return self.config_value(), True def item_value(self): output = {} From 6e2dab91b2e6a7c148f28ebdfe6d85c766774807 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 8 Sep 2020 16:06:11 +0100 Subject: [PATCH 457/813] Image plane cache and PNG reprensentations. --- pype/plugins/maya/load/load_image_plane.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pype/plugins/maya/load/load_image_plane.py b/pype/plugins/maya/load/load_image_plane.py index 17a6866f80..42e7a058ed 100644 --- a/pype/plugins/maya/load/load_image_plane.py +++ b/pype/plugins/maya/load/load_image_plane.py @@ -12,7 +12,7 @@ class ImagePlaneLoader(api.Loader): families = ["plate", "render"] label = "Create imagePlane on selected camera." - representations = ["mov", "exr", "preview"] + representations = ["mov", "exr", "preview", "png"] icon = "image" color = "orange" @@ -81,6 +81,7 @@ class ImagePlaneLoader(api.Loader): 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) image_plane_shape.useFrameExtension.set(1) movie_representations = ["mov", "preview"] From 73dfecbf1a9f0518e0bd15fb7cfbbe27cced0e45 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Tue, 8 Sep 2020 17:53:32 +0200 Subject: [PATCH 458/813] fix tile order for Draft Tile Assembler --- .../maya/publish/submit_maya_deadline.py | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/pype/plugins/maya/publish/submit_maya_deadline.py b/pype/plugins/maya/publish/submit_maya_deadline.py index 747d2727b7..e4048592a7 100644 --- a/pype/plugins/maya/publish/submit_maya_deadline.py +++ b/pype/plugins/maya/publish/submit_maya_deadline.py @@ -25,6 +25,7 @@ import re import hashlib from datetime import datetime import itertools +from collections import OrderedDict import clique import requests @@ -67,7 +68,7 @@ payload_skeleton = { def _format_tiles( filename, index, tiles_x, tiles_y, - width, height, prefix, origin="blc"): + width, height, prefix): """Generate tile entries for Deadline tile job. Returns two dictionaries - one that can be directly used in Deadline @@ -113,12 +114,14 @@ def _format_tiles( """ tile = 0 out = {"JobInfo": {}, "PluginInfo": {}} - cfg = {} + cfg = OrderedDict() w_space = width / tiles_x h_space = height / tiles_y + cfg["TilesCropped"] = "False" + for tile_x in range(1, tiles_x + 1): - for tile_y in range(1, tiles_y + 1): + for tile_y in reversed(range(1, tiles_y + 1)): tile_prefix = "_tile_{}x{}_{}x{}_".format( tile_x, tile_y, tiles_x, @@ -143,14 +146,13 @@ def _format_tiles( cfg["Tile{}".format(tile)] = new_filename cfg["Tile{}Tile".format(tile)] = new_filename + cfg["Tile{}FileName".format(tile)] = new_filename cfg["Tile{}X".format(tile)] = (tile_x - 1) * w_space - if origin == "blc": - cfg["Tile{}Y".format(tile)] = (tile_y - 1) * h_space - else: - cfg["Tile{}Y".format(tile)] = int(height) - ((tile_y - 1) * h_space) # noqa: E501 - cfg["Tile{}Width".format(tile)] = tile_x * w_space - cfg["Tile{}Height".format(tile)] = tile_y * h_space + cfg["Tile{}Y".format(tile)] = int(height) - (tile_y * h_space) + + cfg["Tile{}Width".format(tile)] = w_space + cfg["Tile{}Height".format(tile)] = h_space tile += 1 return out, cfg @@ -538,7 +540,7 @@ class MayaSubmitDeadline(pyblish.api.InstancePlugin): "AuxFiles": [], "JobInfo": { "BatchName": payload["JobInfo"]["BatchName"], - "Frames": 0, + "Frames": 1, "Name": "{} - Tile Assembly Job".format( payload["JobInfo"]["Name"]), "OutputDirectory0": @@ -590,7 +592,7 @@ class MayaSubmitDeadline(pyblish.api.InstancePlugin): payload["JobInfo"]["Name"], frame, instance.data.get("tilesX") * instance.data.get("tilesY") # noqa: E501 - ) + ) self.log.info( "... preparing job {}".format( new_payload["JobInfo"]["Name"])) From 504ae4a1b3f054c1fb8327be89675eede5c56978 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 8 Sep 2020 18:22:13 +0200 Subject: [PATCH 459/813] moved setting attributes on apply overrides --- .../config_setting/widgets/anatomy_inputs.py | 4 ++-- .../config_setting/config_setting/widgets/inputs.py | 10 ++++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index b0aed18e01..6d4417a6aa 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -306,15 +306,15 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): if is_multiroot: self._is_overriden = parent_values is not NOT_SET + self._was_overriden = bool(self._is_overriden) self.singleroot_widget.apply_overrides(NOT_SET) self.multiroot_widget.apply_overrides(parent_values) else: self._is_overriden = value is not NOT_SET + self._was_overriden = bool(self._is_overriden) self.singleroot_widget.apply_overrides(value) self.multiroot_widget.apply_overrides(NOT_SET) - self._was_overriden = bool(self._is_overriden) - def hierarchical_style_update(self): self.singleroot_widget.hierarchical_style_update() self.multiroot_widget.hierarchical_style_update() diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 155bcbb74d..b094e4cf67 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -16,6 +16,7 @@ class ConfigObject(AbstractConfigObject): default_state = "" + _as_widget = False _is_overriden = False _is_modified = False _was_overriden = False @@ -44,6 +45,8 @@ class ConfigObject(AbstractConfigObject): @property def was_overriden(self): """Initial state after applying overrides.""" + if self._as_widget: + return self._parent.was_overriden return self._was_overriden @property @@ -673,7 +676,7 @@ class PathInputWidget(QtWidgets.QWidget, InputObject): if self._is_invalid: self._is_modified = True - elif self._is_overriden: + elif self.is_overriden: self._is_modified = self.item_value() != self.override_value else: self._is_modified = self.item_value() != self.global_value @@ -1126,6 +1129,7 @@ class ListWidget(QtWidgets.QWidget, InputObject): self.updateGeometry() def apply_overrides(self, parent_values): + self._is_modified = False if parent_values is NOT_SET or self.key not in parent_values: override_value = NOT_SET else: @@ -2142,7 +2146,7 @@ class PathWidget(QtWidgets.QWidget, ConfigObject): self._is_modified = self.global_value != self.start_value def apply_overrides(self, parent_values): - # Make sure this is set to False + self._is_modified = False self._state = None self._child_state = None override_values = NOT_SET @@ -2152,6 +2156,8 @@ class PathWidget(QtWidgets.QWidget, ConfigObject): override_values = parent_values.get(self.key, override_values) self._is_overriden = override_values is not NOT_SET + self._was_overriden = bool(self._is_overriden) + if not self.multiplatform: self.input_fields[0].apply_overrides(parent_values) else: From d22371d0f53436385af97bb63892eb7c307030ab Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 8 Sep 2020 18:22:36 +0200 Subject: [PATCH 460/813] modifiable dict sets overrides instead of setting values --- .../config_setting/widgets/inputs.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index b094e4cf67..f975567a6a 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1243,11 +1243,16 @@ class ModifiableDictItem(QtWidgets.QWidget, ConfigObject): self.update_style() self.value_changed.emit(self) - def set_values(self, key, value): + def update_global_values(self, key, value): self.origin_key = key self.key_input.setText(key) self.value_input.update_global_values(value) + def apply_overrides(self, key, value): + self.origin_key = key + self.key_input.setText(key) + self.value_input.apply_overrides(value) + @property def is_group(self): return self._parent.is_group @@ -1521,7 +1526,10 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): # Set value if entered value is not None # else (when add button clicked) trigger `_on_value_change` if value is not None and key is not None: - item_widget.set_values(key, value) + if self._is_overriden: + item_widget.apply_overrides(key, value) + else: + item_widget.update_global_values(key, value) self.hierarchical_style_update() else: self._on_value_change() @@ -2161,8 +2169,8 @@ class PathWidget(QtWidgets.QWidget, ConfigObject): if not self.multiplatform: self.input_fields[0].apply_overrides(parent_values) else: - for item in self.input_fields: - item.apply_overrides(override_values) + for input_field in self.input_fields: + input_field.apply_overrides(override_values) if not self._is_overriden: self._is_overriden = ( From b03687ea055ff30b39b7c9003c888f28acf0c449 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 8 Sep 2020 18:38:52 +0200 Subject: [PATCH 461/813] anatomy widgets propagate child modifications down the hierarchy --- .../config_setting/widgets/anatomy_inputs.py | 42 +++++++++++++++++-- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index 6d4417a6aa..e59de3980f 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -70,6 +70,7 @@ class AnatomyWidget(QtWidgets.QWidget, ConfigObject): body_widget.set_content_widget(content_widget) + self.body_widget = body_widget self.label_widget = body_widget.label_widget self.root_widget.value_changed.connect(self._on_value_change) @@ -122,8 +123,10 @@ class AnatomyWidget(QtWidgets.QWidget, ConfigObject): child_state = "child-{}".format(child_state) if child_state != self._child_state: - self.setProperty("state", child_state) - self.style().polish(self) + self.body_widget.side_line_widget.setProperty("state", child_state) + self.body_widget.side_line_widget.style().polish( + self.body_widget.side_line_widget + ) self._child_state = child_state def hierarchical_style_update(self): @@ -239,6 +242,7 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): main_layout.setContentsMargins(0, 0, 0, 0) main_layout.addWidget(body_widget) + self.body_widget = body_widget self.multiroot_label = multiroot_label self.multiroot_checkbox = multiroot_checkbox self.singleroot_widget = singleroot_widget @@ -342,8 +346,10 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): else: child_state = "" - self.setProperty("state", child_state) - self.style().polish(self) + self.body_widget.side_line_widget.setProperty("state", child_state) + self.body_widget.side_line_widget.style().polish( + self.body_widget.side_line_widget + ) self.label_widget.setProperty("state", state) self.label_widget.style().polish(self.label_widget) @@ -455,6 +461,8 @@ class TemplatesWidget(QtWidgets.QWidget, ConfigObject): self._parent = parent + self._state = None + self._is_group = True self.any_parent_is_group = False self.key = "templates" @@ -467,6 +475,7 @@ class TemplatesWidget(QtWidgets.QWidget, ConfigObject): template_input_data = { "key": self.key } + self.body_widget = body_widget self.label_widget = body_widget.label_widget self.value_input = RawJsonWidget( template_input_data, self, @@ -481,13 +490,38 @@ class TemplatesWidget(QtWidgets.QWidget, ConfigObject): layout.addWidget(body_widget) def update_global_values(self, values): + self._state = None self.value_input.update_global_values(values) def apply_overrides(self, parent_values): + self._state = None self.value_input.apply_overrides(parent_values) def hierarchical_style_update(self): self.value_input.hierarchical_style_update() + self.update_style() + + def update_style(self): + state = self.style_state( + self.child_invalid, self.child_overriden, self.child_modified + ) + if self._state == state: + return + + if state: + child_state = "child-{}".format(state) + else: + child_state = "" + print(child_state) + self.body_widget.side_line_widget.setProperty("state", child_state) + self.body_widget.side_line_widget.style().polish( + self.body_widget.side_line_widget + ) + + self.label_widget.setProperty("state", state) + self.label_widget.style().polish(self.label_widget) + + self._state = state @property def is_modified(self): From fb1f3d26f10e475cd11304a5ff0809c80c5bd92b Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 8 Sep 2020 19:18:06 +0200 Subject: [PATCH 462/813] Fix #237 - Updating a look where the shader name changed Added cleanup of references with failed reference edits --- pype/plugins/maya/load/load_look.py | 52 +++++++++++++++++++-- pype/widgets/message_window.py | 71 ++++++++++++++++++++++++++++- 2 files changed, 119 insertions(+), 4 deletions(-) diff --git a/pype/plugins/maya/load/load_look.py b/pype/plugins/maya/load/load_look.py index b9c0d81104..d82978b1a1 100644 --- a/pype/plugins/maya/load/load_look.py +++ b/pype/plugins/maya/load/load_look.py @@ -3,6 +3,8 @@ from avalon import api, io import json import pype.hosts.maya.lib from collections import defaultdict +from pype.widgets.message_window import ScrollMessageBox +from Qt import QtWidgets class LookLoader(pype.hosts.maya.plugin.ReferenceLoader): @@ -44,12 +46,24 @@ class LookLoader(pype.hosts.maya.plugin.ReferenceLoader): self.update(container, representation) def update(self, container, representation): + """ + Called by Scene Inventory when look should be updated to current + version. + If any reference edits cannot be applied, eg. shader renamed and + material not present, reference is unloaded and cleaned. + All failed edits are highlighted to the user via message box. + Args: + container: object that has look to be updated + representation: (dict): relationship data to get proper + representation from DB and persisted + data in .json + Returns: + None + """ import os from maya import cmds - node = container["objectName"] - path = api.get_representation_path(representation) # Get reference node from container members @@ -127,13 +141,45 @@ class LookLoader(pype.hosts.maya.plugin.ReferenceLoader): with open(shader_relation, "r") as f: relationships = json.load(f) + # update of reference could result in failed edits - material is not + # present because of renaming etc. + failed_edits = cmds.referenceQuery(reference_node, + editStrings=True, + failedEdits=True, + successfulEdits=False) + + # highlight failed edits to user + if failed_edits: + shader_data = relationships.get("relationships", {}) + for rel in shader_data.values(): + for member in rel["members"]: + nodes.add(member['name']) + + # clean references - removes failed reference edits + cmds.file(unloadReference=reference_node) + cmds.file(cr=reference_node) # cleanReference + cmds.file(loadReference=reference_node) + + # reapply shading groups from json representation + pype.hosts.maya.lib.apply_shaders(relationships, + shader_nodes, + nodes) + + msg = ["During reference update some edits failed.", + "All successful edits were kept intact.\n", + "Failed and removed edits:"] + msg.extend(failed_edits) + msg = ScrollMessageBox(QtWidgets.QMessageBox.Warning, + "Some reference edit failed", + msg) + msg.exec_() + attributes = relationships.get("attributes", []) # region compute lookup nodes_by_id = defaultdict(list) for n in nodes: nodes_by_id[pype.hosts.maya.lib.get_id(n)].append(n) - pype.hosts.maya.lib.apply_attributes(attributes, nodes_by_id) # Update metadata diff --git a/pype/widgets/message_window.py b/pype/widgets/message_window.py index 41c709b933..f909c60710 100644 --- a/pype/widgets/message_window.py +++ b/pype/widgets/message_window.py @@ -1,4 +1,4 @@ -from Qt import QtWidgets +from Qt import QtWidgets, QtCore import sys import logging @@ -49,6 +49,17 @@ class Window(QtWidgets.QWidget): def message(title=None, message=None, level="info", parent=None): + """ + Produces centered dialog with specific level denoting severity + Args: + title: (string) dialog title + message: (string) message + level: (string) info|warning|critical + parent: (QtWidgets.QApplication) + + Returns: + None + """ app = parent if not app: app = QtWidgets.QApplication(sys.argv) @@ -68,3 +79,61 @@ def message(title=None, message=None, level="info", parent=None): # skip all possible issues that may happen feature is not crutial log.warning("Couldn't center message.", exc_info=True) # sys.exit(app.exec_()) + + +class ScrollMessageBox(QtWidgets.QDialog): + """ + Basic version of scrollable QMessageBox. No other existing dialog + implementation is scrollable. + Args: + icon: + title: + messages: of messages + cancelable: - True if Cancel button should be added + """ + def __init__(self, icon, title, messages, cancelable=False, + *args, **kwargs): + super(ScrollMessageBox, self).__init__() + self.setWindowTitle(title) + self.icon = icon + + self.setWindowFlags(QtCore.Qt.WindowTitleHint) + + layout = QtWidgets.QVBoxLayout(self) + + scroll_widget = QtWidgets.QScrollArea(self) + scroll_widget.setWidgetResizable(True) + content_widget = QtWidgets.QWidget(self) + scroll_widget.setWidget(content_widget) + + max_len = 0 + content_layout = QtWidgets.QVBoxLayout(content_widget) + for message in messages: + label_widget = QtWidgets.QLabel(message, content_widget) + content_layout.addWidget(label_widget) + max_len = max(max_len, len(message)) + + # guess size of scrollable area + max_width = QtWidgets.QApplication.desktop().availableGeometry().width + scroll_widget.setMinimumWidth(min(max_width, max_len * 6)) + layout.addWidget(scroll_widget) + + if not cancelable: # if no specific buttons OK only + buttons = QtWidgets.QDialogButtonBox.Ok + else: + buttons = QtWidgets.QDialogButtonBox.Ok | \ + QtWidgets.QDialogButtonBox.Cancel + + btn_box = QtWidgets.QDialogButtonBox(buttons) + btn_box.accepted.connect(self.accept) + + if cancelable: + btn_box.reject.connect(self.reject) + + btn = QtWidgets.QPushButton('Copy to clipboard') + btn.clicked.connect(lambda: QtWidgets.QApplication. + clipboard().setText("\n".join(messages))) + btn_box.addButton(btn, QtWidgets.QDialogButtonBox.NoRole) + + layout.addWidget(btn_box) + self.show() From ace606efe6e4bac12e0a2c25060ae6f781335ec3 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 9 Sep 2020 09:51:25 +0200 Subject: [PATCH 463/813] renamed studio to system window --- pype/tools/config_setting/config_setting/widgets/base.py | 4 ++-- pype/tools/config_setting/config_setting/widgets/window.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/base.py b/pype/tools/config_setting/config_setting/widgets/base.py index 066c00c96d..d39b5789d9 100644 --- a/pype/tools/config_setting/config_setting/widgets/base.py +++ b/pype/tools/config_setting/config_setting/widgets/base.py @@ -7,14 +7,14 @@ from . import lib from avalon import io -class StudioWidget(QtWidgets.QWidget): +class SystemWidget(QtWidgets.QWidget): is_overidable = False _is_overriden = False _is_group = False _any_parent_is_group = False def __init__(self, parent=None): - super(StudioWidget, self).__init__(parent) + super(SystemWidget, self).__init__(parent) self._ignore_value_changes = False diff --git a/pype/tools/config_setting/config_setting/widgets/window.py b/pype/tools/config_setting/config_setting/widgets/window.py index af23e68f77..7ad46b1a06 100644 --- a/pype/tools/config_setting/config_setting/widgets/window.py +++ b/pype/tools/config_setting/config_setting/widgets/window.py @@ -1,5 +1,5 @@ from Qt import QtWidgets -from .base import StudioWidget, ProjectWidget +from .base import SystemWidget, ProjectWidget class MainWidget(QtWidgets.QWidget): @@ -13,9 +13,9 @@ class MainWidget(QtWidgets.QWidget): header_tab_widget = QtWidgets.QTabWidget(parent=self) - studio_widget = StudioWidget() + studio_widget = SystemWidget() project_widget = ProjectWidget() - header_tab_widget.addTab(studio_widget, "Studio") + header_tab_widget.addTab(studio_widget, "System") header_tab_widget.addTab(project_widget, "Project") layout = QtWidgets.QVBoxLayout(self) From 28931dba6b17aff9a634e06e6b351b9b7b6c9781 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 9 Sep 2020 10:00:42 +0200 Subject: [PATCH 464/813] removed configs from config tool --- .../project_presets/ftrack/ftrack_config.json | 11 - .../project_presets/global/creator.json | 8 - .../global/slates/example_HD.json | 212 ------------------ .../config/project_presets/maya/capture.json | 108 --------- .../project_presets/plugins/config.json | 1 - .../plugins/ftrack/publish.json | 6 - .../plugins/global/create.json | 1 - .../plugins/global/filter.json | 1 - .../project_presets/plugins/global/load.json | 1 - .../plugins/global/publish.json | 73 ------ .../project_presets/plugins/maya/create.json | 1 - .../project_presets/plugins/maya/filter.json | 9 - .../project_presets/plugins/maya/load.json | 18 -- .../project_presets/plugins/maya/publish.json | 17 -- .../plugins/maya/workfile_build.json | 54 ----- .../project_presets/plugins/nuke/create.json | 8 - .../project_presets/plugins/nuke/load.json | 1 - .../project_presets/plugins/nuke/publish.json | 48 ---- .../plugins/nuke/workfile_build.json | 11 - .../plugins/nukestudio/filter.json | 10 - .../plugins/nukestudio/publish.json | 8 - .../plugins/standalonepublisher/publish.json | 17 -- .../project_presets/plugins/test/create.json | 8 - .../project_presets/plugins/test/publish.json | 10 - .../premiere/asset_default.json | 5 - .../project_presets/premiere/rules_tasks.json | 21 -- .../project_presets/unreal/project_setup.json | 4 - 27 files changed, 672 deletions(-) delete mode 100644 pype/tools/config_setting/config/project_presets/ftrack/ftrack_config.json delete mode 100644 pype/tools/config_setting/config/project_presets/global/creator.json delete mode 100644 pype/tools/config_setting/config/project_presets/global/slates/example_HD.json delete mode 100644 pype/tools/config_setting/config/project_presets/maya/capture.json delete mode 100644 pype/tools/config_setting/config/project_presets/plugins/config.json delete mode 100644 pype/tools/config_setting/config/project_presets/plugins/ftrack/publish.json delete mode 100644 pype/tools/config_setting/config/project_presets/plugins/global/create.json delete mode 100644 pype/tools/config_setting/config/project_presets/plugins/global/filter.json delete mode 100644 pype/tools/config_setting/config/project_presets/plugins/global/load.json delete mode 100644 pype/tools/config_setting/config/project_presets/plugins/global/publish.json delete mode 100644 pype/tools/config_setting/config/project_presets/plugins/maya/create.json delete mode 100644 pype/tools/config_setting/config/project_presets/plugins/maya/filter.json delete mode 100644 pype/tools/config_setting/config/project_presets/plugins/maya/load.json delete mode 100644 pype/tools/config_setting/config/project_presets/plugins/maya/publish.json delete mode 100644 pype/tools/config_setting/config/project_presets/plugins/maya/workfile_build.json delete mode 100644 pype/tools/config_setting/config/project_presets/plugins/nuke/create.json delete mode 100644 pype/tools/config_setting/config/project_presets/plugins/nuke/load.json delete mode 100644 pype/tools/config_setting/config/project_presets/plugins/nuke/publish.json delete mode 100644 pype/tools/config_setting/config/project_presets/plugins/nuke/workfile_build.json delete mode 100644 pype/tools/config_setting/config/project_presets/plugins/nukestudio/filter.json delete mode 100644 pype/tools/config_setting/config/project_presets/plugins/nukestudio/publish.json delete mode 100644 pype/tools/config_setting/config/project_presets/plugins/standalonepublisher/publish.json delete mode 100644 pype/tools/config_setting/config/project_presets/plugins/test/create.json delete mode 100644 pype/tools/config_setting/config/project_presets/plugins/test/publish.json delete mode 100644 pype/tools/config_setting/config/project_presets/premiere/asset_default.json delete mode 100644 pype/tools/config_setting/config/project_presets/premiere/rules_tasks.json delete mode 100644 pype/tools/config_setting/config/project_presets/unreal/project_setup.json diff --git a/pype/tools/config_setting/config/project_presets/ftrack/ftrack_config.json b/pype/tools/config_setting/config/project_presets/ftrack/ftrack_config.json deleted file mode 100644 index c9dbde4596..0000000000 --- a/pype/tools/config_setting/config/project_presets/ftrack/ftrack_config.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "status_update": { - "_ignore_": ["in progress", "ommited", "on hold"], - "Ready": ["not ready"], - "In Progress" : ["_any_"] - }, - "status_version_to_task": { - "in progress": "in progress", - "approved": "approved" - } -} diff --git a/pype/tools/config_setting/config/project_presets/global/creator.json b/pype/tools/config_setting/config/project_presets/global/creator.json deleted file mode 100644 index d14e779f01..0000000000 --- a/pype/tools/config_setting/config/project_presets/global/creator.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "Model": ["model"], - "Render Globals": ["light", "render"], - "Layout": ["layout"], - "Set Dress": ["setdress"], - "Look": ["look"], - "Rig": ["rigging"] -} diff --git a/pype/tools/config_setting/config/project_presets/global/slates/example_HD.json b/pype/tools/config_setting/config/project_presets/global/slates/example_HD.json deleted file mode 100644 index b06391fb63..0000000000 --- a/pype/tools/config_setting/config/project_presets/global/slates/example_HD.json +++ /dev/null @@ -1,212 +0,0 @@ -{ - "width": 1920, - "height": 1080, - "destination_path": "{destination_path}", - "style": { - "*": { - "font-family": "arial", - "font-color": "#ffffff", - "font-bold": false, - "font-italic": false, - "bg-color": "#0077ff", - "alignment-horizontal": "left", - "alignment-vertical": "top" - }, - "layer": { - "padding": 0, - "margin": 0 - }, - "rectangle": { - "padding": 0, - "margin": 0, - "bg-color": "#E9324B", - "fill": true - }, - "main_frame": { - "padding": 0, - "margin": 0, - "bg-color": "#252525" - }, - "table": { - "padding": 0, - "margin": 0, - "bg-color": "transparent" - }, - "table-item": { - "padding": 5, - "padding-bottom": 10, - "margin": 0, - "bg-color": "#212121", - "bg-alter-color": "#272727", - "font-color": "#dcdcdc", - "font-bold": false, - "font-italic": false, - "alignment-horizontal": "left", - "alignment-vertical": "top", - "word-wrap": false, - "ellide": true, - "max-lines": 1 - }, - "table-item-col[0]": { - "font-size": 20, - "font-color": "#898989", - "font-bold": true, - "ellide": false, - "word-wrap": true, - "max-lines": null - }, - "table-item-col[1]": { - "font-size": 40, - "padding-left": 10 - }, - "#colorbar": { - "bg-color": "#9932CC" - } - }, - "items": [{ - "type": "layer", - "direction": 1, - "name": "MainLayer", - "style": { - "#MainLayer": { - "width": 1094, - "height": 1000, - "margin": 25, - "padding": 0 - }, - "#LeftSide": { - "margin-right": 25 - } - }, - "items": [{ - "type": "layer", - "name": "LeftSide", - "items": [{ - "type": "layer", - "direction": 1, - "style": { - "table-item": { - "bg-color": "transparent", - "padding-bottom": 20 - }, - "table-item-col[0]": { - "font-size": 20, - "font-color": "#898989", - "alignment-horizontal": "right" - }, - "table-item-col[1]": { - "alignment-horizontal": "left", - "font-bold": true, - "font-size": 40 - } - }, - "items": [{ - "type": "table", - "values": [ - ["Show:", "{project[name]}"] - ], - "style": { - "table-item-field[0:0]": { - "width": 150 - }, - "table-item-field[0:1]": { - "width": 580 - } - } - }, { - "type": "table", - "values": [ - ["Submitting For:", "{intent}"] - ], - "style": { - "table-item-field[0:0]": { - "width": 160 - }, - "table-item-field[0:1]": { - "width": 218, - "alignment-horizontal": "right" - } - } - }] - }, { - "type": "rectangle", - "style": { - "bg-color": "#bc1015", - "width": 1108, - "height": 5, - "fill": true - } - }, { - "type": "table", - "use_alternate_color": true, - "values": [ - ["Version name:", "{version_name}"], - ["Date:", "{date}"], - ["Shot Types:", "{shot_type}"], - ["Submission Note:", "{submission_note}"] - ], - "style": { - "table-item": { - "padding-bottom": 20 - }, - "table-item-field[0:1]": { - "font-bold": true - }, - "table-item-field[3:0]": { - "word-wrap": true, - "ellide": true, - "max-lines": 4 - }, - "table-item-col[0]": { - "alignment-horizontal": "right", - "width": 150 - }, - "table-item-col[1]": { - "alignment-horizontal": "left", - "width": 958 - } - } - }] - }, { - "type": "layer", - "name": "RightSide", - "items": [{ - "type": "placeholder", - "name": "thumbnail", - "path": "{thumbnail_path}", - "style": { - "width": 730, - "height": 412 - } - }, { - "type": "placeholder", - "name": "colorbar", - "path": "{color_bar_path}", - "return_data": true, - "style": { - "width": 730, - "height": 55 - } - }, { - "type": "table", - "use_alternate_color": true, - "values": [ - ["Vendor:", "{vendor}"], - ["Shot Name:", "{shot_name}"], - ["Frames:", "{frame_start} - {frame_end} ({duration})"] - ], - "style": { - "table-item-col[0]": { - "alignment-horizontal": "left", - "width": 200 - }, - "table-item-col[1]": { - "alignment-horizontal": "right", - "width": 530, - "font-size": 30 - } - } - }] - }] - }] -} diff --git a/pype/tools/config_setting/config/project_presets/maya/capture.json b/pype/tools/config_setting/config/project_presets/maya/capture.json deleted file mode 100644 index b6c4893034..0000000000 --- a/pype/tools/config_setting/config/project_presets/maya/capture.json +++ /dev/null @@ -1,108 +0,0 @@ -{ - "Codec": { - "compression": "jpg", - "format": "image", - "quality": 95 - }, - "Display Options": { - "background": [ - 0.7137254901960784, - 0.7137254901960784, - 0.7137254901960784 - ], - "backgroundBottom": [ - 0.7137254901960784, - 0.7137254901960784, - 0.7137254901960784 - ], - "backgroundTop": [ - 0.7137254901960784, - 0.7137254901960784, - 0.7137254901960784 - ], - "override_display": true - }, - "Generic": { - "isolate_view": true, - "off_screen": true - }, - "IO": { - "name": "", - "open_finished": false, - "raw_frame_numbers": false, - "recent_playblasts": [], - "save_file": false - }, - "PanZoom": { - "pan_zoom": true - }, - "Renderer": { - "rendererName": "vp2Renderer" - }, - "Resolution": { - "height": 1080, - "mode": "Custom", - "percent": 1.0, - "width": 1920 - }, - "Time Range": { - "end_frame": 25, - "frame": "", - "start_frame": 0, - "time": "Time Slider" - }, - "Viewport Options": { - "cameras": false, - "clipGhosts": false, - "controlVertices": false, - "deformers": false, - "dimensions": false, - "displayLights": 0, - "dynamicConstraints": false, - "dynamics": false, - "fluids": false, - "follicles": false, - "gpuCacheDisplayFilter": false, - "greasePencils": false, - "grid": false, - "hairSystems": false, - "handles": false, - "high_quality": true, - "hud": false, - "hulls": false, - "ikHandles": false, - "imagePlane": false, - "joints": false, - "lights": false, - "locators": false, - "manipulators": false, - "motionTrails": false, - "nCloths": false, - "nParticles": false, - "nRigids": false, - "nurbsCurves": false, - "nurbsSurfaces": false, - "override_viewport_options": true, - "particleInstancers": false, - "pivots": false, - "planes": false, - "pluginShapes": false, - "polymeshes": true, - "shadows": false, - "strokes": false, - "subdivSurfaces": false, - "textures": false, - "twoSidedLighting": true - }, - "Camera Options": { - "displayGateMask": false, - "displayResolution": false, - "displayFilmGate": false, - "displayFieldChart": false, - "displaySafeAction": false, - "displaySafeTitle": false, - "displayFilmPivot": false, - "displayFilmOrigin": false, - "overscan": 1.0 - } -} diff --git a/pype/tools/config_setting/config/project_presets/plugins/config.json b/pype/tools/config_setting/config/project_presets/plugins/config.json deleted file mode 100644 index 9e26dfeeb6..0000000000 --- a/pype/tools/config_setting/config/project_presets/plugins/config.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/pype/tools/config_setting/config/project_presets/plugins/ftrack/publish.json b/pype/tools/config_setting/config/project_presets/plugins/ftrack/publish.json deleted file mode 100644 index d0469ae4f7..0000000000 --- a/pype/tools/config_setting/config/project_presets/plugins/ftrack/publish.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "IntegrateFtrackNote": { - "note_with_intent_template": "{intent}: {comment}", - "note_labels": [] - } -} diff --git a/pype/tools/config_setting/config/project_presets/plugins/global/create.json b/pype/tools/config_setting/config/project_presets/plugins/global/create.json deleted file mode 100644 index 0967ef424b..0000000000 --- a/pype/tools/config_setting/config/project_presets/plugins/global/create.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/pype/tools/config_setting/config/project_presets/plugins/global/filter.json b/pype/tools/config_setting/config/project_presets/plugins/global/filter.json deleted file mode 100644 index 0967ef424b..0000000000 --- a/pype/tools/config_setting/config/project_presets/plugins/global/filter.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/pype/tools/config_setting/config/project_presets/plugins/global/load.json b/pype/tools/config_setting/config/project_presets/plugins/global/load.json deleted file mode 100644 index 0967ef424b..0000000000 --- a/pype/tools/config_setting/config/project_presets/plugins/global/load.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/pype/tools/config_setting/config/project_presets/plugins/global/publish.json b/pype/tools/config_setting/config/project_presets/plugins/global/publish.json deleted file mode 100644 index 6e51e00497..0000000000 --- a/pype/tools/config_setting/config/project_presets/plugins/global/publish.json +++ /dev/null @@ -1,73 +0,0 @@ -{ - "IntegrateMasterVersion": { - "enabled": false - }, - "ExtractReview": { - "__documentation__": "http://pype.club/docs/admin_presets_plugins", - "profiles": [ - { - "families": [], - "hosts": [], - "outputs": { - "h264": { - "filter": { - "families": ["render", "review", "ftrack"] - }, - "ext": "mp4", - "ffmpeg_args": { - "input": [ - "-gamma 2.2" - ], - "video_filters": [], - "audio_filters": [], - "output": [ - "-pix_fmt yuv420p", - "-crf 18", - "-intra" - ] - }, - "tags": ["burnin", "ftrackreview"] - } - } - } - ] - }, - "ExtractBurnin": { - "options": { - "opacity": 1, - "x_offset": 5, - "y_offset": 5, - "bg_padding": 5, - "bg_opacity": 0.5, - "font_size": 42 - }, - "fields": { - - }, - "profiles": [ - { - "burnins": { - "burnin": { - "TOP_LEFT": "{yy}-{mm}-{dd}", - "TOP_RIGHT": "{anatomy[version]}", - "TOP_CENTERED": "", - "BOTTOM_RIGHT": "{frame_start}-{current_frame}-{frame_end}", - "BOTTOM_CENTERED": "{asset}", - "BOTTOM_LEFT": "{username}" - } - } - } - ] - }, - "IntegrateAssetNew": { - "template_name_profiles": { - "publish": { - "families": [], - "tasks": [] - }, - "render": { - "families": ["review", "render", "prerender"] - } - } - } -} diff --git a/pype/tools/config_setting/config/project_presets/plugins/maya/create.json b/pype/tools/config_setting/config/project_presets/plugins/maya/create.json deleted file mode 100644 index 0967ef424b..0000000000 --- a/pype/tools/config_setting/config/project_presets/plugins/maya/create.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/pype/tools/config_setting/config/project_presets/plugins/maya/filter.json b/pype/tools/config_setting/config/project_presets/plugins/maya/filter.json deleted file mode 100644 index 83d6f05f31..0000000000 --- a/pype/tools/config_setting/config/project_presets/plugins/maya/filter.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "Preset n1": { - "ValidateNoAnimation": false, - "ValidateShapeDefaultNames": false - }, - "Preset n2": { - "ValidateNoAnimation": false - } -} diff --git a/pype/tools/config_setting/config/project_presets/plugins/maya/load.json b/pype/tools/config_setting/config/project_presets/plugins/maya/load.json deleted file mode 100644 index 260fbb35ee..0000000000 --- a/pype/tools/config_setting/config/project_presets/plugins/maya/load.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "colors": { - "model": [0.821, 0.518, 0.117], - "rig": [0.144, 0.443, 0.463], - "pointcache": [0.368, 0.821, 0.117], - "animation": [0.368, 0.821, 0.117], - "ass": [1.0, 0.332, 0.312], - "camera": [0.447, 0.312, 1.0], - "fbx": [1.0, 0.931, 0.312], - "mayaAscii": [0.312, 1.0, 0.747], - "setdress": [0.312, 1.0, 0.747], - "layout": [0.312, 1.0, 0.747], - "vdbcache": [0.312, 1.0, 0.428], - "vrayproxy": [0.258, 0.95, 0.541], - "yeticache": [0.2, 0.8, 0.3], - "yetiRig": [0, 0.8, 0.5] - } -} diff --git a/pype/tools/config_setting/config/project_presets/plugins/maya/publish.json b/pype/tools/config_setting/config/project_presets/plugins/maya/publish.json deleted file mode 100644 index 2e2b3164f3..0000000000 --- a/pype/tools/config_setting/config/project_presets/plugins/maya/publish.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "ValidateModelName": { - "enabled": false, - "material_file": "/path/to/shader_name_definition.txt", - "regex": "(.*)_(\\d)*_(?P.*)_(GEO)" - }, - "ValidateAssemblyName": { - "enabled": false - }, - "ValidateShaderName": { - "enabled": false, - "regex": "(?P.*)_(.*)_SHD" - }, - "ValidateMeshHasOverlappingUVs": { - "enabled": false - } -} diff --git a/pype/tools/config_setting/config/project_presets/plugins/maya/workfile_build.json b/pype/tools/config_setting/config/project_presets/plugins/maya/workfile_build.json deleted file mode 100644 index 2872b783cb..0000000000 --- a/pype/tools/config_setting/config/project_presets/plugins/maya/workfile_build.json +++ /dev/null @@ -1,54 +0,0 @@ -[{ - "tasks": ["lighting"], - - "current_context": [{ - "subset_name_filters": [".+[Mm]ain"], - "families": ["model"], - "repre_names": ["abc", "ma"], - "loaders": ["ReferenceLoader"] - }, { - "families": ["animation", "pointcache"], - "repre_names": ["abc"], - "loaders": ["ReferenceLoader"] - },{ - "families": ["rendersetup"], - "repre_names": ["json"], - "loaders": ["RenderSetupLoader"] - }, { - "families": ["camera"], - "repre_names": ["abc"], - "loaders": ["ReferenceLoader"] - }], - - "linked_assets": [{ - "families": ["setdress"], - "repre_names": ["ma"], - "loaders": ["ReferenceLoader"] - }, { - "families": ["ass"], - "repre_names": ["ass"], - "loaders":["assLoader"] - }] -}, { - "tasks": ["animation"], - - "current_context": [{ - "families": ["camera"], - "repre_names": ["abc", "ma"], - "loaders": ["ReferenceLoader"] - }, { - "families": ["audio"], - "repre_names": ["wav"], - "loaders": ["RenderSetupLoader"] - }], - - "linked_assets": [{ - "families": ["setdress"], - "repre_names": ["proxy"], - "loaders": ["ReferenceLoader"] - }, { - "families": ["rig"], - "repre_names": ["ass"], - "loaders": ["rigLoader"] - }] -}] diff --git a/pype/tools/config_setting/config/project_presets/plugins/nuke/create.json b/pype/tools/config_setting/config/project_presets/plugins/nuke/create.json deleted file mode 100644 index 4deb0b4ad5..0000000000 --- a/pype/tools/config_setting/config/project_presets/plugins/nuke/create.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "CreateWriteRender": { - "fpath_template": "{work}/renders/nuke/{subset}/{subset}.{frame}.{ext}" - }, - "CreateWritePrerender": { - "fpath_template": "{work}/prerenders/nuke/{subset}/{subset}.{frame}.{ext}" - } -} diff --git a/pype/tools/config_setting/config/project_presets/plugins/nuke/load.json b/pype/tools/config_setting/config/project_presets/plugins/nuke/load.json deleted file mode 100644 index 0967ef424b..0000000000 --- a/pype/tools/config_setting/config/project_presets/plugins/nuke/load.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/pype/tools/config_setting/config/project_presets/plugins/nuke/publish.json b/pype/tools/config_setting/config/project_presets/plugins/nuke/publish.json deleted file mode 100644 index ab0d0e76a5..0000000000 --- a/pype/tools/config_setting/config/project_presets/plugins/nuke/publish.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "ExtractThumbnail": { - "nodes": { - "Reformat": [ - ["type", "to format"], - ["format", "HD_1080"], - ["filter", "Lanczos6"], - ["black_outside", true], - ["pbb", false] - ] - } - }, - "ValidateNukeWriteKnobs": { - "enabled": false, - "knobs": { - "render": { - "review": true - } - } - }, - "ExtractReviewDataLut": { - "__documentation__": { - "viewer_lut_raw": "set to `true` if you Input_process node on viewer is used with baked screen space, so you have to look with RAW viewer lut. Else just keep it on `false`", - "enabled": "keep in on `false` if you want Nuke baking colorspace workflow applied else FFmpeg will convert imgsequence with baked LUT" - }, - "enabled": false - }, - "ExtractReviewDataMov": { - "__documentation__": { - "viewer_lut_raw": "set to `true` if you Input_process node on viewer is used with baked screen space, so you have to look with RAW viewer lut. Else just keep it on `false`", - "enabled": "keep in on `true` if you want Nuke baking colorspace workflow applied" - }, - "enabled": true, - "viewer_lut_raw": false - }, - "ExtractSlateFrame": { - "__documentation__": { - "viewer_lut_raw": "set to `true` if you Input_process node on viewer is used with baked screen space, so you have to look with RAW viewer lut. Else just keep it on `false`" - }, - "viewer_lut_raw": false - }, - "NukeSubmitDeadline": { - "deadline_priority": 50, - "deadline_pool": "", - "deadline_pool_secondary": "", - "deadline_chunk_size": 1 - } -} diff --git a/pype/tools/config_setting/config/project_presets/plugins/nuke/workfile_build.json b/pype/tools/config_setting/config/project_presets/plugins/nuke/workfile_build.json deleted file mode 100644 index d3613c929e..0000000000 --- a/pype/tools/config_setting/config/project_presets/plugins/nuke/workfile_build.json +++ /dev/null @@ -1,11 +0,0 @@ -[{ - "tasks": ["compositing"], - - "current_context": [{ - "families": ["render", "plate"], - "repre_names": ["exr" ,"dpx"], - "loaders": ["LoadSequence"] - }], - - "linked_assets": [] -}] diff --git a/pype/tools/config_setting/config/project_presets/plugins/nukestudio/filter.json b/pype/tools/config_setting/config/project_presets/plugins/nukestudio/filter.json deleted file mode 100644 index bd6a0dc1bd..0000000000 --- a/pype/tools/config_setting/config/project_presets/plugins/nukestudio/filter.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "strict": { - "ValidateVersion": true, - "VersionUpWorkfile": true - }, - "benevolent": { - "ValidateVersion": false, - "VersionUpWorkfile": false - } -} \ No newline at end of file diff --git a/pype/tools/config_setting/config/project_presets/plugins/nukestudio/publish.json b/pype/tools/config_setting/config/project_presets/plugins/nukestudio/publish.json deleted file mode 100644 index 8c4ad133f1..0000000000 --- a/pype/tools/config_setting/config/project_presets/plugins/nukestudio/publish.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "CollectInstanceVersion": { - "enabled": false - }, - "ExtractReviewCutUpVideo": { - "tags_addition": [] - } -} diff --git a/pype/tools/config_setting/config/project_presets/plugins/standalonepublisher/publish.json b/pype/tools/config_setting/config/project_presets/plugins/standalonepublisher/publish.json deleted file mode 100644 index ecfff12db9..0000000000 --- a/pype/tools/config_setting/config/project_presets/plugins/standalonepublisher/publish.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "ExtractReviewSP": { - "outputs": { - "h264": { - "input": [ - "-gamma 2.2" - ], - "output": [ - "-pix_fmt yuv420p", - "-crf 18" - ], - "tags": ["preview"], - "ext": "mov" - } - } - } -} diff --git a/pype/tools/config_setting/config/project_presets/plugins/test/create.json b/pype/tools/config_setting/config/project_presets/plugins/test/create.json deleted file mode 100644 index fa0b2fc05f..0000000000 --- a/pype/tools/config_setting/config/project_presets/plugins/test/create.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "MyTestCreator": { - "my_test_property": "B", - "active": false, - "new_property": "new", - "family": "new_family" - } -} diff --git a/pype/tools/config_setting/config/project_presets/plugins/test/publish.json b/pype/tools/config_setting/config/project_presets/plugins/test/publish.json deleted file mode 100644 index 3180dd5d8a..0000000000 --- a/pype/tools/config_setting/config/project_presets/plugins/test/publish.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "MyTestPlugin": { - "label": "loaded from preset", - "optional": true, - "families": ["changed", "by", "preset"] - }, - "MyTestRemovedPlugin": { - "enabled": false - } -} diff --git a/pype/tools/config_setting/config/project_presets/premiere/asset_default.json b/pype/tools/config_setting/config/project_presets/premiere/asset_default.json deleted file mode 100644 index 84d2bde3d8..0000000000 --- a/pype/tools/config_setting/config/project_presets/premiere/asset_default.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "frameStart": 1001, - "handleStart": 0, - "handleEnd": 0 -} diff --git a/pype/tools/config_setting/config/project_presets/premiere/rules_tasks.json b/pype/tools/config_setting/config/project_presets/premiere/rules_tasks.json deleted file mode 100644 index 333c9cd70b..0000000000 --- a/pype/tools/config_setting/config/project_presets/premiere/rules_tasks.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "defaultTasks": ["Layout", "Animation"], - "taskToSubsets": { - "Layout": ["reference", "audio"], - "Animation": ["audio"] - }, - "subsetToRepresentations": { - "reference": { - "preset": "h264", - "representation": "mp4" - }, - "thumbnail": { - "preset": "jpeg_thumb", - "representation": "jpg" - }, - "audio": { - "preset": "48khz", - "representation": "wav" - } - } -} diff --git a/pype/tools/config_setting/config/project_presets/unreal/project_setup.json b/pype/tools/config_setting/config/project_presets/unreal/project_setup.json deleted file mode 100644 index 8a4dffc526..0000000000 --- a/pype/tools/config_setting/config/project_presets/unreal/project_setup.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "dev_mode": false, - "install_unreal_python_engine": false -} From f2bb1af12836a9a298212525da6b9ea0a02e1215 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 9 Sep 2020 10:03:35 +0200 Subject: [PATCH 465/813] moved config to folder configurations --- pype/api.py | 2 +- pype/{ => configurations}/config.py | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename pype/{ => configurations}/config.py (100%) diff --git a/pype/api.py b/pype/api.py index e2705f81ea..88dfab19ec 100644 --- a/pype/api.py +++ b/pype/api.py @@ -1,4 +1,4 @@ -from . import config +from .configurations import config from pypeapp import ( Logger, Anatomy, diff --git a/pype/config.py b/pype/configurations/config.py similarity index 100% rename from pype/config.py rename to pype/configurations/config.py From d5e7ccce3f9dbf3b9c5b76180d4cd256e8c0c44d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 9 Sep 2020 10:04:37 +0200 Subject: [PATCH 466/813] copied pype-config to pype --- .../configurations/defaults/anatomy/README.md | 0 .../defaults/anatomy/default.yaml | 29 ++ .../defaults/anatomy/roots.json | 5 + .../defaults/environments/avalon.json | 16 ++ .../defaults/environments/blender.json | 7 + .../defaults/environments/celaction.json | 3 + .../defaults/environments/deadline.json | 3 + .../defaults/environments/ftrack.json | 18 ++ .../defaults/environments/global.json | 44 +++ .../defaults/environments/harmony.json | 4 + .../defaults/environments/houdini.json | 12 + .../defaults/environments/maya.json | 14 + .../defaults/environments/maya_2018.json | 11 + .../defaults/environments/maya_2020.json | 11 + .../defaults/environments/mayabatch.json | 14 + .../defaults/environments/mayabatch_2019.json | 11 + .../defaults/environments/mtoa_3.1.1.json | 23 ++ .../defaults/environments/muster.json | 3 + .../defaults/environments/nuke.json | 15 + .../defaults/environments/nukestudio.json | 11 + .../environments/nukestudio_10.0.json | 4 + .../defaults/environments/nukex.json | 10 + .../defaults/environments/nukex_10.0.json | 4 + .../defaults/environments/photoshop.json | 4 + .../defaults/environments/premiere.json | 11 + .../defaults/environments/resolve.json | 40 +++ .../defaults/environments/storyboardpro.json | 4 + .../defaults/environments/unreal_4.24.json | 5 + .../defaults/environments/vray_4300.json | 15 + .../defaults/launchers/blender_2.80.toml | 7 + .../defaults/launchers/blender_2.81.toml | 7 + .../defaults/launchers/blender_2.82.toml | 7 + .../defaults/launchers/blender_2.83.toml | 7 + .../defaults/launchers/celaction_local.toml | 8 + .../defaults/launchers/celaction_publish.toml | 7 + .../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 | 8 + .../defaults/launchers/houdini_16.toml | 7 + .../defaults/launchers/houdini_17.toml | 7 + .../defaults/launchers/houdini_18.toml | 7 + .../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 | 26 ++ .../defaults/launchers/maya_2017.toml | 28 ++ .../defaults/launchers/maya_2018.toml | 14 + .../defaults/launchers/maya_2019.toml | 14 + .../defaults/launchers/maya_2020.toml | 14 + .../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 ++ .../defaults/launchers/myapp.toml | 5 + .../defaults/launchers/nuke_10.0.toml | 7 + .../defaults/launchers/nuke_11.0.toml | 7 + .../defaults/launchers/nuke_11.2.toml | 7 + .../defaults/launchers/nuke_11.3.toml | 7 + .../defaults/launchers/nuke_12.0.toml | 7 + .../defaults/launchers/nukestudio_10.0.toml | 7 + .../defaults/launchers/nukestudio_11.0.toml | 7 + .../defaults/launchers/nukestudio_11.2.toml | 7 + .../defaults/launchers/nukestudio_11.3.toml | 7 + .../defaults/launchers/nukestudio_12.0.toml | 7 + .../defaults/launchers/nukex_10.0.toml | 7 + .../defaults/launchers/nukex_11.0.toml | 7 + .../defaults/launchers/nukex_11.2.toml | 7 + .../defaults/launchers/nukex_11.3.toml | 7 + .../defaults/launchers/nukex_12.0.toml | 7 + .../defaults/launchers/photoshop_2020.toml | 8 + .../defaults/launchers/premiere_2019.toml | 8 + .../defaults/launchers/premiere_2020.toml | 9 + .../defaults/launchers/python_2.toml | 10 + .../defaults/launchers/python_3.toml | 10 + .../defaults/launchers/resolve_16.toml | 9 + .../defaults/launchers/shell.toml | 7 + .../defaults/launchers/storyboardpro_7.toml | 8 + .../defaults/launchers/unreal_4.24.toml | 8 + .../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 + .../presets/colorspace/aces103-cg.json | 46 +++ .../defaults/presets/colorspace/default.json | 42 +++ .../defaults/presets/dataflow/aces-exr.json | 58 ++++ .../defaults/presets/dataflow/default.json | 55 ++++ .../presets/ftrack/ftrack_config.json | 16 ++ .../ftrack/ftrack_custom_attributes.json | 165 +++++++++++ .../ftrack/partnership_ftrack_cred.json | 5 + .../presets/ftrack/plugins/server.json | 1 + .../defaults/presets/ftrack/plugins/user.json | 5 + .../presets/ftrack/project_defaults.json | 18 ++ .../configurations/defaults/presets/init.json | 4 + .../defaults/presets/maya/capture.json | 108 ++++++++ .../presets/muster/templates_mapping.json | 19 ++ .../defaults/presets/nukestudio/tags.json | 262 ++++++++++++++++++ .../presets/plugins/celaction/publish.json | 10 + .../defaults/presets/plugins/config.json | 1 + .../presets/plugins/ftrack/publish.json | 6 + .../presets/plugins/global/create.json | 1 + .../presets/plugins/global/filter.json | 1 + .../defaults/presets/plugins/global/load.json | 1 + .../presets/plugins/global/publish.json | 86 ++++++ .../defaults/presets/plugins/maya/create.json | 1 + .../defaults/presets/plugins/maya/filter.json | 9 + .../defaults/presets/plugins/maya/load.json | 18 ++ .../presets/plugins/maya/publish.json | 17 ++ .../presets/plugins/maya/workfile_build.json | 54 ++++ .../defaults/presets/plugins/nuke/create.json | 8 + .../defaults/presets/plugins/nuke/load.json | 1 + .../presets/plugins/nuke/publish.json | 48 ++++ .../presets/plugins/nuke/workfile_build.json | 11 + .../presets/plugins/nukestudio/filter.json | 10 + .../presets/plugins/nukestudio/publish.json | 8 + .../presets/plugins/resolve/create.json | 7 + .../plugins/standalonepublisher/publish.json | 25 ++ .../defaults/presets/plugins/test/create.json | 8 + .../presets/plugins/test/publish.json | 10 + .../presets/premiere/asset_default.json | 5 + .../presets/premiere/rules_tasks.json | 21 ++ .../presets/standalone_publish/families.json | 90 ++++++ .../defaults/presets/tools/creator.json | 8 + .../tools/project_folder_structure.json | 22 ++ .../defaults/presets/tools/pyblish.json | 17 ++ .../presets/tools/slates/example_HD.json | 212 ++++++++++++++ .../defaults/presets/tools/sw_folders.json | 8 + .../defaults/presets/tools/workfiles.json | 7 + .../defaults/presets/tray/menu_items.json | 28 ++ .../presets/unreal/project_setup.json | 4 + .../plugins/celaction/publish.json | 11 + .../plugins/config.json | 1 + .../plugins/ftrack/publish.json | 7 + .../plugins/global/create.json | 1 + .../plugins/global/filter.json | 1 + .../plugins/global/load.json | 1 + .../plugins/global/publish.json | 97 +++++++ .../plugins/maya/create.json | 1 + .../plugins/maya/filter.json | 9 + .../plugins/maya/load.json | 18 ++ .../plugins/maya/publish.json | 17 ++ .../plugins/maya/workfile_build.json | 136 +++++++++ .../plugins/nuke/create.json | 8 + .../plugins/nuke/load.json | 1 + .../plugins/nuke/publish.json | 53 ++++ .../plugins/nuke/workfile_build.json | 23 ++ .../plugins/nukestudio/filter.json | 10 + .../plugins/nukestudio/publish.json | 9 + .../plugins/resolve/create.json | 7 + .../plugins/standalonepublisher/publish.json | 27 ++ .../plugins/test/create.json | 8 + .../plugins/test/publish.json | 10 + .../global/applications.json | 39 +++ .../studio_configurations/global/intent.json | 8 + .../studio_configurations/global/tools.json | 6 + .../global/tray_modules.json | 28 ++ .../muster/templates_mapping.json | 19 ++ .../standalone_publish/families.json | 90 ++++++ 211 files changed, 3697 insertions(+) create mode 100644 pype/configurations/defaults/anatomy/README.md create mode 100644 pype/configurations/defaults/anatomy/default.yaml create mode 100644 pype/configurations/defaults/anatomy/roots.json create mode 100644 pype/configurations/defaults/environments/avalon.json create mode 100644 pype/configurations/defaults/environments/blender.json create mode 100644 pype/configurations/defaults/environments/celaction.json create mode 100644 pype/configurations/defaults/environments/deadline.json create mode 100644 pype/configurations/defaults/environments/ftrack.json create mode 100644 pype/configurations/defaults/environments/global.json create mode 100644 pype/configurations/defaults/environments/harmony.json create mode 100644 pype/configurations/defaults/environments/houdini.json create mode 100644 pype/configurations/defaults/environments/maya.json create mode 100644 pype/configurations/defaults/environments/maya_2018.json create mode 100644 pype/configurations/defaults/environments/maya_2020.json create mode 100644 pype/configurations/defaults/environments/mayabatch.json create mode 100644 pype/configurations/defaults/environments/mayabatch_2019.json create mode 100644 pype/configurations/defaults/environments/mtoa_3.1.1.json create mode 100644 pype/configurations/defaults/environments/muster.json create mode 100644 pype/configurations/defaults/environments/nuke.json create mode 100644 pype/configurations/defaults/environments/nukestudio.json create mode 100644 pype/configurations/defaults/environments/nukestudio_10.0.json create mode 100644 pype/configurations/defaults/environments/nukex.json create mode 100644 pype/configurations/defaults/environments/nukex_10.0.json create mode 100644 pype/configurations/defaults/environments/photoshop.json create mode 100644 pype/configurations/defaults/environments/premiere.json create mode 100644 pype/configurations/defaults/environments/resolve.json create mode 100644 pype/configurations/defaults/environments/storyboardpro.json create mode 100644 pype/configurations/defaults/environments/unreal_4.24.json create mode 100644 pype/configurations/defaults/environments/vray_4300.json create mode 100644 pype/configurations/defaults/launchers/blender_2.80.toml create mode 100644 pype/configurations/defaults/launchers/blender_2.81.toml create mode 100644 pype/configurations/defaults/launchers/blender_2.82.toml create mode 100644 pype/configurations/defaults/launchers/blender_2.83.toml create mode 100644 pype/configurations/defaults/launchers/celaction_local.toml create mode 100644 pype/configurations/defaults/launchers/celaction_publish.toml create mode 100644 pype/configurations/defaults/launchers/darwin/blender_2.82 create mode 100644 pype/configurations/defaults/launchers/darwin/harmony_17 create mode 100644 pype/configurations/defaults/launchers/darwin/harmony_17_launch create mode 100644 pype/configurations/defaults/launchers/darwin/python3 create mode 100644 pype/configurations/defaults/launchers/harmony_17.toml create mode 100644 pype/configurations/defaults/launchers/houdini_16.toml create mode 100644 pype/configurations/defaults/launchers/houdini_17.toml create mode 100644 pype/configurations/defaults/launchers/houdini_18.toml create mode 100644 pype/configurations/defaults/launchers/linux/maya2016 create mode 100644 pype/configurations/defaults/launchers/linux/maya2017 create mode 100644 pype/configurations/defaults/launchers/linux/maya2018 create mode 100644 pype/configurations/defaults/launchers/linux/maya2019 create mode 100644 pype/configurations/defaults/launchers/linux/maya2020 create mode 100644 pype/configurations/defaults/launchers/linux/nuke11.3 create mode 100644 pype/configurations/defaults/launchers/linux/nuke12.0 create mode 100644 pype/configurations/defaults/launchers/linux/nukestudio11.3 create mode 100644 pype/configurations/defaults/launchers/linux/nukestudio12.0 create mode 100644 pype/configurations/defaults/launchers/linux/nukex11.3 create mode 100644 pype/configurations/defaults/launchers/linux/nukex12.0 create mode 100644 pype/configurations/defaults/launchers/maya_2016.toml create mode 100644 pype/configurations/defaults/launchers/maya_2017.toml create mode 100644 pype/configurations/defaults/launchers/maya_2018.toml create mode 100644 pype/configurations/defaults/launchers/maya_2019.toml create mode 100644 pype/configurations/defaults/launchers/maya_2020.toml create mode 100644 pype/configurations/defaults/launchers/mayabatch_2019.toml create mode 100644 pype/configurations/defaults/launchers/mayabatch_2020.toml create mode 100644 pype/configurations/defaults/launchers/mayapy2016.toml create mode 100644 pype/configurations/defaults/launchers/mayapy2017.toml create mode 100644 pype/configurations/defaults/launchers/mayapy2018.toml create mode 100644 pype/configurations/defaults/launchers/mayapy2019.toml create mode 100644 pype/configurations/defaults/launchers/mayapy2020.toml create mode 100644 pype/configurations/defaults/launchers/myapp.toml create mode 100644 pype/configurations/defaults/launchers/nuke_10.0.toml create mode 100644 pype/configurations/defaults/launchers/nuke_11.0.toml create mode 100644 pype/configurations/defaults/launchers/nuke_11.2.toml create mode 100644 pype/configurations/defaults/launchers/nuke_11.3.toml create mode 100644 pype/configurations/defaults/launchers/nuke_12.0.toml create mode 100644 pype/configurations/defaults/launchers/nukestudio_10.0.toml create mode 100644 pype/configurations/defaults/launchers/nukestudio_11.0.toml create mode 100644 pype/configurations/defaults/launchers/nukestudio_11.2.toml create mode 100644 pype/configurations/defaults/launchers/nukestudio_11.3.toml create mode 100644 pype/configurations/defaults/launchers/nukestudio_12.0.toml create mode 100644 pype/configurations/defaults/launchers/nukex_10.0.toml create mode 100644 pype/configurations/defaults/launchers/nukex_11.0.toml create mode 100644 pype/configurations/defaults/launchers/nukex_11.2.toml create mode 100644 pype/configurations/defaults/launchers/nukex_11.3.toml create mode 100644 pype/configurations/defaults/launchers/nukex_12.0.toml create mode 100644 pype/configurations/defaults/launchers/photoshop_2020.toml create mode 100644 pype/configurations/defaults/launchers/premiere_2019.toml create mode 100644 pype/configurations/defaults/launchers/premiere_2020.toml create mode 100644 pype/configurations/defaults/launchers/python_2.toml create mode 100644 pype/configurations/defaults/launchers/python_3.toml create mode 100644 pype/configurations/defaults/launchers/resolve_16.toml create mode 100644 pype/configurations/defaults/launchers/shell.toml create mode 100644 pype/configurations/defaults/launchers/storyboardpro_7.toml create mode 100644 pype/configurations/defaults/launchers/unreal_4.24.toml create mode 100644 pype/configurations/defaults/launchers/windows/blender_2.80.bat create mode 100644 pype/configurations/defaults/launchers/windows/blender_2.81.bat create mode 100644 pype/configurations/defaults/launchers/windows/blender_2.82.bat create mode 100644 pype/configurations/defaults/launchers/windows/blender_2.83.bat create mode 100644 pype/configurations/defaults/launchers/windows/celaction_local.bat create mode 100644 pype/configurations/defaults/launchers/windows/celaction_publish.bat create mode 100644 pype/configurations/defaults/launchers/windows/harmony_17.bat create mode 100644 pype/configurations/defaults/launchers/windows/houdini_16.bat create mode 100644 pype/configurations/defaults/launchers/windows/houdini_17.bat create mode 100644 pype/configurations/defaults/launchers/windows/houdini_18.bat create mode 100644 pype/configurations/defaults/launchers/windows/maya2016.bat create mode 100644 pype/configurations/defaults/launchers/windows/maya2017.bat create mode 100644 pype/configurations/defaults/launchers/windows/maya2018.bat create mode 100644 pype/configurations/defaults/launchers/windows/maya2019.bat create mode 100644 pype/configurations/defaults/launchers/windows/maya2020.bat create mode 100644 pype/configurations/defaults/launchers/windows/mayabatch2019.bat create mode 100644 pype/configurations/defaults/launchers/windows/mayabatch2020.bat create mode 100644 pype/configurations/defaults/launchers/windows/mayapy2016.bat create mode 100644 pype/configurations/defaults/launchers/windows/mayapy2017.bat create mode 100644 pype/configurations/defaults/launchers/windows/mayapy2018.bat create mode 100644 pype/configurations/defaults/launchers/windows/mayapy2019.bat create mode 100644 pype/configurations/defaults/launchers/windows/mayapy2020.bat create mode 100644 pype/configurations/defaults/launchers/windows/nuke10.0.bat create mode 100644 pype/configurations/defaults/launchers/windows/nuke11.0.bat create mode 100644 pype/configurations/defaults/launchers/windows/nuke11.2.bat create mode 100644 pype/configurations/defaults/launchers/windows/nuke11.3.bat create mode 100644 pype/configurations/defaults/launchers/windows/nuke12.0.bat create mode 100644 pype/configurations/defaults/launchers/windows/nukestudio10.0.bat create mode 100644 pype/configurations/defaults/launchers/windows/nukestudio11.0.bat create mode 100644 pype/configurations/defaults/launchers/windows/nukestudio11.2.bat create mode 100644 pype/configurations/defaults/launchers/windows/nukestudio11.3.bat create mode 100644 pype/configurations/defaults/launchers/windows/nukestudio12.0.bat create mode 100644 pype/configurations/defaults/launchers/windows/nukex10.0.bat create mode 100644 pype/configurations/defaults/launchers/windows/nukex11.0.bat create mode 100644 pype/configurations/defaults/launchers/windows/nukex11.2.bat create mode 100644 pype/configurations/defaults/launchers/windows/nukex11.3.bat create mode 100644 pype/configurations/defaults/launchers/windows/nukex12.0.bat create mode 100644 pype/configurations/defaults/launchers/windows/photoshop_2020.bat create mode 100644 pype/configurations/defaults/launchers/windows/premiere_pro_2019.bat create mode 100644 pype/configurations/defaults/launchers/windows/premiere_pro_2020.bat create mode 100644 pype/configurations/defaults/launchers/windows/python3.bat create mode 100644 pype/configurations/defaults/launchers/windows/resolve_16.bat create mode 100644 pype/configurations/defaults/launchers/windows/shell.bat create mode 100644 pype/configurations/defaults/launchers/windows/storyboardpro_7.bat create mode 100644 pype/configurations/defaults/launchers/windows/unreal.bat create mode 100644 pype/configurations/defaults/presets/colorspace/aces103-cg.json create mode 100644 pype/configurations/defaults/presets/colorspace/default.json create mode 100644 pype/configurations/defaults/presets/dataflow/aces-exr.json create mode 100644 pype/configurations/defaults/presets/dataflow/default.json create mode 100644 pype/configurations/defaults/presets/ftrack/ftrack_config.json create mode 100644 pype/configurations/defaults/presets/ftrack/ftrack_custom_attributes.json create mode 100644 pype/configurations/defaults/presets/ftrack/partnership_ftrack_cred.json create mode 100644 pype/configurations/defaults/presets/ftrack/plugins/server.json create mode 100644 pype/configurations/defaults/presets/ftrack/plugins/user.json create mode 100644 pype/configurations/defaults/presets/ftrack/project_defaults.json create mode 100644 pype/configurations/defaults/presets/init.json create mode 100644 pype/configurations/defaults/presets/maya/capture.json create mode 100644 pype/configurations/defaults/presets/muster/templates_mapping.json create mode 100644 pype/configurations/defaults/presets/nukestudio/tags.json create mode 100644 pype/configurations/defaults/presets/plugins/celaction/publish.json create mode 100644 pype/configurations/defaults/presets/plugins/config.json create mode 100644 pype/configurations/defaults/presets/plugins/ftrack/publish.json create mode 100644 pype/configurations/defaults/presets/plugins/global/create.json create mode 100644 pype/configurations/defaults/presets/plugins/global/filter.json create mode 100644 pype/configurations/defaults/presets/plugins/global/load.json create mode 100644 pype/configurations/defaults/presets/plugins/global/publish.json create mode 100644 pype/configurations/defaults/presets/plugins/maya/create.json create mode 100644 pype/configurations/defaults/presets/plugins/maya/filter.json create mode 100644 pype/configurations/defaults/presets/plugins/maya/load.json create mode 100644 pype/configurations/defaults/presets/plugins/maya/publish.json create mode 100644 pype/configurations/defaults/presets/plugins/maya/workfile_build.json create mode 100644 pype/configurations/defaults/presets/plugins/nuke/create.json create mode 100644 pype/configurations/defaults/presets/plugins/nuke/load.json create mode 100644 pype/configurations/defaults/presets/plugins/nuke/publish.json create mode 100644 pype/configurations/defaults/presets/plugins/nuke/workfile_build.json create mode 100644 pype/configurations/defaults/presets/plugins/nukestudio/filter.json create mode 100644 pype/configurations/defaults/presets/plugins/nukestudio/publish.json create mode 100644 pype/configurations/defaults/presets/plugins/resolve/create.json create mode 100644 pype/configurations/defaults/presets/plugins/standalonepublisher/publish.json create mode 100644 pype/configurations/defaults/presets/plugins/test/create.json create mode 100644 pype/configurations/defaults/presets/plugins/test/publish.json create mode 100644 pype/configurations/defaults/presets/premiere/asset_default.json create mode 100644 pype/configurations/defaults/presets/premiere/rules_tasks.json create mode 100644 pype/configurations/defaults/presets/standalone_publish/families.json create mode 100644 pype/configurations/defaults/presets/tools/creator.json create mode 100644 pype/configurations/defaults/presets/tools/project_folder_structure.json create mode 100644 pype/configurations/defaults/presets/tools/pyblish.json create mode 100644 pype/configurations/defaults/presets/tools/slates/example_HD.json create mode 100644 pype/configurations/defaults/presets/tools/sw_folders.json create mode 100644 pype/configurations/defaults/presets/tools/workfiles.json create mode 100644 pype/configurations/defaults/presets/tray/menu_items.json create mode 100644 pype/configurations/defaults/presets/unreal/project_setup.json create mode 100644 pype/configurations/defaults/project_configurations/plugins/celaction/publish.json create mode 100644 pype/configurations/defaults/project_configurations/plugins/config.json create mode 100644 pype/configurations/defaults/project_configurations/plugins/ftrack/publish.json create mode 100644 pype/configurations/defaults/project_configurations/plugins/global/create.json create mode 100644 pype/configurations/defaults/project_configurations/plugins/global/filter.json create mode 100644 pype/configurations/defaults/project_configurations/plugins/global/load.json create mode 100644 pype/configurations/defaults/project_configurations/plugins/global/publish.json create mode 100644 pype/configurations/defaults/project_configurations/plugins/maya/create.json create mode 100644 pype/configurations/defaults/project_configurations/plugins/maya/filter.json create mode 100644 pype/configurations/defaults/project_configurations/plugins/maya/load.json create mode 100644 pype/configurations/defaults/project_configurations/plugins/maya/publish.json create mode 100644 pype/configurations/defaults/project_configurations/plugins/maya/workfile_build.json create mode 100644 pype/configurations/defaults/project_configurations/plugins/nuke/create.json create mode 100644 pype/configurations/defaults/project_configurations/plugins/nuke/load.json create mode 100644 pype/configurations/defaults/project_configurations/plugins/nuke/publish.json create mode 100644 pype/configurations/defaults/project_configurations/plugins/nuke/workfile_build.json create mode 100644 pype/configurations/defaults/project_configurations/plugins/nukestudio/filter.json create mode 100644 pype/configurations/defaults/project_configurations/plugins/nukestudio/publish.json create mode 100644 pype/configurations/defaults/project_configurations/plugins/resolve/create.json create mode 100644 pype/configurations/defaults/project_configurations/plugins/standalonepublisher/publish.json create mode 100644 pype/configurations/defaults/project_configurations/plugins/test/create.json create mode 100644 pype/configurations/defaults/project_configurations/plugins/test/publish.json create mode 100644 pype/configurations/defaults/studio_configurations/global/applications.json create mode 100644 pype/configurations/defaults/studio_configurations/global/intent.json create mode 100644 pype/configurations/defaults/studio_configurations/global/tools.json create mode 100644 pype/configurations/defaults/studio_configurations/global/tray_modules.json create mode 100644 pype/configurations/defaults/studio_configurations/muster/templates_mapping.json create mode 100644 pype/configurations/defaults/studio_configurations/standalone_publish/families.json diff --git a/pype/configurations/defaults/anatomy/README.md b/pype/configurations/defaults/anatomy/README.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/pype/configurations/defaults/anatomy/default.yaml b/pype/configurations/defaults/anatomy/default.yaml new file mode 100644 index 0000000000..381aa05d4a --- /dev/null +++ b/pype/configurations/defaults/anatomy/default.yaml @@ -0,0 +1,29 @@ + +version_padding: 3 +version: "v{version:0>{@version_padding}}" +frame_padding: 4 +frame: "{frame:0>{@frame_padding}}" + +work: + folder: "{root}/{project[name]}/{hierarchy}/{asset}/work/{task}" + file: "{project[code]}_{asset}_{task}_{@version}<_{comment}>.{ext}" + path: "{@folder}/{@file}" + +render: + folder: "{root}/{project[name]}/{hierarchy}/{asset}/publish/render/{subset}/{@version}" + file: "{project[code]}_{asset}_{subset}_{@version}<_{output}><.{@frame}>.{representation}" + path: "{@folder}/{@file}" + +texture: + path: "{root}/{project[name]}/{hierarchy}/{asset}/publish/{family}/{subset}" + +publish: + folder: "{root}/{project[name]}/{hierarchy}/{asset}/publish/{family}/{subset}/{@version}" + file: "{project[code]}_{asset}_{subset}_{@version}<_{output}><.{@frame}>.{representation}" + path: "{@folder}/{@file}" + thumbnail: "{thumbnail_root}/{project[name]}/{_id}_{thumbnail_type}{ext}" + +master: + folder: "{root}/{project[name]}/{hierarchy}/{asset}/publish/{family}/{subset}/master" + file: "{project[code]}_{asset}_{subset}_master<_{output}><.{frame}>.{representation}" + path: "{@folder}/{@file}" diff --git a/pype/configurations/defaults/anatomy/roots.json b/pype/configurations/defaults/anatomy/roots.json new file mode 100644 index 0000000000..0282471a60 --- /dev/null +++ b/pype/configurations/defaults/anatomy/roots.json @@ -0,0 +1,5 @@ +{ + "windows": "C:/projects", + "linux": "/mnt/share/projects", + "darwin": "/Volumes/path" +} diff --git a/pype/configurations/defaults/environments/avalon.json b/pype/configurations/defaults/environments/avalon.json new file mode 100644 index 0000000000..832ba07e71 --- /dev/null +++ b/pype/configurations/defaults/environments/avalon.json @@ -0,0 +1,16 @@ +{ + "AVALON_CONFIG": "pype", + "AVALON_PROJECTS": "{PYPE_PROJECTS_PATH}", + "AVALON_USERNAME": "avalon", + "AVALON_PASSWORD": "secret", + "AVALON_DEBUG": "1", + "AVALON_MONGO": "mongodb://localhost:2707", + "AVALON_DB": "avalon", + "AVALON_DB_DATA": "{PYPE_SETUP_PATH}/../mongo_db_data", + "AVALON_EARLY_ADOPTER": "1", + "AVALON_SCHEMA": "{PYPE_MODULE_ROOT}/schema", + "AVALON_LOCATION": "http://127.0.0.1", + "AVALON_LABEL": "Pype", + "AVALON_TIMEOUT": "1000", + "AVALON_THUMBNAIL_ROOT": "" +} diff --git a/pype/configurations/defaults/environments/blender.json b/pype/configurations/defaults/environments/blender.json new file mode 100644 index 0000000000..6f4f6a012d --- /dev/null +++ b/pype/configurations/defaults/environments/blender.json @@ -0,0 +1,7 @@ +{ + "BLENDER_USER_SCRIPTS": "{PYPE_SETUP_PATH}/repos/avalon-core/setup/blender", + "PYTHONPATH": [ + "{PYPE_SETUP_PATH}/repos/avalon-core/setup/blender", + "{PYTHONPATH}" + ] +} diff --git a/pype/configurations/defaults/environments/celaction.json b/pype/configurations/defaults/environments/celaction.json new file mode 100644 index 0000000000..cdd4e609ab --- /dev/null +++ b/pype/configurations/defaults/environments/celaction.json @@ -0,0 +1,3 @@ +{ + "CELACTION_TEMPLATE": "{PYPE_MODULE_ROOT}/pype/hosts/celaction/celaction_template_scene.scn" +} diff --git a/pype/configurations/defaults/environments/deadline.json b/pype/configurations/defaults/environments/deadline.json new file mode 100644 index 0000000000..e8ef52805b --- /dev/null +++ b/pype/configurations/defaults/environments/deadline.json @@ -0,0 +1,3 @@ +{ + "DEADLINE_REST_URL": "http://localhost:8082" +} diff --git a/pype/configurations/defaults/environments/ftrack.json b/pype/configurations/defaults/environments/ftrack.json new file mode 100644 index 0000000000..4f25de027b --- /dev/null +++ b/pype/configurations/defaults/environments/ftrack.json @@ -0,0 +1,18 @@ +{ + "FTRACK_SERVER": "https://pype.ftrackapp.com", + "FTRACK_ACTIONS_PATH": [ + "{PYPE_MODULE_ROOT}/pype/modules/ftrack/actions" + ], + "FTRACK_EVENTS_PATH": [ + "{PYPE_MODULE_ROOT}/pype/modules/ftrack/events" + ], + "PYTHONPATH": [ + "{PYPE_MODULE_ROOT}/pype/vendor", + "{PYTHONPATH}" + ], + "PYBLISHPLUGINPATH": [ + "{PYPE_MODULE_ROOT}/pype/plugins/ftrack/publish" + ], + "FTRACK_EVENTS_MONGO_DB": "pype", + "FTRACK_EVENTS_MONGO_COL": "ftrack_events" +} diff --git a/pype/configurations/defaults/environments/global.json b/pype/configurations/defaults/environments/global.json new file mode 100644 index 0000000000..ef528e6857 --- /dev/null +++ b/pype/configurations/defaults/environments/global.json @@ -0,0 +1,44 @@ +{ + "PYPE_STUDIO_NAME": "Studio Name", + "PYPE_STUDIO_CODE": "stu", + "PYPE_APP_ROOT": "{PYPE_SETUP_PATH}/pypeapp", + "PYPE_MODULE_ROOT": "{PYPE_SETUP_PATH}/repos/pype", + "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" + }, + "DJV_PATH": { + "windows": [ + "C:/Program Files/djv-1.1.0-Windows-64/bin/djv_view.exe", + "C:/Program Files/DJV/bin/djv_view.exe", + "{STUDIO_SOFT}/djv/windows/bin/djv_view.exe" + ], + "linux": [ + "usr/local/djv/djv_view", + "{STUDIO_SOFT}/djv/linux/bin/djv_view" + ], + "darwin": "Application/DJV.app/Contents/MacOS/DJV" + }, + "PATH": [ + "{PYPE_CONFIG}/launchers", + "{PYPE_APP_ROOT}", + "{FFMPEG_PATH}", + "{PATH}" + ], + "PYPE_OCIO_CONFIG": "{STUDIO_SOFT}/OpenColorIO-Configs", + "PYTHONPATH": { + "windows": "{VIRTUAL_ENV}/Lib/site-packages;{PYPE_MODULE_ROOT}/pype/tools;{PYTHONPATH}", + "linux": "{VIRTUAL_ENV}/lib/python{PYTHON_VERSION}/site-packages:{PYPE_MODULE_ROOT}/pype/tools:{PYTHONPATH}", + "darwin": "{VIRTUAL_ENV}/lib/python{PYTHON_VERSION}/site-packages:{PYPE_MODULE_ROOT}/pype/tools:{PYTHONPATH}" + }, + "PYPE_PROJECT_CONFIGS": "{PYPE_SETUP_PATH}/../studio-project-configs", + "PYPE_PYTHON_EXE": { + "windows": "{VIRTUAL_ENV}/Scripts/python.exe", + "linux": "{VIRTUAL_ENV}/Scripts/python", + "darwin": "{VIRTUAL_ENV}/bin/python" + }, + "PYBLISH_GUI": "pyblish_pype" +} diff --git a/pype/configurations/defaults/environments/harmony.json b/pype/configurations/defaults/environments/harmony.json new file mode 100644 index 0000000000..d394343935 --- /dev/null +++ b/pype/configurations/defaults/environments/harmony.json @@ -0,0 +1,4 @@ +{ + "AVALON_HARMONY_WORKFILES_ON_LAUNCH": "1", + "PYBLISH_GUI_ALWAYS_EXEC": "1" +} diff --git a/pype/configurations/defaults/environments/houdini.json b/pype/configurations/defaults/environments/houdini.json new file mode 100644 index 0000000000..95c7d19088 --- /dev/null +++ b/pype/configurations/defaults/environments/houdini.json @@ -0,0 +1,12 @@ +{ + "HOUDINI_PATH": { + "darwin": "{PYPE_MODULE_ROOT}/setup/houdini:&", + "linux": "{PYPE_MODULE_ROOT}/setup/houdini:&", + "windows": "{PYPE_MODULE_ROOT}/setup/houdini;&" + }, + "HOUDINI_MENU_PATH": { + "darwin": "{PYPE_MODULE_ROOT}/setup/houdini:&", + "linux": "{PYPE_MODULE_ROOT}/setup/houdini:&", + "windows": "{PYPE_MODULE_ROOT}/setup/houdini;&" + } +} diff --git a/pype/configurations/defaults/environments/maya.json b/pype/configurations/defaults/environments/maya.json new file mode 100644 index 0000000000..7785b108f7 --- /dev/null +++ b/pype/configurations/defaults/environments/maya.json @@ -0,0 +1,14 @@ +{ + "PYTHONPATH": [ + "{PYPE_SETUP_PATH}/repos/avalon-core/setup/maya", + "{PYPE_SETUP_PATH}/repos/maya-look-assigner", + "{PYTHON_ENV}/python2/Lib/site-packages", + "{PYTHONPATH}" + ], + "MAYA_DISABLE_CLIC_IPM": "Yes", + "MAYA_DISABLE_CIP": "Yes", + "MAYA_DISABLE_CER": "Yes", + "PYMEL_SKIP_MEL_INIT": "Yes", + "LC_ALL": "C", + "PYPE_LOG_NO_COLORS": "Yes" +} diff --git a/pype/configurations/defaults/environments/maya_2018.json b/pype/configurations/defaults/environments/maya_2018.json new file mode 100644 index 0000000000..72a0c57ce3 --- /dev/null +++ b/pype/configurations/defaults/environments/maya_2018.json @@ -0,0 +1,11 @@ +{ + "MAYA_VERSION": "2018", + "MAYA_LOCATION": { + "darwin": "/Applications/Autodesk/maya{MAYA_VERSION}/Maya.app/Contents", + "linux": "/usr/autodesk/maya{MAYA_VERSION}", + "windows": "C:/Program Files/Autodesk/Maya{MAYA_VERSION}" + }, + "DYLD_LIBRARY_PATH": { + "darwin": "{MAYA_LOCATION}/MacOS" + } +} diff --git a/pype/configurations/defaults/environments/maya_2020.json b/pype/configurations/defaults/environments/maya_2020.json new file mode 100644 index 0000000000..efd0250bc8 --- /dev/null +++ b/pype/configurations/defaults/environments/maya_2020.json @@ -0,0 +1,11 @@ +{ + "MAYA_VERSION": "2020", + "MAYA_LOCATION": { + "darwin": "/Applications/Autodesk/maya{MAYA_VERSION}/Maya.app/Contents", + "linux": "/usr/autodesk/maya{MAYA_VERSION}", + "windows": "C:/Program Files/Autodesk/Maya{MAYA_VERSION}" + }, + "DYLD_LIBRARY_PATH": { + "darwin": "{MAYA_LOCATION}/MacOS" + } +} diff --git a/pype/configurations/defaults/environments/mayabatch.json b/pype/configurations/defaults/environments/mayabatch.json new file mode 100644 index 0000000000..7785b108f7 --- /dev/null +++ b/pype/configurations/defaults/environments/mayabatch.json @@ -0,0 +1,14 @@ +{ + "PYTHONPATH": [ + "{PYPE_SETUP_PATH}/repos/avalon-core/setup/maya", + "{PYPE_SETUP_PATH}/repos/maya-look-assigner", + "{PYTHON_ENV}/python2/Lib/site-packages", + "{PYTHONPATH}" + ], + "MAYA_DISABLE_CLIC_IPM": "Yes", + "MAYA_DISABLE_CIP": "Yes", + "MAYA_DISABLE_CER": "Yes", + "PYMEL_SKIP_MEL_INIT": "Yes", + "LC_ALL": "C", + "PYPE_LOG_NO_COLORS": "Yes" +} diff --git a/pype/configurations/defaults/environments/mayabatch_2019.json b/pype/configurations/defaults/environments/mayabatch_2019.json new file mode 100644 index 0000000000..aa7360a943 --- /dev/null +++ b/pype/configurations/defaults/environments/mayabatch_2019.json @@ -0,0 +1,11 @@ +{ + "MAYA_VERSION": "2019", + "MAYA_LOCATION": { + "darwin": "/Applications/Autodesk/maya{MAYA_VERSION}/Maya.app/Contents", + "linux": "/usr/autodesk/maya{MAYA_VERSION}", + "windows": "C:/Program Files/Autodesk/Maya{MAYA_VERSION}" + }, + "DYLD_LIBRARY_PATH": { + "darwin": "{MAYA_LOCATION}/MacOS" + } +} diff --git a/pype/configurations/defaults/environments/mtoa_3.1.1.json b/pype/configurations/defaults/environments/mtoa_3.1.1.json new file mode 100644 index 0000000000..f7b9f94d4e --- /dev/null +++ b/pype/configurations/defaults/environments/mtoa_3.1.1.json @@ -0,0 +1,23 @@ +{ + "MTOA": "{PYPE_STUDIO_SOFTWARE}/arnold/mtoa_{MAYA_VERSION}_{MTOA_VERSION}", + "MTOA_VERSION": "3.1.1", + "MAYA_RENDER_DESC_PATH": "{MTOA}", + "MAYA_MODULE_PATH": "{MTOA}", + "ARNOLD_PLUGIN_PATH": "{MTOA}/shaders", + "MTOA_EXTENSIONS_PATH": { + "darwin": "{MTOA}/extensions", + "linux": "{MTOA}/extensions", + "windows": "{MTOA}/extensions" + }, + "MTOA_EXTENSIONS": { + "darwin": "{MTOA}/extensions", + "linux": "{MTOA}/extensions", + "windows": "{MTOA}/extensions" + }, + "DYLD_LIBRARY_PATH": { + "darwin": "{MTOA}/bin" + }, + "PATH": { + "windows": "{PATH};{MTOA}/bin" + } +} diff --git a/pype/configurations/defaults/environments/muster.json b/pype/configurations/defaults/environments/muster.json new file mode 100644 index 0000000000..26f311146a --- /dev/null +++ b/pype/configurations/defaults/environments/muster.json @@ -0,0 +1,3 @@ +{ + "MUSTER_REST_URL": "http://127.0.0.1:9890" +} diff --git a/pype/configurations/defaults/environments/nuke.json b/pype/configurations/defaults/environments/nuke.json new file mode 100644 index 0000000000..50dd31ac91 --- /dev/null +++ b/pype/configurations/defaults/environments/nuke.json @@ -0,0 +1,15 @@ +{ + "NUKE_PATH": [ + "{PYPE_SETUP_PATH}/repos/avalon-core/setup/nuke/nuke_path", + "{PYPE_MODULE_ROOT}/setup/nuke/nuke_path", + "{PYPE_STUDIO_PLUGINS}/nuke" + ], + "PATH": { + "windows": "C:/Program Files (x86)/QuickTime/QTSystem/;{PATH}" + }, + "PYPE_LOG_NO_COLORS": "True", + "PYTHONPATH": { + "windows": "{VIRTUAL_ENV}/Lib/site-packages;{PYTHONPATH}", + "linux": "{VIRTUAL_ENV}/lib/python3.6/site-packages:{PYTHONPATH}" + } +} diff --git a/pype/configurations/defaults/environments/nukestudio.json b/pype/configurations/defaults/environments/nukestudio.json new file mode 100644 index 0000000000..b05e2411f0 --- /dev/null +++ b/pype/configurations/defaults/environments/nukestudio.json @@ -0,0 +1,11 @@ +{ + "HIERO_PLUGIN_PATH": [ + "{PYPE_MODULE_ROOT}/setup/nukestudio/hiero_plugin_path" + ], + "PATH": { + "windows": "C:/Program Files (x86)/QuickTime/QTSystem/;{PATH}" + }, + "WORKFILES_STARTUP": "0", + "TAG_ASSETBUILD_STARTUP": "0", + "PYPE_LOG_NO_COLORS": "True" +} diff --git a/pype/configurations/defaults/environments/nukestudio_10.0.json b/pype/configurations/defaults/environments/nukestudio_10.0.json new file mode 100644 index 0000000000..9bdcef53c9 --- /dev/null +++ b/pype/configurations/defaults/environments/nukestudio_10.0.json @@ -0,0 +1,4 @@ +{ + "PYPE_LOG_NO_COLORS": "Yes", + "QT_PREFERRED_BINDING": "PySide" +} \ No newline at end of file diff --git a/pype/configurations/defaults/environments/nukex.json b/pype/configurations/defaults/environments/nukex.json new file mode 100644 index 0000000000..2b77f44076 --- /dev/null +++ b/pype/configurations/defaults/environments/nukex.json @@ -0,0 +1,10 @@ +{ + "NUKE_PATH": [ + "{PYPE_SETUP_PATH}/repos/avalon-core/setup/nuke/nuke_path", + "{PYPE_MODULE_ROOT}/setup/nuke/nuke_path", + "{PYPE_STUDIO_PLUGINS}/nuke" + ], + "PATH": { + "windows": "C:/Program Files (x86)/QuickTime/QTSystem/;{PATH}" + } +} diff --git a/pype/configurations/defaults/environments/nukex_10.0.json b/pype/configurations/defaults/environments/nukex_10.0.json new file mode 100644 index 0000000000..9bdcef53c9 --- /dev/null +++ b/pype/configurations/defaults/environments/nukex_10.0.json @@ -0,0 +1,4 @@ +{ + "PYPE_LOG_NO_COLORS": "Yes", + "QT_PREFERRED_BINDING": "PySide" +} \ No newline at end of file diff --git a/pype/configurations/defaults/environments/photoshop.json b/pype/configurations/defaults/environments/photoshop.json new file mode 100644 index 0000000000..2208a88665 --- /dev/null +++ b/pype/configurations/defaults/environments/photoshop.json @@ -0,0 +1,4 @@ +{ + "AVALON_PHOTOSHOP_WORKFILES_ON_LAUNCH": "1", + "PYTHONPATH": "{PYTHONPATH}" +} diff --git a/pype/configurations/defaults/environments/premiere.json b/pype/configurations/defaults/environments/premiere.json new file mode 100644 index 0000000000..27dc5c564b --- /dev/null +++ b/pype/configurations/defaults/environments/premiere.json @@ -0,0 +1,11 @@ +{ + "EXTENSIONS_PATH": { + "windows": "{USERPROFILE}/AppData/Roaming/Adobe/CEP/extensions", + "darvin": "{USER}/Library/Application Support/Adobe/CEP/extensions" + }, + "EXTENSIONS_CACHE_PATH": { + "windows": "{USERPROFILE}/AppData/Local/Temp/cep_cache", + "darvin": "{USER}/Library/Application Support/Adobe/CEP/cep_cache" + }, + "installed_zxp": "" +} diff --git a/pype/configurations/defaults/environments/resolve.json b/pype/configurations/defaults/environments/resolve.json new file mode 100644 index 0000000000..1ff197dd5a --- /dev/null +++ b/pype/configurations/defaults/environments/resolve.json @@ -0,0 +1,40 @@ +{ + "RESOLVE_UTILITY_SCRIPTS_SOURCE_DIR": [ + "{STUDIO_SOFT}/davinci_resolve/scripts/python" + ], + "RESOLVE_SCRIPT_API": { + "windows": "{PROGRAMDATA}/Blackmagic Design/DaVinci Resolve/Support/Developer/Scripting", + "darvin": "/Library/Application Support/Blackmagic Design/DaVinci Resolve/Developer/Scripting", + "linux": "/opt/resolve/Developer/Scripting" + }, + "RESOLVE_SCRIPT_LIB": { + "windows": "C:/Program Files/Blackmagic Design/DaVinci Resolve/fusionscript.dll", + "darvin": "/Applications/DaVinci Resolve/DaVinci Resolve.app/Contents/Libraries/Fusion/fusionscript.so", + "linux": "/opt/resolve/libs/Fusion/fusionscript.so" + }, + "RESOLVE_UTILITY_SCRIPTS_DIR": { + "windows": "{PROGRAMDATA}/Blackmagic Design/DaVinci Resolve/Fusion/Scripts/Comp", + "darvin": "/Library/Application Support/Blackmagic Design/DaVinci Resolve/Fusion/Scripts/Comp", + "linux": "/opt/resolve/Fusion/Scripts/Comp" + }, + "PYTHON36_RESOLVE": { + "windows": "{LOCALAPPDATA}/Programs/Python/Python36", + "darvin": "~/Library/Python/3.6/bin", + "linux": "/opt/Python/3.6/bin" + }, + "PYTHONPATH": [ + "{PYTHON36_RESOLVE}/Lib/site-packages", + "{VIRTUAL_ENV}/Lib/site-packages", + "{PYTHONPATH}", + "{RESOLVE_SCRIPT_API}/Modules", + "{PYTHONPATH}" + ], + "PATH": [ + "{PYTHON36_RESOLVE}", + "{PYTHON36_RESOLVE}/Scripts", + "{PATH}" + ], + "PRE_PYTHON_SCRIPT": "{PYPE_MODULE_ROOT}/pype/resolve/preload_console.py", + "PYPE_LOG_NO_COLORS": "True", + "RESOLVE_DEV": "True" +} diff --git a/pype/configurations/defaults/environments/storyboardpro.json b/pype/configurations/defaults/environments/storyboardpro.json new file mode 100644 index 0000000000..581ad4db45 --- /dev/null +++ b/pype/configurations/defaults/environments/storyboardpro.json @@ -0,0 +1,4 @@ +{ + "AVALON_TOONBOOM_WORKFILES_ON_LAUNCH": "1", + "PYBLISH_LITE_ALWAYS_EXEC": "1" +} diff --git a/pype/configurations/defaults/environments/unreal_4.24.json b/pype/configurations/defaults/environments/unreal_4.24.json new file mode 100644 index 0000000000..8feeb0230f --- /dev/null +++ b/pype/configurations/defaults/environments/unreal_4.24.json @@ -0,0 +1,5 @@ +{ + "AVALON_UNREAL_PLUGIN": "{PYPE_SETUP_PATH}/repos/avalon-unreal-integration", + "PYPE_LOG_NO_COLORS": "True", + "QT_PREFERRED_BINDING": "PySide" +} diff --git a/pype/configurations/defaults/environments/vray_4300.json b/pype/configurations/defaults/environments/vray_4300.json new file mode 100644 index 0000000000..3212188441 --- /dev/null +++ b/pype/configurations/defaults/environments/vray_4300.json @@ -0,0 +1,15 @@ +{ + "VRAY_VERSION": "43001", + "VRAY_ROOT": "C:/vray/vray_{VRAY_VERSION}", + "MAYA_RENDER_DESC_PATH": "{VRAY_ROOT}/maya_root/bin/rendererDesc", + "VRAY_FOR_MAYA2019_MAIN": "{VRAY_ROOT}/maya_vray", + "VRAY_FOR_MAYA2019_PLUGINS": "{VRAY_ROOT}/maya_vray/vrayplugins", + "VRAY_PLUGINS": "{VRAY_ROOT}/maya_vray/vrayplugins", + "VRAY_OSL_PATH_MAYA2019": "{VRAY_ROOT}/vray/opensl", + "PATH": "{VRAY_ROOT}/maya_root/bin;{PATH}", + "MAYA_PLUG_IN_PATH": "{VRAY_ROOT}/maya_vray/plug-ins", + "MAYA_SCRIPT_PATH": "{VRAY_ROOT}/maya_vray/scripts", + "PYTHONPATH": "{VRAY_ROOT}/maya_vray/scripts;{PYTHONPATH}", + "XBMLANGPATH": "{VRAY_ROOT}/maya_vray/icons;{XBMLANGPATH}", + "VRAY_AUTH_CLIENT_FILE_PATH": "{VRAY_ROOT}" +} diff --git a/pype/configurations/defaults/launchers/blender_2.80.toml b/pype/configurations/defaults/launchers/blender_2.80.toml new file mode 100644 index 0000000000..5fea78b7b0 --- /dev/null +++ b/pype/configurations/defaults/launchers/blender_2.80.toml @@ -0,0 +1,7 @@ +application_dir = "blender" +executable = "blender_2.80" +schema = "avalon-core:application-1.0" +label = "Blender 2.80" +ftrack_label = "Blender" +icon ="blender" +ftrack_icon = '{}/app_icons/blender.png' diff --git a/pype/configurations/defaults/launchers/blender_2.81.toml b/pype/configurations/defaults/launchers/blender_2.81.toml new file mode 100644 index 0000000000..4f85ee5558 --- /dev/null +++ b/pype/configurations/defaults/launchers/blender_2.81.toml @@ -0,0 +1,7 @@ +application_dir = "blender" +executable = "blender_2.81" +schema = "avalon-core:application-1.0" +label = "Blender 2.81" +ftrack_label = "Blender" +icon ="blender" +ftrack_icon = '{}/app_icons/blender.png' diff --git a/pype/configurations/defaults/launchers/blender_2.82.toml b/pype/configurations/defaults/launchers/blender_2.82.toml new file mode 100644 index 0000000000..840001452e --- /dev/null +++ b/pype/configurations/defaults/launchers/blender_2.82.toml @@ -0,0 +1,7 @@ +application_dir = "blender" +executable = "blender_2.82" +schema = "avalon-core:application-1.0" +label = "Blender 2.82" +ftrack_label = "Blender" +icon ="blender" +ftrack_icon = '{}/app_icons/blender.png' diff --git a/pype/configurations/defaults/launchers/blender_2.83.toml b/pype/configurations/defaults/launchers/blender_2.83.toml new file mode 100644 index 0000000000..7fc8bf87b9 --- /dev/null +++ b/pype/configurations/defaults/launchers/blender_2.83.toml @@ -0,0 +1,7 @@ +application_dir = "blender" +executable = "blender_2.83" +schema = "avalon-core:application-1.0" +label = "Blender 2.83" +ftrack_label = "Blender" +icon ="blender" +ftrack_icon = '{}/app_icons/blender.png' diff --git a/pype/configurations/defaults/launchers/celaction_local.toml b/pype/configurations/defaults/launchers/celaction_local.toml new file mode 100644 index 0000000000..aef3548e08 --- /dev/null +++ b/pype/configurations/defaults/launchers/celaction_local.toml @@ -0,0 +1,8 @@ +executable = "celaction_local" +schema = "avalon-core:application-1.0" +application_dir = "celaction" +label = "CelAction2D" +ftrack_label = "CelAction2D" +icon ="celaction_local" +launch_hook = "pype/hooks/celaction/prelaunch.py/CelactionPrelaunchHook" +ftrack_icon = '{}/app_icons/celaction_local.png' diff --git a/pype/configurations/defaults/launchers/celaction_publish.toml b/pype/configurations/defaults/launchers/celaction_publish.toml new file mode 100644 index 0000000000..86f4ae39e7 --- /dev/null +++ b/pype/configurations/defaults/launchers/celaction_publish.toml @@ -0,0 +1,7 @@ +schema = "avalon-core:application-1.0" +application_dir = "shell" +executable = "celaction_publish" +label = "Shell" + +[environment] +CREATE_NEW_CONSOLE = "Yes" diff --git a/pype/configurations/defaults/launchers/darwin/blender_2.82 b/pype/configurations/defaults/launchers/darwin/blender_2.82 new file mode 100644 index 0000000000..8254411ea2 --- /dev/null +++ b/pype/configurations/defaults/launchers/darwin/blender_2.82 @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +open -a blender $@ diff --git a/pype/configurations/defaults/launchers/darwin/harmony_17 b/pype/configurations/defaults/launchers/darwin/harmony_17 new file mode 100644 index 0000000000..b7eba2c2d0 --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/darwin/harmony_17_launch b/pype/configurations/defaults/launchers/darwin/harmony_17_launch new file mode 100644 index 0000000000..5dcf5db57e --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/darwin/python3 b/pype/configurations/defaults/launchers/darwin/python3 new file mode 100644 index 0000000000..c2b82c7638 --- /dev/null +++ b/pype/configurations/defaults/launchers/darwin/python3 @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +open /usr/bin/python3 --args $@ diff --git a/pype/configurations/defaults/launchers/harmony_17.toml b/pype/configurations/defaults/launchers/harmony_17.toml new file mode 100644 index 0000000000..dbb76444a7 --- /dev/null +++ b/pype/configurations/defaults/launchers/harmony_17.toml @@ -0,0 +1,8 @@ +application_dir = "harmony" +label = "Harmony 17" +ftrack_label = "Harmony" +schema = "avalon-core:application-1.0" +executable = "harmony_17" +description = "" +icon ="harmony_icon" +ftrack_icon = '{}/app_icons/harmony.png' diff --git a/pype/configurations/defaults/launchers/houdini_16.toml b/pype/configurations/defaults/launchers/houdini_16.toml new file mode 100644 index 0000000000..e29fa74cad --- /dev/null +++ b/pype/configurations/defaults/launchers/houdini_16.toml @@ -0,0 +1,7 @@ +executable = "houdini_16" +schema = "avalon-core:application-1.0" +application_dir = "houdini" +label = "Houdini 16" +ftrack_label = "Houdini" +icon = "houdini_icon" +ftrack_icon = '{}/app_icons/houdini.png' diff --git a/pype/configurations/defaults/launchers/houdini_17.toml b/pype/configurations/defaults/launchers/houdini_17.toml new file mode 100644 index 0000000000..5d01364330 --- /dev/null +++ b/pype/configurations/defaults/launchers/houdini_17.toml @@ -0,0 +1,7 @@ +executable = "houdini_17" +schema = "avalon-core:application-1.0" +application_dir = "houdini" +label = "Houdini 17.0" +ftrack_label = "Houdini" +icon = "houdini_icon" +ftrack_icon = '{}/app_icons/houdini.png' diff --git a/pype/configurations/defaults/launchers/houdini_18.toml b/pype/configurations/defaults/launchers/houdini_18.toml new file mode 100644 index 0000000000..93b9a3334d --- /dev/null +++ b/pype/configurations/defaults/launchers/houdini_18.toml @@ -0,0 +1,7 @@ +executable = "houdini_18" +schema = "avalon-core:application-1.0" +application_dir = "houdini" +label = "Houdini 18" +ftrack_label = "Houdini" +icon = "houdini_icon" +ftrack_icon = '{}/app_icons/houdini.png' diff --git a/pype/configurations/defaults/launchers/linux/maya2016 b/pype/configurations/defaults/launchers/linux/maya2016 new file mode 100644 index 0000000000..98424304b1 --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/linux/maya2017 b/pype/configurations/defaults/launchers/linux/maya2017 new file mode 100644 index 0000000000..7a2662a55e --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/linux/maya2018 b/pype/configurations/defaults/launchers/linux/maya2018 new file mode 100644 index 0000000000..db832b3fe7 --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/linux/maya2019 b/pype/configurations/defaults/launchers/linux/maya2019 new file mode 100644 index 0000000000..8398734ab9 --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/linux/maya2020 b/pype/configurations/defaults/launchers/linux/maya2020 new file mode 100644 index 0000000000..18a1edd598 --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/linux/nuke11.3 b/pype/configurations/defaults/launchers/linux/nuke11.3 new file mode 100644 index 0000000000..b1c9a90d74 --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/linux/nuke12.0 b/pype/configurations/defaults/launchers/linux/nuke12.0 new file mode 100644 index 0000000000..99ea1a6b0c --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/linux/nukestudio11.3 b/pype/configurations/defaults/launchers/linux/nukestudio11.3 new file mode 100644 index 0000000000..750d54a7d5 --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/linux/nukestudio12.0 b/pype/configurations/defaults/launchers/linux/nukestudio12.0 new file mode 100644 index 0000000000..ba5cf654a8 --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/linux/nukex11.3 b/pype/configurations/defaults/launchers/linux/nukex11.3 new file mode 100644 index 0000000000..d913e4b961 --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/linux/nukex12.0 b/pype/configurations/defaults/launchers/linux/nukex12.0 new file mode 100644 index 0000000000..da2721c48b --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/maya_2016.toml b/pype/configurations/defaults/launchers/maya_2016.toml new file mode 100644 index 0000000000..d69c4effaf --- /dev/null +++ b/pype/configurations/defaults/launchers/maya_2016.toml @@ -0,0 +1,26 @@ +application_dir = "maya" +default_dirs = [ + "scenes", + "data", + "renderData/shaders", + "images" +] +label = "Autodesk Maya 2016x64" +ftrack_label = "Maya" +schema = "avalon-core:application-1.0" +executable = "maya2016" +description = "" +icon ="maya_icon" +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/configurations/defaults/launchers/maya_2017.toml b/pype/configurations/defaults/launchers/maya_2017.toml new file mode 100644 index 0000000000..2d1c35b530 --- /dev/null +++ b/pype/configurations/defaults/launchers/maya_2017.toml @@ -0,0 +1,28 @@ +application_dir = "maya" +default_dirs = [ + "scenes", + "data", + "renderData/shaders", + "images" +] +label = "Autodesk Maya 2017" +ftrack_label = "Maya" +schema = "avalon-core:application-1.0" +executable = "maya2017" +description = "" +icon ="maya_icon" +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/configurations/defaults/launchers/maya_2018.toml b/pype/configurations/defaults/launchers/maya_2018.toml new file mode 100644 index 0000000000..f180263fa2 --- /dev/null +++ b/pype/configurations/defaults/launchers/maya_2018.toml @@ -0,0 +1,14 @@ +application_dir = "maya" +default_dirs = [ + "renders" +] +label = "Autodesk Maya 2018" +ftrack_label = "Maya" +schema = "avalon-core:application-1.0" +executable = "maya2018" +description = "" +icon ="maya_icon" +ftrack_icon = '{}/app_icons/maya.png' + +[copy] +"{PYPE_MODULE_ROOT}/pype/resources/maya/workspace.mel" = "workspace.mel" diff --git a/pype/configurations/defaults/launchers/maya_2019.toml b/pype/configurations/defaults/launchers/maya_2019.toml new file mode 100644 index 0000000000..7ec2cbcedd --- /dev/null +++ b/pype/configurations/defaults/launchers/maya_2019.toml @@ -0,0 +1,14 @@ +application_dir = "maya" +default_dirs = [ + "renders" +] +label = "Autodesk Maya 2019" +ftrack_label = "Maya" +schema = "avalon-core:application-1.0" +executable = "maya2019" +description = "" +icon ="maya_icon" +ftrack_icon = '{}/app_icons/maya.png' + +[copy] +"{PYPE_MODULE_ROOT}/pype/resources/maya/workspace.mel" = "workspace.mel" diff --git a/pype/configurations/defaults/launchers/maya_2020.toml b/pype/configurations/defaults/launchers/maya_2020.toml new file mode 100644 index 0000000000..49d84ef9a0 --- /dev/null +++ b/pype/configurations/defaults/launchers/maya_2020.toml @@ -0,0 +1,14 @@ +application_dir = "maya" +default_dirs = [ + "renders" +] +label = "Autodesk Maya 2020" +ftrack_label = "Maya" +schema = "avalon-core:application-1.0" +executable = "maya2020" +description = "" +icon ="maya_icon" +ftrack_icon = '{}/app_icons/maya.png' + +[copy] +"{PYPE_MODULE_ROOT}/pype/resources/maya/workspace.mel" = "workspace.mel" diff --git a/pype/configurations/defaults/launchers/mayabatch_2019.toml b/pype/configurations/defaults/launchers/mayabatch_2019.toml new file mode 100644 index 0000000000..a928618d2b --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/mayabatch_2020.toml b/pype/configurations/defaults/launchers/mayabatch_2020.toml new file mode 100644 index 0000000000..cd1e1e4474 --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/mayapy2016.toml b/pype/configurations/defaults/launchers/mayapy2016.toml new file mode 100644 index 0000000000..ad1e3dee86 --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/mayapy2017.toml b/pype/configurations/defaults/launchers/mayapy2017.toml new file mode 100644 index 0000000000..8d2095ff47 --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/mayapy2018.toml b/pype/configurations/defaults/launchers/mayapy2018.toml new file mode 100644 index 0000000000..597744fd85 --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/mayapy2019.toml b/pype/configurations/defaults/launchers/mayapy2019.toml new file mode 100644 index 0000000000..3c8a9860f9 --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/mayapy2020.toml b/pype/configurations/defaults/launchers/mayapy2020.toml new file mode 100644 index 0000000000..8f2d2e4a67 --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/myapp.toml b/pype/configurations/defaults/launchers/myapp.toml new file mode 100644 index 0000000000..21da0d52b2 --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/nuke_10.0.toml b/pype/configurations/defaults/launchers/nuke_10.0.toml new file mode 100644 index 0000000000..2195fd3e82 --- /dev/null +++ b/pype/configurations/defaults/launchers/nuke_10.0.toml @@ -0,0 +1,7 @@ +executable = "nuke10.0" +schema = "avalon-core:application-1.0" +application_dir = "nuke" +label = "Nuke 10.0v4" +ftrack_label = "Nuke" +icon ="nuke_icon" +ftrack_icon = '{}/app_icons/nuke.png' diff --git a/pype/configurations/defaults/launchers/nuke_11.0.toml b/pype/configurations/defaults/launchers/nuke_11.0.toml new file mode 100644 index 0000000000..0c981b479a --- /dev/null +++ b/pype/configurations/defaults/launchers/nuke_11.0.toml @@ -0,0 +1,7 @@ +executable = "nuke11.0" +schema = "avalon-core:application-1.0" +application_dir = "nuke" +label = "Nuke 11.0" +ftrack_label = "Nuke" +icon ="nuke_icon" +ftrack_icon = '{}/app_icons/nuke.png' diff --git a/pype/configurations/defaults/launchers/nuke_11.2.toml b/pype/configurations/defaults/launchers/nuke_11.2.toml new file mode 100644 index 0000000000..57c962d126 --- /dev/null +++ b/pype/configurations/defaults/launchers/nuke_11.2.toml @@ -0,0 +1,7 @@ +executable = "nuke11.2" +schema = "avalon-core:application-1.0" +application_dir = "nuke" +label = "Nuke 11.2" +ftrack_label = "Nuke" +icon ="nuke_icon" +ftrack_icon = '{}/app_icons/nuke.png' diff --git a/pype/configurations/defaults/launchers/nuke_11.3.toml b/pype/configurations/defaults/launchers/nuke_11.3.toml new file mode 100644 index 0000000000..87f769c23b --- /dev/null +++ b/pype/configurations/defaults/launchers/nuke_11.3.toml @@ -0,0 +1,7 @@ +executable = "nuke11.3" +schema = "avalon-core:application-1.0" +application_dir = "nuke" +label = "Nuke 11.3" +ftrack_label = "Nuke" +icon ="nuke_icon" +ftrack_icon = '{}/app_icons/nuke.png' diff --git a/pype/configurations/defaults/launchers/nuke_12.0.toml b/pype/configurations/defaults/launchers/nuke_12.0.toml new file mode 100644 index 0000000000..62936b4cdb --- /dev/null +++ b/pype/configurations/defaults/launchers/nuke_12.0.toml @@ -0,0 +1,7 @@ +executable = "nuke12.0" +schema = "avalon-core:application-1.0" +application_dir = "nuke" +label = "Nuke 12.0" +ftrack_label = "Nuke" +icon ="nuke_icon" +ftrack_icon = '{}/app_icons/nuke.png' diff --git a/pype/configurations/defaults/launchers/nukestudio_10.0.toml b/pype/configurations/defaults/launchers/nukestudio_10.0.toml new file mode 100644 index 0000000000..41601e4d40 --- /dev/null +++ b/pype/configurations/defaults/launchers/nukestudio_10.0.toml @@ -0,0 +1,7 @@ +executable = "nukestudio10.0" +schema = "avalon-core:application-1.0" +application_dir = "nukestudio" +label = "NukeStudio 10.0" +ftrack_label = "NukeStudio" +icon ="nuke_icon" +ftrack_icon = '{}/app_icons/nuke.png' diff --git a/pype/configurations/defaults/launchers/nukestudio_11.0.toml b/pype/configurations/defaults/launchers/nukestudio_11.0.toml new file mode 100644 index 0000000000..7a9d84707a --- /dev/null +++ b/pype/configurations/defaults/launchers/nukestudio_11.0.toml @@ -0,0 +1,7 @@ +executable = "nukestudio11.0" +schema = "avalon-core:application-1.0" +application_dir = "nukestudio" +label = "NukeStudio 11.0" +ftrack_label = "NukeStudio" +icon ="nuke_icon" +ftrack_icon = '{}/app_icons/nuke.png' diff --git a/pype/configurations/defaults/launchers/nukestudio_11.2.toml b/pype/configurations/defaults/launchers/nukestudio_11.2.toml new file mode 100644 index 0000000000..21557033ca --- /dev/null +++ b/pype/configurations/defaults/launchers/nukestudio_11.2.toml @@ -0,0 +1,7 @@ +executable = "nukestudio11.2" +schema = "avalon-core:application-1.0" +application_dir = "nukestudio" +label = "NukeStudio 11.2" +ftrack_label = "NukeStudio" +icon ="nuke_icon" +ftrack_icon = '{}/app_icons/nuke.png' diff --git a/pype/configurations/defaults/launchers/nukestudio_11.3.toml b/pype/configurations/defaults/launchers/nukestudio_11.3.toml new file mode 100644 index 0000000000..1946ad6c3b --- /dev/null +++ b/pype/configurations/defaults/launchers/nukestudio_11.3.toml @@ -0,0 +1,7 @@ +executable = "nukestudio11.3" +schema = "avalon-core:application-1.0" +application_dir = "nukestudio" +label = "NukeStudio 11.3" +ftrack_label = "NukeStudio" +icon ="nuke_icon" +ftrack_icon = '{}/app_icons/nuke.png' diff --git a/pype/configurations/defaults/launchers/nukestudio_12.0.toml b/pype/configurations/defaults/launchers/nukestudio_12.0.toml new file mode 100644 index 0000000000..4ce7f9b538 --- /dev/null +++ b/pype/configurations/defaults/launchers/nukestudio_12.0.toml @@ -0,0 +1,7 @@ +executable = "nukestudio12.0" +schema = "avalon-core:application-1.0" +application_dir = "nukestudio" +label = "NukeStudio 12.0" +ftrack_label = "NukeStudio" +icon ="nuke_icon" +ftrack_icon = '{}/app_icons/nuke.png' diff --git a/pype/configurations/defaults/launchers/nukex_10.0.toml b/pype/configurations/defaults/launchers/nukex_10.0.toml new file mode 100644 index 0000000000..7dee22996d --- /dev/null +++ b/pype/configurations/defaults/launchers/nukex_10.0.toml @@ -0,0 +1,7 @@ +executable = "nukex10.0" +schema = "avalon-core:application-1.0" +application_dir = "nuke" +label = "NukeX 10.0" +ftrack_label = "NukeX" +icon ="nuke_icon" +ftrack_icon = '{}/app_icons/nukex.png' diff --git a/pype/configurations/defaults/launchers/nukex_11.0.toml b/pype/configurations/defaults/launchers/nukex_11.0.toml new file mode 100644 index 0000000000..c2b4970a26 --- /dev/null +++ b/pype/configurations/defaults/launchers/nukex_11.0.toml @@ -0,0 +1,7 @@ +executable = "nukex11.0" +schema = "avalon-core:application-1.0" +application_dir = "nuke" +label = "NukeX 11.2" +ftrack_label = "NukeX" +icon ="nuke_icon" +ftrack_icon = '{}/app_icons/nukex.png' diff --git a/pype/configurations/defaults/launchers/nukex_11.2.toml b/pype/configurations/defaults/launchers/nukex_11.2.toml new file mode 100644 index 0000000000..3857b9995c --- /dev/null +++ b/pype/configurations/defaults/launchers/nukex_11.2.toml @@ -0,0 +1,7 @@ +executable = "nukex11.2" +schema = "avalon-core:application-1.0" +application_dir = "nuke" +label = "NukeX 11.2" +ftrack_label = "NukeX" +icon ="nuke_icon" +ftrack_icon = '{}/app_icons/nukex.png' diff --git a/pype/configurations/defaults/launchers/nukex_11.3.toml b/pype/configurations/defaults/launchers/nukex_11.3.toml new file mode 100644 index 0000000000..56428470eb --- /dev/null +++ b/pype/configurations/defaults/launchers/nukex_11.3.toml @@ -0,0 +1,7 @@ +executable = "nukex11.3" +schema = "avalon-core:application-1.0" +application_dir = "nuke" +label = "NukeX 11.3" +ftrack_label = "NukeX" +icon ="nuke_icon" +ftrack_icon = '{}/app_icons/nukex.png' diff --git a/pype/configurations/defaults/launchers/nukex_12.0.toml b/pype/configurations/defaults/launchers/nukex_12.0.toml new file mode 100644 index 0000000000..33d7fddb88 --- /dev/null +++ b/pype/configurations/defaults/launchers/nukex_12.0.toml @@ -0,0 +1,7 @@ +executable = "nukex12.0" +schema = "avalon-core:application-1.0" +application_dir = "nuke" +label = "NukeX 12.0" +ftrack_label = "NukeX" +icon ="nuke_icon" +ftrack_icon = '{}/app_icons/nukex.png' diff --git a/pype/configurations/defaults/launchers/photoshop_2020.toml b/pype/configurations/defaults/launchers/photoshop_2020.toml new file mode 100644 index 0000000000..117b668232 --- /dev/null +++ b/pype/configurations/defaults/launchers/photoshop_2020.toml @@ -0,0 +1,8 @@ +executable = "photoshop_2020" +schema = "avalon-core:application-1.0" +application_dir = "photoshop" +label = "Adobe Photoshop 2020" +icon ="photoshop_icon" +ftrack_label = "Photoshop" +ftrack_icon = '{}/app_icons/photoshop.png' +launch_hook = "pype/hooks/photoshop/prelaunch.py/PhotoshopPrelaunch" diff --git a/pype/configurations/defaults/launchers/premiere_2019.toml b/pype/configurations/defaults/launchers/premiere_2019.toml new file mode 100644 index 0000000000..f4c19c62cb --- /dev/null +++ b/pype/configurations/defaults/launchers/premiere_2019.toml @@ -0,0 +1,8 @@ +executable = "premiere_pro_2019" +schema = "avalon-core:application-1.0" +application_dir = "premiere" +label = "Adobe Premiere Pro CC 2019" +icon ="premiere_icon" + +ftrack_label = "Premiere" +ftrack_icon = '{}/app_icons/premiere.png' diff --git a/pype/configurations/defaults/launchers/premiere_2020.toml b/pype/configurations/defaults/launchers/premiere_2020.toml new file mode 100644 index 0000000000..4d721c898f --- /dev/null +++ b/pype/configurations/defaults/launchers/premiere_2020.toml @@ -0,0 +1,9 @@ +executable = "premiere_pro_2020" +schema = "avalon-core:application-1.0" +application_dir = "premiere" +label = "Adobe Premiere Pro CC 2020" +launch_hook = "pype/hooks/premiere/prelaunch.py/PremierePrelaunch" +icon ="premiere_icon" + +ftrack_label = "Premiere" +ftrack_icon = '{}/app_icons/premiere.png' diff --git a/pype/configurations/defaults/launchers/python_2.toml b/pype/configurations/defaults/launchers/python_2.toml new file mode 100644 index 0000000000..e9e8dd7899 --- /dev/null +++ b/pype/configurations/defaults/launchers/python_2.toml @@ -0,0 +1,10 @@ +schema = "avalon-core:application-1.0" +application_dir = "python" +executable = "python" +label = "Python 2" +ftrack_label = "Python" +icon ="python_icon" +ftrack_icon = '{}/app_icons/python.png' + +[environment] +CREATE_NEW_CONSOLE = "Yes" diff --git a/pype/configurations/defaults/launchers/python_3.toml b/pype/configurations/defaults/launchers/python_3.toml new file mode 100644 index 0000000000..5cbd8b2943 --- /dev/null +++ b/pype/configurations/defaults/launchers/python_3.toml @@ -0,0 +1,10 @@ +schema = "avalon-core:application-1.0" +application_dir = "python" +executable = "python3" +label = "Python 3" +ftrack_label = "Python" +icon ="python_icon" +ftrack_icon = '{}/app_icons/python.png' + +[environment] +CREATE_NEW_CONSOLE = "Yes" diff --git a/pype/configurations/defaults/launchers/resolve_16.toml b/pype/configurations/defaults/launchers/resolve_16.toml new file mode 100644 index 0000000000..430fd1a638 --- /dev/null +++ b/pype/configurations/defaults/launchers/resolve_16.toml @@ -0,0 +1,9 @@ +executable = "resolve_16" +schema = "avalon-core:application-1.0" +application_dir = "resolve" +label = "BM DaVinci Resolve 16" +launch_hook = "pype/hooks/resolve/prelaunch.py/ResolvePrelaunch" +icon ="resolve" + +ftrack_label = "BM DaVinci Resolve" +ftrack_icon = '{}/app_icons/resolve.png' diff --git a/pype/configurations/defaults/launchers/shell.toml b/pype/configurations/defaults/launchers/shell.toml new file mode 100644 index 0000000000..959ad392ea --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/storyboardpro_7.toml b/pype/configurations/defaults/launchers/storyboardpro_7.toml new file mode 100644 index 0000000000..ce8e96a49d --- /dev/null +++ b/pype/configurations/defaults/launchers/storyboardpro_7.toml @@ -0,0 +1,8 @@ +application_dir = "storyboardpro" +label = "Storyboard Pro 7" +ftrack_label = "Storyboard Pro" +schema = "avalon-core:application-1.0" +executable = "storyboardpro_7" +description = "" +icon ="storyboardpro_icon" +ftrack_icon = '{}/app_icons/storyboardpro.png' diff --git a/pype/configurations/defaults/launchers/unreal_4.24.toml b/pype/configurations/defaults/launchers/unreal_4.24.toml new file mode 100644 index 0000000000..0a799e5dcb --- /dev/null +++ b/pype/configurations/defaults/launchers/unreal_4.24.toml @@ -0,0 +1,8 @@ +executable = "unreal" +schema = "avalon-core:application-1.0" +application_dir = "unreal" +label = "Unreal Editor 4.24" +ftrack_label = "UnrealEditor" +icon ="ue4_icon" +launch_hook = "pype/hooks/unreal/unreal_prelaunch.py/UnrealPrelaunch" +ftrack_icon = '{}/app_icons/ue4.png' diff --git a/pype/configurations/defaults/launchers/windows/blender_2.80.bat b/pype/configurations/defaults/launchers/windows/blender_2.80.bat new file mode 100644 index 0000000000..5b8a37356b --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/windows/blender_2.81.bat b/pype/configurations/defaults/launchers/windows/blender_2.81.bat new file mode 100644 index 0000000000..a900b18eda --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/windows/blender_2.82.bat b/pype/configurations/defaults/launchers/windows/blender_2.82.bat new file mode 100644 index 0000000000..7105c1efe1 --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/windows/blender_2.83.bat b/pype/configurations/defaults/launchers/windows/blender_2.83.bat new file mode 100644 index 0000000000..671952f0d7 --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/windows/celaction_local.bat b/pype/configurations/defaults/launchers/windows/celaction_local.bat new file mode 100644 index 0000000000..8f2171617e --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/windows/celaction_publish.bat b/pype/configurations/defaults/launchers/windows/celaction_publish.bat new file mode 100644 index 0000000000..77ec2ac24e --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/windows/harmony_17.bat b/pype/configurations/defaults/launchers/windows/harmony_17.bat new file mode 100644 index 0000000000..0822650875 --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/windows/houdini_16.bat b/pype/configurations/defaults/launchers/windows/houdini_16.bat new file mode 100644 index 0000000000..018ba08b4c --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/windows/houdini_17.bat b/pype/configurations/defaults/launchers/windows/houdini_17.bat new file mode 100644 index 0000000000..950a599623 --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/windows/houdini_18.bat b/pype/configurations/defaults/launchers/windows/houdini_18.bat new file mode 100644 index 0000000000..3d6b1ae258 --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/windows/maya2016.bat b/pype/configurations/defaults/launchers/windows/maya2016.bat new file mode 100644 index 0000000000..54f15cf269 --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/windows/maya2017.bat b/pype/configurations/defaults/launchers/windows/maya2017.bat new file mode 100644 index 0000000000..5c2aeb495c --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/windows/maya2018.bat b/pype/configurations/defaults/launchers/windows/maya2018.bat new file mode 100644 index 0000000000..28cf776c77 --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/windows/maya2019.bat b/pype/configurations/defaults/launchers/windows/maya2019.bat new file mode 100644 index 0000000000..7e80dd2557 --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/windows/maya2020.bat b/pype/configurations/defaults/launchers/windows/maya2020.bat new file mode 100644 index 0000000000..b2acb5df5a --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/windows/mayabatch2019.bat b/pype/configurations/defaults/launchers/windows/mayabatch2019.bat new file mode 100644 index 0000000000..ddd9b9b956 --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/windows/mayabatch2020.bat b/pype/configurations/defaults/launchers/windows/mayabatch2020.bat new file mode 100644 index 0000000000..b1cbc6dbb6 --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/windows/mayapy2016.bat b/pype/configurations/defaults/launchers/windows/mayapy2016.bat new file mode 100644 index 0000000000..205991fd3d --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/windows/mayapy2017.bat b/pype/configurations/defaults/launchers/windows/mayapy2017.bat new file mode 100644 index 0000000000..14aacc5a7f --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/windows/mayapy2018.bat b/pype/configurations/defaults/launchers/windows/mayapy2018.bat new file mode 100644 index 0000000000..c47c472f46 --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/windows/mayapy2019.bat b/pype/configurations/defaults/launchers/windows/mayapy2019.bat new file mode 100644 index 0000000000..73ca5b2d40 --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/windows/mayapy2020.bat b/pype/configurations/defaults/launchers/windows/mayapy2020.bat new file mode 100644 index 0000000000..770a03dcf5 --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/windows/nuke10.0.bat b/pype/configurations/defaults/launchers/windows/nuke10.0.bat new file mode 100644 index 0000000000..a47cbdfb20 --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/windows/nuke11.0.bat b/pype/configurations/defaults/launchers/windows/nuke11.0.bat new file mode 100644 index 0000000000..a374c5cf5b --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/windows/nuke11.2.bat b/pype/configurations/defaults/launchers/windows/nuke11.2.bat new file mode 100644 index 0000000000..4c777ac28c --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/windows/nuke11.3.bat b/pype/configurations/defaults/launchers/windows/nuke11.3.bat new file mode 100644 index 0000000000..a023f5f46f --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/windows/nuke12.0.bat b/pype/configurations/defaults/launchers/windows/nuke12.0.bat new file mode 100644 index 0000000000..d8fb5772bb --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/windows/nukestudio10.0.bat b/pype/configurations/defaults/launchers/windows/nukestudio10.0.bat new file mode 100644 index 0000000000..82f833667c --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/windows/nukestudio11.0.bat b/pype/configurations/defaults/launchers/windows/nukestudio11.0.bat new file mode 100644 index 0000000000..b66797727e --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/windows/nukestudio11.2.bat b/pype/configurations/defaults/launchers/windows/nukestudio11.2.bat new file mode 100644 index 0000000000..a653d816b4 --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/windows/nukestudio11.3.bat b/pype/configurations/defaults/launchers/windows/nukestudio11.3.bat new file mode 100644 index 0000000000..62c8718873 --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/windows/nukestudio12.0.bat b/pype/configurations/defaults/launchers/windows/nukestudio12.0.bat new file mode 100644 index 0000000000..488232bcbf --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/windows/nukex10.0.bat b/pype/configurations/defaults/launchers/windows/nukex10.0.bat new file mode 100644 index 0000000000..1759706a7b --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/windows/nukex11.0.bat b/pype/configurations/defaults/launchers/windows/nukex11.0.bat new file mode 100644 index 0000000000..b554a7b6fa --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/windows/nukex11.2.bat b/pype/configurations/defaults/launchers/windows/nukex11.2.bat new file mode 100644 index 0000000000..a4cb5dec5c --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/windows/nukex11.3.bat b/pype/configurations/defaults/launchers/windows/nukex11.3.bat new file mode 100644 index 0000000000..490b55cf4c --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/windows/nukex12.0.bat b/pype/configurations/defaults/launchers/windows/nukex12.0.bat new file mode 100644 index 0000000000..26adf0d3f1 --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/windows/photoshop_2020.bat b/pype/configurations/defaults/launchers/windows/photoshop_2020.bat new file mode 100644 index 0000000000..6b90922ef6 --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/windows/premiere_pro_2019.bat b/pype/configurations/defaults/launchers/windows/premiere_pro_2019.bat new file mode 100644 index 0000000000..4886737d2f --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/windows/premiere_pro_2020.bat b/pype/configurations/defaults/launchers/windows/premiere_pro_2020.bat new file mode 100644 index 0000000000..14662d3be3 --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/windows/python3.bat b/pype/configurations/defaults/launchers/windows/python3.bat new file mode 100644 index 0000000000..c7c116fe72 --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/windows/resolve_16.bat b/pype/configurations/defaults/launchers/windows/resolve_16.bat new file mode 100644 index 0000000000..1a5d964e6b --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/windows/shell.bat b/pype/configurations/defaults/launchers/windows/shell.bat new file mode 100644 index 0000000000..eb0895364f --- /dev/null +++ b/pype/configurations/defaults/launchers/windows/shell.bat @@ -0,0 +1,2 @@ +@echo off +start cmd diff --git a/pype/configurations/defaults/launchers/windows/storyboardpro_7.bat b/pype/configurations/defaults/launchers/windows/storyboardpro_7.bat new file mode 100644 index 0000000000..122edac572 --- /dev/null +++ b/pype/configurations/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/configurations/defaults/launchers/windows/unreal.bat b/pype/configurations/defaults/launchers/windows/unreal.bat new file mode 100644 index 0000000000..7771aaa5a5 --- /dev/null +++ b/pype/configurations/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 diff --git a/pype/configurations/defaults/presets/colorspace/aces103-cg.json b/pype/configurations/defaults/presets/colorspace/aces103-cg.json new file mode 100644 index 0000000000..dd3fca4c2d --- /dev/null +++ b/pype/configurations/defaults/presets/colorspace/aces103-cg.json @@ -0,0 +1,46 @@ +{ + "resolve": { + + }, + "nukestudio": { + + }, + "nuke": { + "root": { + "colorManagement": "OCIO", + "OCIO_config": "aces_1.0.3", + "workingSpaceLUT": "ACES - ACEScg", + "defaultViewerLUT": "OCIO LUTs", + "monitorLut": "ACES/sRGB", + "int8Lut": "Utility - sRGB - Texture", + "int16Lut": "Utility - sRGB - Texture", + "logLut": "Input - ADX - ADX10", + "floatLut": "ACES - ACES2065-1" + }, + "viewer": { + "viewerProcess": "sRGB (ACES)" + }, + "write": { + "render": { + "colorspace": "ACES - ACES2065-1" + }, + "prerender": { + "colorspace": "ACES - ACES2065-1" + }, + "still": { + "colorspace": "Utility - Curve - sRGB" + } + }, + "read": { + "[^-a-zA-Z0-9](beauty)[^-a-zA-Z0-9]": "lin_srgb", + "[^-a-zA-Z0-9](P|N|Z|crypto)[^-a-zA-Z0-9]": "raw", + "[^-a-zA-Z0-9](plateRef)[^-a-zA-Z0-9]": "crv_srgb" + } + }, + "maya": { + + }, + "houdini": { + + } +} diff --git a/pype/configurations/defaults/presets/colorspace/default.json b/pype/configurations/defaults/presets/colorspace/default.json new file mode 100644 index 0000000000..8b934f810d --- /dev/null +++ b/pype/configurations/defaults/presets/colorspace/default.json @@ -0,0 +1,42 @@ +{ + "nuke": { + "root": { + "colorManagement": "Nuke", + "OCIO_config": "nuke-default", + "defaultViewerLUT": "Nuke Root LUTs", + "monitorLut": "sRGB", + "int8Lut": "sRGB", + "int16Lut": "sRGB", + "logLut": "Cineon", + "floatLut": "linear" + }, + "viewer": { + "viewerProcess": "sRGB" + }, + "write": { + "render": { + "colorspace": "linear" + }, + "prerender": { + "colorspace": "linear" + }, + "still": { + "colorspace": "sRGB" + } + }, + "read": { + "[^-a-zA-Z0-9]beauty[^-a-zA-Z0-9]": "linear", + "[^-a-zA-Z0-9](P|N|Z|crypto)[^-a-zA-Z0-9]": "linear", + "[^-a-zA-Z0-9](plateRef)[^-a-zA-Z0-9]": "sRGB" + } + }, + "maya": { + + }, + "houdini": { + + }, + "resolve": { + + } +} diff --git a/pype/configurations/defaults/presets/dataflow/aces-exr.json b/pype/configurations/defaults/presets/dataflow/aces-exr.json new file mode 100644 index 0000000000..75846c0bd6 --- /dev/null +++ b/pype/configurations/defaults/presets/dataflow/aces-exr.json @@ -0,0 +1,58 @@ +{ + "nuke": { + "nodes": { + "connected": true, + "modifymetadata": { + "_id": "connect_metadata", + "_previous": "ENDING", + "metadata.set.pype_studio_name": "{PYPE_STUDIO_NAME}", + "metadata.set.avalon_project_name": "{AVALON_PROJECT}", + "metadata.set.avalon_project_code": "{PYPE_STUDIO_CODE}", + "metadata.set.avalon_asset_name": "{AVALON_ASSET}" + }, + "crop": { + "_id": "connect_crop", + "_previous": "connect_metadata", + "box": [ + "{metadata.crop.x}", + "{metadata.crop.y}", + "{metadata.crop.right}", + "{metadata.crop.top}" + ] + }, + "write": { + "render": { + "_id": "output_write", + "_previous": "connect_crop", + "file_type": "exr", + "datatype": "16 bit half", + "compression": "Zip (1 scanline)", + "create_directories": true, + "autocrop": true, + "tile_color": "0xff0000ff", + "channels": "rgb" + }, + "prerender": { + "_id": "output_write", + "_previous": "connect_crop", + "file_type": "exr", + "datatype": "16 bit half", + "compression": "Zip (1 scanline)", + "create_directories": true, + "autocrop": false, + "tile_color": "0xc9892aff", + "channels": "rgba" + }, + "still": { + "_previous": "connect_crop", + "channels": "rgba", + "file_type": "tiff", + "datatype": "16 bit", + "compression": "LZW", + "create_directories": true, + "tile_color": "0x4145afff" + } + } + } + } +} diff --git a/pype/configurations/defaults/presets/dataflow/default.json b/pype/configurations/defaults/presets/dataflow/default.json new file mode 100644 index 0000000000..d2f470b5bc --- /dev/null +++ b/pype/configurations/defaults/presets/dataflow/default.json @@ -0,0 +1,55 @@ +{ + "nuke": { + "nodes": { + "connected": true, + "modifymetadata": { + "_id": "connect_metadata", + "_previous": "ENDING", + "metadata.set.pype_studio_name": "{PYPE_STUDIO_NAME}", + "metadata.set.avalon_project_name": "{AVALON_PROJECT}", + "metadata.set.avalon_project_code": "{PYPE_STUDIO_CODE}", + "metadata.set.avalon_asset_name": "{AVALON_ASSET}" + }, + "crop": { + "_id": "connect_crop", + "_previous": "connect_metadata", + "box": [ + "{metadata.crop.x}", + "{metadata.crop.y}", + "{metadata.crop.right}", + "{metadata.crop.top}" + ] + }, + "write": { + "render": { + "_id": "output_write", + "_previous": "connect_crop", + "file_type": "exr", + "datatype": "16 bit half", + "compression": "Zip (1 scanline)", + "autocrop": true, + "tile_color": "0xff0000ff", + "channels": "rgb" + }, + "prerender": { + "_id": "output_write", + "_previous": "connect_crop", + "file_type": "exr", + "datatype": "16 bit half", + "compression": "Zip (1 scanline)", + "autocrop": false, + "tile_color": "0xc9892aff", + "channels": "rgba" + }, + "still": { + "_previous": "connect_crop", + "channels": "rgba", + "file_type": "tiff", + "datatype": "16 bit", + "compression": "LZW", + "tile_color": "0x4145afff" + } + } + } + } +} diff --git a/pype/configurations/defaults/presets/ftrack/ftrack_config.json b/pype/configurations/defaults/presets/ftrack/ftrack_config.json new file mode 100644 index 0000000000..1ef3a9d69f --- /dev/null +++ b/pype/configurations/defaults/presets/ftrack/ftrack_config.json @@ -0,0 +1,16 @@ +{ + "sync_to_avalon": { + "statuses_name_change": ["not ready", "ready"] + }, + + "status_update": { + "_ignore_": ["in progress", "ommited", "on hold"], + "Ready": ["not ready"], + "In Progress" : ["_any_"] + }, + "status_version_to_task": { + "__description__": "Status `from` (key) must be lowered!", + "in progress": "in progress", + "approved": "approved" + } +} diff --git a/pype/configurations/defaults/presets/ftrack/ftrack_custom_attributes.json b/pype/configurations/defaults/presets/ftrack/ftrack_custom_attributes.json new file mode 100644 index 0000000000..f03d473cd0 --- /dev/null +++ b/pype/configurations/defaults/presets/ftrack/ftrack_custom_attributes.json @@ -0,0 +1,165 @@ +[{ + "label": "FPS", + "key": "fps", + "type": "number", + "is_hierarchical": true, + "group": "avalon", + "write_security_role": ["ALL"], + "read_security_role": ["ALL"], + "default": null, + "config": { + "isdecimal": true + } +}, { + "label": "Applications", + "key": "applications", + "type": "enumerator", + "entity_type": "show", + "group": "avalon", + "config": { + "multiselect": true, + "data": [ + {"blender_2.80": "Blender 2.80"}, + {"blender_2.81": "Blender 2.81"}, + {"blender_2.82": "Blender 2.82"}, + {"blender_2.83": "Blender 2.83"}, + {"celaction_local": "CelAction2D Local"}, + {"maya_2017": "Maya 2017"}, + {"maya_2018": "Maya 2018"}, + {"maya_2019": "Maya 2019"}, + {"nuke_10.0": "Nuke 10.0"}, + {"nuke_11.2": "Nuke 11.2"}, + {"nuke_11.3": "Nuke 11.3"}, + {"nuke_12.0": "Nuke 12.0"}, + {"nukex_10.0": "NukeX 10.0"}, + {"nukex_11.2": "NukeX 11.2"}, + {"nukex_11.3": "NukeX 11.3"}, + {"nukex_12.0": "NukeX 12.0"}, + {"nukestudio_10.0": "NukeStudio 10.0"}, + {"nukestudio_11.2": "NukeStudio 11.2"}, + {"nukestudio_11.3": "NukeStudio 11.3"}, + {"nukestudio_12.0": "NukeStudio 12.0"}, + {"harmony_17": "Harmony 17"}, + {"houdini_16.5": "Houdini 16.5"}, + {"houdini_17": "Houdini 17"}, + {"houdini_18": "Houdini 18"}, + {"photoshop_2020": "Photoshop 2020"}, + {"python_3": "Python 3"}, + {"python_2": "Python 2"}, + {"premiere_2019": "Premiere Pro 2019"}, + {"premiere_2020": "Premiere Pro 2020"}, + {"resolve_16": "BM DaVinci Resolve 16"} + ] + } +}, { + "label": "Avalon auto-sync", + "key": "avalon_auto_sync", + "type": "boolean", + "entity_type": "show", + "group": "avalon", + "write_security_role": ["API", "Administrator"], + "read_security_role": ["API", "Administrator"] +}, { + "label": "Intent", + "key": "intent", + "type": "enumerator", + "entity_type": "assetversion", + "group": "avalon", + "config": { + "multiselect": false, + "data": [ + {"test": "Test"}, + {"wip": "WIP"}, + {"final": "Final"} + ] + } +}, { + "label": "Library Project", + "key": "library_project", + "type": "boolean", + "entity_type": "show", + "group": "avalon", + "write_security_role": ["API", "Administrator"], + "read_security_role": ["API", "Administrator"] +}, { + "label": "Clip in", + "key": "clipIn", + "type": "number", + "is_hierarchical": true, + "group": "avalon", + "default": null +}, { + "label": "Clip out", + "key": "clipOut", + "type": "number", + "is_hierarchical": true, + "group": "avalon", + "default": null +}, { + "label": "Frame start", + "key": "frameStart", + "type": "number", + "is_hierarchical": true, + "group": "avalon", + "default": null +}, { + "label": "Frame end", + "key": "frameEnd", + "type": "number", + "is_hierarchical": true, + "group": "avalon", + "default": null +}, { + "label": "Tools", + "key": "tools_env", + "type": "enumerator", + "is_hierarchical": true, + "group": "avalon", + "config": { + "multiselect": true, + "data": [ + {"mtoa_3.0.1": "mtoa_3.0.1"}, + {"mtoa_3.1.1": "mtoa_3.1.1"}, + {"mtoa_3.2.0": "mtoa_3.2.0"}, + {"yeti_2.1.2": "yeti_2.1"} + ] + } +}, { + "label": "Resolution Width", + "key": "resolutionWidth", + "type": "number", + "is_hierarchical": true, + "group": "avalon", + "default": null +}, { + "label": "Resolution Height", + "key": "resolutionHeight", + "type": "number", + "is_hierarchical": true, + "group": "avalon", + "default": null +}, { + "label": "Pixel aspect", + "key": "pixelAspect", + "type": "number", + "is_hierarchical": true, + "group": "avalon", + "config": { + "isdecimal": true + } +}, { + "label": "Frame handles start", + "key": "handleStart", + "type": "number", + "is_hierarchical": true, + "group": "avalon", + "default": null +}, { + "label": "Frame handles end", + "key": "handleEnd", + "type": "number", + "is_hierarchical": true, + "group": "avalon", + "default": null +} +] diff --git a/pype/configurations/defaults/presets/ftrack/partnership_ftrack_cred.json b/pype/configurations/defaults/presets/ftrack/partnership_ftrack_cred.json new file mode 100644 index 0000000000..6b3a32f181 --- /dev/null +++ b/pype/configurations/defaults/presets/ftrack/partnership_ftrack_cred.json @@ -0,0 +1,5 @@ +{ + "server_url": "", + "api_key": "", + "api_user": "" +} diff --git a/pype/configurations/defaults/presets/ftrack/plugins/server.json b/pype/configurations/defaults/presets/ftrack/plugins/server.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/pype/configurations/defaults/presets/ftrack/plugins/server.json @@ -0,0 +1 @@ +{} diff --git a/pype/configurations/defaults/presets/ftrack/plugins/user.json b/pype/configurations/defaults/presets/ftrack/plugins/user.json new file mode 100644 index 0000000000..1ba8e9b511 --- /dev/null +++ b/pype/configurations/defaults/presets/ftrack/plugins/user.json @@ -0,0 +1,5 @@ +{ + "TestAction": { + "ignore_me": true + } +} diff --git a/pype/configurations/defaults/presets/ftrack/project_defaults.json b/pype/configurations/defaults/presets/ftrack/project_defaults.json new file mode 100644 index 0000000000..a4e3aa3362 --- /dev/null +++ b/pype/configurations/defaults/presets/ftrack/project_defaults.json @@ -0,0 +1,18 @@ +{ + "fps": 25, + "frameStart": 1001, + "frameEnd": 1100, + "clipIn": 1001, + "clipOut": 1100, + "handleStart": 10, + "handleEnd": 10, + + "resolutionHeight": 1080, + "resolutionWidth": 1920, + "pixelAspect": 1.0, + "applications": [ + "maya_2019", "nuke_11.3", "nukex_11.3", "nukestudio_11.3", "deadline" + ], + "tools_env": [], + "avalon_auto_sync": true +} diff --git a/pype/configurations/defaults/presets/init.json b/pype/configurations/defaults/presets/init.json new file mode 100644 index 0000000000..361ee7445b --- /dev/null +++ b/pype/configurations/defaults/presets/init.json @@ -0,0 +1,4 @@ +{ + "colorspace": "default", + "dataflow": "default" +} diff --git a/pype/configurations/defaults/presets/maya/capture.json b/pype/configurations/defaults/presets/maya/capture.json new file mode 100644 index 0000000000..b6c4893034 --- /dev/null +++ b/pype/configurations/defaults/presets/maya/capture.json @@ -0,0 +1,108 @@ +{ + "Codec": { + "compression": "jpg", + "format": "image", + "quality": 95 + }, + "Display Options": { + "background": [ + 0.7137254901960784, + 0.7137254901960784, + 0.7137254901960784 + ], + "backgroundBottom": [ + 0.7137254901960784, + 0.7137254901960784, + 0.7137254901960784 + ], + "backgroundTop": [ + 0.7137254901960784, + 0.7137254901960784, + 0.7137254901960784 + ], + "override_display": true + }, + "Generic": { + "isolate_view": true, + "off_screen": true + }, + "IO": { + "name": "", + "open_finished": false, + "raw_frame_numbers": false, + "recent_playblasts": [], + "save_file": false + }, + "PanZoom": { + "pan_zoom": true + }, + "Renderer": { + "rendererName": "vp2Renderer" + }, + "Resolution": { + "height": 1080, + "mode": "Custom", + "percent": 1.0, + "width": 1920 + }, + "Time Range": { + "end_frame": 25, + "frame": "", + "start_frame": 0, + "time": "Time Slider" + }, + "Viewport Options": { + "cameras": false, + "clipGhosts": false, + "controlVertices": false, + "deformers": false, + "dimensions": false, + "displayLights": 0, + "dynamicConstraints": false, + "dynamics": false, + "fluids": false, + "follicles": false, + "gpuCacheDisplayFilter": false, + "greasePencils": false, + "grid": false, + "hairSystems": false, + "handles": false, + "high_quality": true, + "hud": false, + "hulls": false, + "ikHandles": false, + "imagePlane": false, + "joints": false, + "lights": false, + "locators": false, + "manipulators": false, + "motionTrails": false, + "nCloths": false, + "nParticles": false, + "nRigids": false, + "nurbsCurves": false, + "nurbsSurfaces": false, + "override_viewport_options": true, + "particleInstancers": false, + "pivots": false, + "planes": false, + "pluginShapes": false, + "polymeshes": true, + "shadows": false, + "strokes": false, + "subdivSurfaces": false, + "textures": false, + "twoSidedLighting": true + }, + "Camera Options": { + "displayGateMask": false, + "displayResolution": false, + "displayFilmGate": false, + "displayFieldChart": false, + "displaySafeAction": false, + "displaySafeTitle": false, + "displayFilmPivot": false, + "displayFilmOrigin": false, + "overscan": 1.0 + } +} diff --git a/pype/configurations/defaults/presets/muster/templates_mapping.json b/pype/configurations/defaults/presets/muster/templates_mapping.json new file mode 100644 index 0000000000..4edab9077d --- /dev/null +++ b/pype/configurations/defaults/presets/muster/templates_mapping.json @@ -0,0 +1,19 @@ +{ + "3delight": 41, + "arnold": 46, + "arnold_sf": 57, + "gelato": 30, + "harware": 3, + "krakatoa": 51, + "file_layers": 7, + "mentalray": 2, + "mentalray_sf": 6, + "redshift": 55, + "renderman": 29, + "software": 1, + "software_sf": 5, + "turtle": 10, + "vector": 4, + "vray": 37, + "ffmpeg": 48 +} diff --git a/pype/configurations/defaults/presets/nukestudio/tags.json b/pype/configurations/defaults/presets/nukestudio/tags.json new file mode 100644 index 0000000000..56fcfcbce9 --- /dev/null +++ b/pype/configurations/defaults/presets/nukestudio/tags.json @@ -0,0 +1,262 @@ +{ + "Hierarchy": { + "editable": "1", + "note": "{folder}/{sequence}/{shot}", + "icon": { + "path": "hierarchy.png" + }, + "metadata": { + "folder": "FOLDER_NAME", + "shot": "{clip}", + "track": "{track}", + "sequence": "{sequence}", + "episode": "EPISODE_NAME", + "root": "{projectroot}" + } + }, + "Source Resolution": { + "editable": "1", + "note": "Use source resolution", + "icon": { + "path": "resolution.png" + }, + "metadata": { + "family": "resolution" + } + }, + "Retiming": { + "editable": "1", + "note": "Clip has retime or TimeWarp effects (or multiple effects stacked on the clip)", + "icon": { + "path": "retiming.png" + }, + "metadata": { + "family": "retiming", + "marginIn": 1, + "marginOut": 1 + } + }, + "Frame start": { + "editable": "1", + "note": "Starting frame for comps. \n\n> Use `value` and add either number or write `source` (if you want to preserve source frame numbering)", + "icon": { + "path": "icons:TagBackground.png" + }, + "metadata": { + "family": "frameStart", + "value": "1001" + } + }, + "[Lenses]": { + "Set lense here": { + "editable": "1", + "note": "Adjust parameters of your lense and then drop to clip. Remember! You can always overwrite on clip", + "icon": { + "path": "lense.png" + }, + "metadata": { + "focalLengthMm": 57 + + } + } + }, + "[Subsets]": { + "Audio": { + "editable": "1", + "note": "Export with Audio", + "icon": { + "path": "volume.png" + }, + "metadata": { + "family": "audio", + "subset": "main" + } + }, + "plateFg": { + "editable": "1", + "note": "Add to publish to \"forground\" subset. Change metadata subset name if different order number", + "icon": { + "path": "z_layer_fg.png" + }, + "metadata": { + "family": "plate", + "subset": "Fg01" + } + }, + "plateBg": { + "editable": "1", + "note": "Add to publish to \"background\" subset. Change metadata subset name if different order number", + "icon": { + "path": "z_layer_bg.png" + }, + "metadata": { + "family": "plate", + "subset": "Bg01" + } + }, + "plateRef": { + "editable": "1", + "note": "Add to publish to \"reference\" subset.", + "icon": { + "path": "icons:Reference.png" + }, + "metadata": { + "family": "plate", + "subset": "Ref" + } + }, + "plateMain": { + "editable": "1", + "note": "Add to publish to \"main\" subset.", + "icon": { + "path": "z_layer_main.png" + }, + "metadata": { + "family": "plate", + "subset": "main" + } + }, + "plateProxy": { + "editable": "1", + "note": "Add to publish to \"proxy\" subset.", + "icon": { + "path": "z_layer_main.png" + }, + "metadata": { + "family": "plate", + "subset": "proxy" + } + }, + "review": { + "editable": "1", + "note": "Upload to Ftrack as review component.", + "icon": { + "path": "review.png" + }, + "metadata": { + "family": "review", + "track": "review" + } + } + }, + "[Handles]": { + "start: add 20 frames": { + "editable": "1", + "note": "Adding frames to start of selected clip", + "icon": { + "path": "3_add_handles_start.png" + }, + "metadata": { + "family": "handles", + "value": "20", + "args": "{'op':'add','where':'start'}" + } + }, + "start: add 10 frames": { + "editable": "1", + "note": "Adding frames to start of selected clip", + "icon": { + "path": "3_add_handles_start.png" + }, + "metadata": { + "family": "handles", + "value": "10", + "args": "{'op':'add','where':'start'}" + } + }, + "start: add 5 frames": { + "editable": "1", + "note": "Adding frames to start of selected clip", + "icon": { + "path": "3_add_handles_start.png" + }, + "metadata": { + "family": "handles", + "value": "5", + "args": "{'op':'add','where':'start'}" + } + }, + "start: add 0 frames": { + "editable": "1", + "note": "Adding frames to start of selected clip", + "icon": { + "path": "3_add_handles_start.png" + }, + "metadata": { + "family": "handles", + "value": "0", + "args": "{'op':'add','where':'start'}" + } + }, + "end: add 20 frames": { + "editable": "1", + "note": "Adding frames to end of selected clip", + "icon": { + "path": "1_add_handles_end.png" + }, + "metadata": { + "family": "handles", + "value": "20", + "args": "{'op':'add','where':'end'}" + } + }, + "end: add 10 frames": { + "editable": "1", + "note": "Adding frames to end of selected clip", + "icon": { + "path": "1_add_handles_end.png" + }, + "metadata": { + "family": "handles", + "value": "10", + "args": "{'op':'add','where':'end'}" + } + }, + "end: add 5 frames": { + "editable": "1", + "note": "Adding frames to end of selected clip", + "icon": { + "path": "1_add_handles_end.png" + }, + "metadata": { + "family": "handles", + "value": "5", + "args": "{'op':'add','where':'end'}" + } + }, + "end: add 0 frames": { + "editable": "1", + "note": "Adding frames to end of selected clip", + "icon": { + "path": "1_add_handles_end.png" + }, + "metadata": { + "family": "handles", + "value": "0", + "args": "{'op':'add','where':'end'}" + } + } + }, + "NukeScript": { + "editable": "1", + "note": "Collecting track items to Nuke scripts.", + "icon": { + "path": "icons:TagNuke.png" + }, + "metadata": { + "family": "nukescript", + "subset": "main" + } + }, + "Comment": { + "editable": "1", + "note": "Comment on a shot.", + "icon": { + "path": "icons:TagComment.png" + }, + "metadata": { + "family": "comment", + "subset": "main" + } + } +} diff --git a/pype/configurations/defaults/presets/plugins/celaction/publish.json b/pype/configurations/defaults/presets/plugins/celaction/publish.json new file mode 100644 index 0000000000..e791f574d9 --- /dev/null +++ b/pype/configurations/defaults/presets/plugins/celaction/publish.json @@ -0,0 +1,10 @@ +{ + "ExtractCelactionDeadline": { + "deadline_department": "", + "deadline_priority": 50, + "deadline_pool": "", + "deadline_pool_secondary": "", + "deadline_group": "", + "deadline_chunk_size": 10 + } +} diff --git a/pype/configurations/defaults/presets/plugins/config.json b/pype/configurations/defaults/presets/plugins/config.json new file mode 100644 index 0000000000..9e26dfeeb6 --- /dev/null +++ b/pype/configurations/defaults/presets/plugins/config.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/pype/configurations/defaults/presets/plugins/ftrack/publish.json b/pype/configurations/defaults/presets/plugins/ftrack/publish.json new file mode 100644 index 0000000000..d0469ae4f7 --- /dev/null +++ b/pype/configurations/defaults/presets/plugins/ftrack/publish.json @@ -0,0 +1,6 @@ +{ + "IntegrateFtrackNote": { + "note_with_intent_template": "{intent}: {comment}", + "note_labels": [] + } +} diff --git a/pype/configurations/defaults/presets/plugins/global/create.json b/pype/configurations/defaults/presets/plugins/global/create.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/pype/configurations/defaults/presets/plugins/global/create.json @@ -0,0 +1 @@ +{} diff --git a/pype/configurations/defaults/presets/plugins/global/filter.json b/pype/configurations/defaults/presets/plugins/global/filter.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/pype/configurations/defaults/presets/plugins/global/filter.json @@ -0,0 +1 @@ +{} diff --git a/pype/configurations/defaults/presets/plugins/global/load.json b/pype/configurations/defaults/presets/plugins/global/load.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/pype/configurations/defaults/presets/plugins/global/load.json @@ -0,0 +1 @@ +{} diff --git a/pype/configurations/defaults/presets/plugins/global/publish.json b/pype/configurations/defaults/presets/plugins/global/publish.json new file mode 100644 index 0000000000..016868fc92 --- /dev/null +++ b/pype/configurations/defaults/presets/plugins/global/publish.json @@ -0,0 +1,86 @@ +{ + "IntegrateMasterVersion": { + "enabled": false + }, + "ExtractJpegEXR": { + "ffmpeg_args": { + "input": [ + "-gamma 2.2" + ], + "output": [] + } + }, + "ExtractReview": { + "__documentation__": "http://pype.club/docs/admin_presets_plugins", + "profiles": [ + { + "families": [], + "hosts": [], + "outputs": { + "h264": { + "filter": { + "families": ["render", "review", "ftrack"] + }, + "ext": "mp4", + "ffmpeg_args": { + "input": [ + "-gamma 2.2" + ], + "video_filters": [], + "audio_filters": [], + "output": [ + "-pix_fmt yuv420p", + "-crf 18", + "-intra" + ] + }, + "tags": ["burnin", "ftrackreview"] + } + } + } + ] + }, + "ExtractBurnin": { + "options": { + "opacity": 1, + "x_offset": 5, + "y_offset": 5, + "bg_padding": 5, + "bg_opacity": 0.5, + "font_size": 42 + }, + "fields": { + + }, + "profiles": [ + { + "burnins": { + "burnin": { + "TOP_LEFT": "{yy}-{mm}-{dd}", + "TOP_RIGHT": "{anatomy[version]}", + "TOP_CENTERED": "", + "BOTTOM_RIGHT": "{frame_start}-{current_frame}-{frame_end}", + "BOTTOM_CENTERED": "{asset}", + "BOTTOM_LEFT": "{username}" + } + } + } + ] + }, + "IntegrateAssetNew": { + "template_name_profiles": { + "publish": { + "families": [], + "tasks": [] + }, + "render": { + "families": ["review", "render", "prerender"] + } + } + }, + "ProcessSubmittedJobOnFarm": { + "deadline_department": "", + "deadline_pool": "", + "deadline_group": "" + } +} diff --git a/pype/configurations/defaults/presets/plugins/maya/create.json b/pype/configurations/defaults/presets/plugins/maya/create.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/pype/configurations/defaults/presets/plugins/maya/create.json @@ -0,0 +1 @@ +{} diff --git a/pype/configurations/defaults/presets/plugins/maya/filter.json b/pype/configurations/defaults/presets/plugins/maya/filter.json new file mode 100644 index 0000000000..83d6f05f31 --- /dev/null +++ b/pype/configurations/defaults/presets/plugins/maya/filter.json @@ -0,0 +1,9 @@ +{ + "Preset n1": { + "ValidateNoAnimation": false, + "ValidateShapeDefaultNames": false + }, + "Preset n2": { + "ValidateNoAnimation": false + } +} diff --git a/pype/configurations/defaults/presets/plugins/maya/load.json b/pype/configurations/defaults/presets/plugins/maya/load.json new file mode 100644 index 0000000000..260fbb35ee --- /dev/null +++ b/pype/configurations/defaults/presets/plugins/maya/load.json @@ -0,0 +1,18 @@ +{ + "colors": { + "model": [0.821, 0.518, 0.117], + "rig": [0.144, 0.443, 0.463], + "pointcache": [0.368, 0.821, 0.117], + "animation": [0.368, 0.821, 0.117], + "ass": [1.0, 0.332, 0.312], + "camera": [0.447, 0.312, 1.0], + "fbx": [1.0, 0.931, 0.312], + "mayaAscii": [0.312, 1.0, 0.747], + "setdress": [0.312, 1.0, 0.747], + "layout": [0.312, 1.0, 0.747], + "vdbcache": [0.312, 1.0, 0.428], + "vrayproxy": [0.258, 0.95, 0.541], + "yeticache": [0.2, 0.8, 0.3], + "yetiRig": [0, 0.8, 0.5] + } +} diff --git a/pype/configurations/defaults/presets/plugins/maya/publish.json b/pype/configurations/defaults/presets/plugins/maya/publish.json new file mode 100644 index 0000000000..2e2b3164f3 --- /dev/null +++ b/pype/configurations/defaults/presets/plugins/maya/publish.json @@ -0,0 +1,17 @@ +{ + "ValidateModelName": { + "enabled": false, + "material_file": "/path/to/shader_name_definition.txt", + "regex": "(.*)_(\\d)*_(?P.*)_(GEO)" + }, + "ValidateAssemblyName": { + "enabled": false + }, + "ValidateShaderName": { + "enabled": false, + "regex": "(?P.*)_(.*)_SHD" + }, + "ValidateMeshHasOverlappingUVs": { + "enabled": false + } +} diff --git a/pype/configurations/defaults/presets/plugins/maya/workfile_build.json b/pype/configurations/defaults/presets/plugins/maya/workfile_build.json new file mode 100644 index 0000000000..2872b783cb --- /dev/null +++ b/pype/configurations/defaults/presets/plugins/maya/workfile_build.json @@ -0,0 +1,54 @@ +[{ + "tasks": ["lighting"], + + "current_context": [{ + "subset_name_filters": [".+[Mm]ain"], + "families": ["model"], + "repre_names": ["abc", "ma"], + "loaders": ["ReferenceLoader"] + }, { + "families": ["animation", "pointcache"], + "repre_names": ["abc"], + "loaders": ["ReferenceLoader"] + },{ + "families": ["rendersetup"], + "repre_names": ["json"], + "loaders": ["RenderSetupLoader"] + }, { + "families": ["camera"], + "repre_names": ["abc"], + "loaders": ["ReferenceLoader"] + }], + + "linked_assets": [{ + "families": ["setdress"], + "repre_names": ["ma"], + "loaders": ["ReferenceLoader"] + }, { + "families": ["ass"], + "repre_names": ["ass"], + "loaders":["assLoader"] + }] +}, { + "tasks": ["animation"], + + "current_context": [{ + "families": ["camera"], + "repre_names": ["abc", "ma"], + "loaders": ["ReferenceLoader"] + }, { + "families": ["audio"], + "repre_names": ["wav"], + "loaders": ["RenderSetupLoader"] + }], + + "linked_assets": [{ + "families": ["setdress"], + "repre_names": ["proxy"], + "loaders": ["ReferenceLoader"] + }, { + "families": ["rig"], + "repre_names": ["ass"], + "loaders": ["rigLoader"] + }] +}] diff --git a/pype/configurations/defaults/presets/plugins/nuke/create.json b/pype/configurations/defaults/presets/plugins/nuke/create.json new file mode 100644 index 0000000000..4deb0b4ad5 --- /dev/null +++ b/pype/configurations/defaults/presets/plugins/nuke/create.json @@ -0,0 +1,8 @@ +{ + "CreateWriteRender": { + "fpath_template": "{work}/renders/nuke/{subset}/{subset}.{frame}.{ext}" + }, + "CreateWritePrerender": { + "fpath_template": "{work}/prerenders/nuke/{subset}/{subset}.{frame}.{ext}" + } +} diff --git a/pype/configurations/defaults/presets/plugins/nuke/load.json b/pype/configurations/defaults/presets/plugins/nuke/load.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/pype/configurations/defaults/presets/plugins/nuke/load.json @@ -0,0 +1 @@ +{} diff --git a/pype/configurations/defaults/presets/plugins/nuke/publish.json b/pype/configurations/defaults/presets/plugins/nuke/publish.json new file mode 100644 index 0000000000..ab0d0e76a5 --- /dev/null +++ b/pype/configurations/defaults/presets/plugins/nuke/publish.json @@ -0,0 +1,48 @@ +{ + "ExtractThumbnail": { + "nodes": { + "Reformat": [ + ["type", "to format"], + ["format", "HD_1080"], + ["filter", "Lanczos6"], + ["black_outside", true], + ["pbb", false] + ] + } + }, + "ValidateNukeWriteKnobs": { + "enabled": false, + "knobs": { + "render": { + "review": true + } + } + }, + "ExtractReviewDataLut": { + "__documentation__": { + "viewer_lut_raw": "set to `true` if you Input_process node on viewer is used with baked screen space, so you have to look with RAW viewer lut. Else just keep it on `false`", + "enabled": "keep in on `false` if you want Nuke baking colorspace workflow applied else FFmpeg will convert imgsequence with baked LUT" + }, + "enabled": false + }, + "ExtractReviewDataMov": { + "__documentation__": { + "viewer_lut_raw": "set to `true` if you Input_process node on viewer is used with baked screen space, so you have to look with RAW viewer lut. Else just keep it on `false`", + "enabled": "keep in on `true` if you want Nuke baking colorspace workflow applied" + }, + "enabled": true, + "viewer_lut_raw": false + }, + "ExtractSlateFrame": { + "__documentation__": { + "viewer_lut_raw": "set to `true` if you Input_process node on viewer is used with baked screen space, so you have to look with RAW viewer lut. Else just keep it on `false`" + }, + "viewer_lut_raw": false + }, + "NukeSubmitDeadline": { + "deadline_priority": 50, + "deadline_pool": "", + "deadline_pool_secondary": "", + "deadline_chunk_size": 1 + } +} diff --git a/pype/configurations/defaults/presets/plugins/nuke/workfile_build.json b/pype/configurations/defaults/presets/plugins/nuke/workfile_build.json new file mode 100644 index 0000000000..d3613c929e --- /dev/null +++ b/pype/configurations/defaults/presets/plugins/nuke/workfile_build.json @@ -0,0 +1,11 @@ +[{ + "tasks": ["compositing"], + + "current_context": [{ + "families": ["render", "plate"], + "repre_names": ["exr" ,"dpx"], + "loaders": ["LoadSequence"] + }], + + "linked_assets": [] +}] diff --git a/pype/configurations/defaults/presets/plugins/nukestudio/filter.json b/pype/configurations/defaults/presets/plugins/nukestudio/filter.json new file mode 100644 index 0000000000..bd6a0dc1bd --- /dev/null +++ b/pype/configurations/defaults/presets/plugins/nukestudio/filter.json @@ -0,0 +1,10 @@ +{ + "strict": { + "ValidateVersion": true, + "VersionUpWorkfile": true + }, + "benevolent": { + "ValidateVersion": false, + "VersionUpWorkfile": false + } +} \ No newline at end of file diff --git a/pype/configurations/defaults/presets/plugins/nukestudio/publish.json b/pype/configurations/defaults/presets/plugins/nukestudio/publish.json new file mode 100644 index 0000000000..8c4ad133f1 --- /dev/null +++ b/pype/configurations/defaults/presets/plugins/nukestudio/publish.json @@ -0,0 +1,8 @@ +{ + "CollectInstanceVersion": { + "enabled": false + }, + "ExtractReviewCutUpVideo": { + "tags_addition": [] + } +} diff --git a/pype/configurations/defaults/presets/plugins/resolve/create.json b/pype/configurations/defaults/presets/plugins/resolve/create.json new file mode 100644 index 0000000000..29ca5900fb --- /dev/null +++ b/pype/configurations/defaults/presets/plugins/resolve/create.json @@ -0,0 +1,7 @@ +{ + "CreateShotClip": { + "clipName": "{track}{sequence}{shot}", + "folder": "takes", + "steps": 20 + } +} diff --git a/pype/configurations/defaults/presets/plugins/standalonepublisher/publish.json b/pype/configurations/defaults/presets/plugins/standalonepublisher/publish.json new file mode 100644 index 0000000000..2b2fb660c2 --- /dev/null +++ b/pype/configurations/defaults/presets/plugins/standalonepublisher/publish.json @@ -0,0 +1,25 @@ +{ + "ExtractThumbnailSP": { + "ffmpeg_args": { + "input": [ + "-gamma 2.2" + ], + "output": [] + } + }, + "ExtractReviewSP": { + "outputs": { + "h264": { + "input": [ + "-gamma 2.2" + ], + "output": [ + "-pix_fmt yuv420p", + "-crf 18" + ], + "tags": ["preview"], + "ext": "mov" + } + } + } +} diff --git a/pype/configurations/defaults/presets/plugins/test/create.json b/pype/configurations/defaults/presets/plugins/test/create.json new file mode 100644 index 0000000000..fa0b2fc05f --- /dev/null +++ b/pype/configurations/defaults/presets/plugins/test/create.json @@ -0,0 +1,8 @@ +{ + "MyTestCreator": { + "my_test_property": "B", + "active": false, + "new_property": "new", + "family": "new_family" + } +} diff --git a/pype/configurations/defaults/presets/plugins/test/publish.json b/pype/configurations/defaults/presets/plugins/test/publish.json new file mode 100644 index 0000000000..3180dd5d8a --- /dev/null +++ b/pype/configurations/defaults/presets/plugins/test/publish.json @@ -0,0 +1,10 @@ +{ + "MyTestPlugin": { + "label": "loaded from preset", + "optional": true, + "families": ["changed", "by", "preset"] + }, + "MyTestRemovedPlugin": { + "enabled": false + } +} diff --git a/pype/configurations/defaults/presets/premiere/asset_default.json b/pype/configurations/defaults/presets/premiere/asset_default.json new file mode 100644 index 0000000000..84d2bde3d8 --- /dev/null +++ b/pype/configurations/defaults/presets/premiere/asset_default.json @@ -0,0 +1,5 @@ +{ + "frameStart": 1001, + "handleStart": 0, + "handleEnd": 0 +} diff --git a/pype/configurations/defaults/presets/premiere/rules_tasks.json b/pype/configurations/defaults/presets/premiere/rules_tasks.json new file mode 100644 index 0000000000..333c9cd70b --- /dev/null +++ b/pype/configurations/defaults/presets/premiere/rules_tasks.json @@ -0,0 +1,21 @@ +{ + "defaultTasks": ["Layout", "Animation"], + "taskToSubsets": { + "Layout": ["reference", "audio"], + "Animation": ["audio"] + }, + "subsetToRepresentations": { + "reference": { + "preset": "h264", + "representation": "mp4" + }, + "thumbnail": { + "preset": "jpeg_thumb", + "representation": "jpg" + }, + "audio": { + "preset": "48khz", + "representation": "wav" + } + } +} diff --git a/pype/configurations/defaults/presets/standalone_publish/families.json b/pype/configurations/defaults/presets/standalone_publish/families.json new file mode 100644 index 0000000000..d05941cc26 --- /dev/null +++ b/pype/configurations/defaults/presets/standalone_publish/families.json @@ -0,0 +1,90 @@ +{ + "create_look": { + "name": "look", + "label": "Look", + "family": "look", + "icon": "paint-brush", + "defaults": ["Main"], + "help": "Shader connections defining shape look" + }, + "create_model": { + "name": "model", + "label": "Model", + "family": "model", + "icon": "cube", + "defaults": ["Main", "Proxy", "Sculpt"], + "help": "Polygonal static geometry" + }, + "create_workfile": { + "name": "workfile", + "label": "Workfile", + "family": "workfile", + "icon": "cube", + "defaults": ["Main"], + "help": "Working scene backup" + }, + "create_camera": { + "name": "camera", + "label": "Camera", + "family": "camera", + "icon": "video-camera", + "defaults": ["Main"], + "help": "Single baked camera" + }, + "create_pointcache": { + "name": "pointcache", + "label": "Pointcache", + "family": "pointcache", + "icon": "gears", + "defaults": ["Main"], + "help": "Alembic pointcache for animated data" + }, + "create_rig": { + "name": "rig", + "label": "Rig", + "family": "rig", + "icon": "wheelchair", + "defaults": ["Main"], + "help": "Artist-friendly rig with controls" + }, + "create_layout": { + "name": "layout", + "label": "Layout", + "family": "layout", + "icon": "cubes", + "defaults": ["Main"], + "help": "Simple scene for animators with camera" + }, + "create_plate": { + "name": "plate", + "label": "Plate", + "family": "plate", + "icon": "camera", + "defaults": ["Main", "BG", "Reference"], + "help": "Plates for compositors" + }, + "create_matchmove": { + "name": "matchmove", + "label": "Matchmove script", + "family": "matchmove", + "icon": "empire", + "defaults": ["Camera", "Object", "Mocap"], + "help": "Script exported from matchmoving application" + }, + "create_images": { + "name": "image", + "label": "Image file", + "family": "image", + "icon": "image", + "defaults": ["ConceptArt", "Reference", "Texture", "MattePaint"], + "help": "Holder for all kinds of image data" + }, + "create_editorial": { + "name": "editorial", + "label": "Editorial", + "family": "editorial", + "icon": "image", + "defaults": ["Main"], + "help": "Editorial files to generate shots." + } +} diff --git a/pype/configurations/defaults/presets/tools/creator.json b/pype/configurations/defaults/presets/tools/creator.json new file mode 100644 index 0000000000..d14e779f01 --- /dev/null +++ b/pype/configurations/defaults/presets/tools/creator.json @@ -0,0 +1,8 @@ +{ + "Model": ["model"], + "Render Globals": ["light", "render"], + "Layout": ["layout"], + "Set Dress": ["setdress"], + "Look": ["look"], + "Rig": ["rigging"] +} diff --git a/pype/configurations/defaults/presets/tools/project_folder_structure.json b/pype/configurations/defaults/presets/tools/project_folder_structure.json new file mode 100644 index 0000000000..83bd5f12a9 --- /dev/null +++ b/pype/configurations/defaults/presets/tools/project_folder_structure.json @@ -0,0 +1,22 @@ +{ + "__project_root__": { + "prod" : {}, + "resources" : { + "footage": { + "plates": {}, + "offline": {} + }, + "audio": {}, + "art_dept": {} + }, + "editorial" : {}, + "assets[ftrack.Library]": { + "characters[ftrack]": {}, + "locations[ftrack]": {} + }, + "shots[ftrack.Sequence]": { + "scripts": {}, + "editorial[ftrack.Folder]": {} + } + } +} diff --git a/pype/configurations/defaults/presets/tools/pyblish.json b/pype/configurations/defaults/presets/tools/pyblish.json new file mode 100644 index 0000000000..e81932ec45 --- /dev/null +++ b/pype/configurations/defaults/presets/tools/pyblish.json @@ -0,0 +1,17 @@ +{ + "ui": { + "intents": { + "__description__": [ + "In items you can specify {label: value} of intents", + "`default` may be used for setting default value." + ], + "default": "wip", + "items": { + "": "", + "wip": "WIP", + "test": "TEST", + "final": "FINAL" + } + } + } +} diff --git a/pype/configurations/defaults/presets/tools/slates/example_HD.json b/pype/configurations/defaults/presets/tools/slates/example_HD.json new file mode 100644 index 0000000000..b06391fb63 --- /dev/null +++ b/pype/configurations/defaults/presets/tools/slates/example_HD.json @@ -0,0 +1,212 @@ +{ + "width": 1920, + "height": 1080, + "destination_path": "{destination_path}", + "style": { + "*": { + "font-family": "arial", + "font-color": "#ffffff", + "font-bold": false, + "font-italic": false, + "bg-color": "#0077ff", + "alignment-horizontal": "left", + "alignment-vertical": "top" + }, + "layer": { + "padding": 0, + "margin": 0 + }, + "rectangle": { + "padding": 0, + "margin": 0, + "bg-color": "#E9324B", + "fill": true + }, + "main_frame": { + "padding": 0, + "margin": 0, + "bg-color": "#252525" + }, + "table": { + "padding": 0, + "margin": 0, + "bg-color": "transparent" + }, + "table-item": { + "padding": 5, + "padding-bottom": 10, + "margin": 0, + "bg-color": "#212121", + "bg-alter-color": "#272727", + "font-color": "#dcdcdc", + "font-bold": false, + "font-italic": false, + "alignment-horizontal": "left", + "alignment-vertical": "top", + "word-wrap": false, + "ellide": true, + "max-lines": 1 + }, + "table-item-col[0]": { + "font-size": 20, + "font-color": "#898989", + "font-bold": true, + "ellide": false, + "word-wrap": true, + "max-lines": null + }, + "table-item-col[1]": { + "font-size": 40, + "padding-left": 10 + }, + "#colorbar": { + "bg-color": "#9932CC" + } + }, + "items": [{ + "type": "layer", + "direction": 1, + "name": "MainLayer", + "style": { + "#MainLayer": { + "width": 1094, + "height": 1000, + "margin": 25, + "padding": 0 + }, + "#LeftSide": { + "margin-right": 25 + } + }, + "items": [{ + "type": "layer", + "name": "LeftSide", + "items": [{ + "type": "layer", + "direction": 1, + "style": { + "table-item": { + "bg-color": "transparent", + "padding-bottom": 20 + }, + "table-item-col[0]": { + "font-size": 20, + "font-color": "#898989", + "alignment-horizontal": "right" + }, + "table-item-col[1]": { + "alignment-horizontal": "left", + "font-bold": true, + "font-size": 40 + } + }, + "items": [{ + "type": "table", + "values": [ + ["Show:", "{project[name]}"] + ], + "style": { + "table-item-field[0:0]": { + "width": 150 + }, + "table-item-field[0:1]": { + "width": 580 + } + } + }, { + "type": "table", + "values": [ + ["Submitting For:", "{intent}"] + ], + "style": { + "table-item-field[0:0]": { + "width": 160 + }, + "table-item-field[0:1]": { + "width": 218, + "alignment-horizontal": "right" + } + } + }] + }, { + "type": "rectangle", + "style": { + "bg-color": "#bc1015", + "width": 1108, + "height": 5, + "fill": true + } + }, { + "type": "table", + "use_alternate_color": true, + "values": [ + ["Version name:", "{version_name}"], + ["Date:", "{date}"], + ["Shot Types:", "{shot_type}"], + ["Submission Note:", "{submission_note}"] + ], + "style": { + "table-item": { + "padding-bottom": 20 + }, + "table-item-field[0:1]": { + "font-bold": true + }, + "table-item-field[3:0]": { + "word-wrap": true, + "ellide": true, + "max-lines": 4 + }, + "table-item-col[0]": { + "alignment-horizontal": "right", + "width": 150 + }, + "table-item-col[1]": { + "alignment-horizontal": "left", + "width": 958 + } + } + }] + }, { + "type": "layer", + "name": "RightSide", + "items": [{ + "type": "placeholder", + "name": "thumbnail", + "path": "{thumbnail_path}", + "style": { + "width": 730, + "height": 412 + } + }, { + "type": "placeholder", + "name": "colorbar", + "path": "{color_bar_path}", + "return_data": true, + "style": { + "width": 730, + "height": 55 + } + }, { + "type": "table", + "use_alternate_color": true, + "values": [ + ["Vendor:", "{vendor}"], + ["Shot Name:", "{shot_name}"], + ["Frames:", "{frame_start} - {frame_end} ({duration})"] + ], + "style": { + "table-item-col[0]": { + "alignment-horizontal": "left", + "width": 200 + }, + "table-item-col[1]": { + "alignment-horizontal": "right", + "width": 530, + "font-size": 30 + } + } + }] + }] + }] +} diff --git a/pype/configurations/defaults/presets/tools/sw_folders.json b/pype/configurations/defaults/presets/tools/sw_folders.json new file mode 100644 index 0000000000..a154935dce --- /dev/null +++ b/pype/configurations/defaults/presets/tools/sw_folders.json @@ -0,0 +1,8 @@ +{ + "compositing": ["nuke", "ae"], + "modeling": ["maya", "app2"], + "lookdev": ["substance"], + "animation": [], + "lighting": [], + "rigging": [] +} diff --git a/pype/configurations/defaults/presets/tools/workfiles.json b/pype/configurations/defaults/presets/tools/workfiles.json new file mode 100644 index 0000000000..393b2e3c10 --- /dev/null +++ b/pype/configurations/defaults/presets/tools/workfiles.json @@ -0,0 +1,7 @@ +{ + "last_workfile_on_startup": [ + { + "enabled": false + } + ] +} diff --git a/pype/configurations/defaults/presets/tray/menu_items.json b/pype/configurations/defaults/presets/tray/menu_items.json new file mode 100644 index 0000000000..6c6763848b --- /dev/null +++ b/pype/configurations/defaults/presets/tray/menu_items.json @@ -0,0 +1,28 @@ +{ + "item_usage": { + "User settings": false, + "Ftrack": true, + "Muster": false, + "Avalon": true, + "Clockify": false, + "Standalone Publish": true, + "Logging": true, + "Idle Manager": true, + "Timers Manager": true, + "Rest Api": true, + "Adobe Communicator": true + }, + "attributes": { + "Rest Api": { + "default_port": 8021, + "exclude_ports": [] + }, + "Timers Manager": { + "full_time": 15, + "message_time": 0.5 + }, + "Clockify": { + "workspace_name": null + } + } +} diff --git a/pype/configurations/defaults/presets/unreal/project_setup.json b/pype/configurations/defaults/presets/unreal/project_setup.json new file mode 100644 index 0000000000..8a4dffc526 --- /dev/null +++ b/pype/configurations/defaults/presets/unreal/project_setup.json @@ -0,0 +1,4 @@ +{ + "dev_mode": false, + "install_unreal_python_engine": false +} diff --git a/pype/configurations/defaults/project_configurations/plugins/celaction/publish.json b/pype/configurations/defaults/project_configurations/plugins/celaction/publish.json new file mode 100644 index 0000000000..fd1af23d84 --- /dev/null +++ b/pype/configurations/defaults/project_configurations/plugins/celaction/publish.json @@ -0,0 +1,11 @@ +{ + "ExtractCelactionDeadline": { + "enabled": true, + "deadline_department": "", + "deadline_priority": 50, + "deadline_pool": "", + "deadline_pool_secondary": "", + "deadline_group": "", + "deadline_chunk_size": 10 + } +} \ No newline at end of file diff --git a/pype/configurations/defaults/project_configurations/plugins/config.json b/pype/configurations/defaults/project_configurations/plugins/config.json new file mode 100644 index 0000000000..9e26dfeeb6 --- /dev/null +++ b/pype/configurations/defaults/project_configurations/plugins/config.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/pype/configurations/defaults/project_configurations/plugins/ftrack/publish.json b/pype/configurations/defaults/project_configurations/plugins/ftrack/publish.json new file mode 100644 index 0000000000..d8d93a36ee --- /dev/null +++ b/pype/configurations/defaults/project_configurations/plugins/ftrack/publish.json @@ -0,0 +1,7 @@ +{ + "IntegrateFtrackNote": { + "enabled": false, + "note_with_intent_template": "{intent}: {comment}", + "note_labels": [] + } +} \ No newline at end of file diff --git a/pype/configurations/defaults/project_configurations/plugins/global/create.json b/pype/configurations/defaults/project_configurations/plugins/global/create.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/pype/configurations/defaults/project_configurations/plugins/global/create.json @@ -0,0 +1 @@ +{} diff --git a/pype/configurations/defaults/project_configurations/plugins/global/filter.json b/pype/configurations/defaults/project_configurations/plugins/global/filter.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/pype/configurations/defaults/project_configurations/plugins/global/filter.json @@ -0,0 +1 @@ +{} diff --git a/pype/configurations/defaults/project_configurations/plugins/global/load.json b/pype/configurations/defaults/project_configurations/plugins/global/load.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/pype/configurations/defaults/project_configurations/plugins/global/load.json @@ -0,0 +1 @@ +{} diff --git a/pype/configurations/defaults/project_configurations/plugins/global/publish.json b/pype/configurations/defaults/project_configurations/plugins/global/publish.json new file mode 100644 index 0000000000..3c5de85e68 --- /dev/null +++ b/pype/configurations/defaults/project_configurations/plugins/global/publish.json @@ -0,0 +1,97 @@ +{ + "IntegrateMasterVersion": { + "enabled": false + }, + "ExtractJpegEXR": { + "ffmpeg_args": { + "input": [ + "-gamma 2.2" + ], + "output": [] + } + }, + "ExtractReview": { + "enabled": true, + "profiles": [ + { + "families": [], + "hosts": [], + "outputs": { + "h264": { + "filter": { + "families": [ + "render", + "review", + "ftrack" + ] + }, + "ext": "mp4", + "ffmpeg_args": { + "input": [ + "-gamma 2.2" + ], + "video_filters": [], + "audio_filters": [], + "output": [ + "-pix_fmt yuv420p", + "-crf 18", + "-intra" + ] + }, + "tags": [ + "burnin", + "ftrackreview" + ] + } + } + } + ] + }, + "ExtractBurnin": { + "enabled": false, + "options": { + "font_size": 42, + "opacity": 1, + "bg_opacity": 0, + "x_offset": 5, + "y_offset": 5, + "bg_padding": 5 + }, + "fields": {}, + "profiles": [ + { + "burnins": { + "burnin": { + "TOP_LEFT": "{yy}-{mm}-{dd}", + "TOP_RIGHT": "{anatomy[version]}", + "TOP_CENTERED": "", + "BOTTOM_RIGHT": "{frame_start}-{current_frame}-{frame_end}", + "BOTTOM_CENTERED": "{asset}", + "BOTTOM_LEFT": "{username}" + } + } + } + ] + }, + "IntegrateAssetNew": { + "template_name_profiles": { + "publish": { + "families": [], + "tasks": [] + }, + "render": { + "families": [ + "review", + "render", + "prerender" + ] + } + } + }, + "ProcessSubmittedJobOnFarm": { + "enabled": false, + "deadline_department": "", + "deadline_pool": "", + "deadline_group": "" + } +} \ No newline at end of file diff --git a/pype/configurations/defaults/project_configurations/plugins/maya/create.json b/pype/configurations/defaults/project_configurations/plugins/maya/create.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/pype/configurations/defaults/project_configurations/plugins/maya/create.json @@ -0,0 +1 @@ +{} diff --git a/pype/configurations/defaults/project_configurations/plugins/maya/filter.json b/pype/configurations/defaults/project_configurations/plugins/maya/filter.json new file mode 100644 index 0000000000..83d6f05f31 --- /dev/null +++ b/pype/configurations/defaults/project_configurations/plugins/maya/filter.json @@ -0,0 +1,9 @@ +{ + "Preset n1": { + "ValidateNoAnimation": false, + "ValidateShapeDefaultNames": false + }, + "Preset n2": { + "ValidateNoAnimation": false + } +} diff --git a/pype/configurations/defaults/project_configurations/plugins/maya/load.json b/pype/configurations/defaults/project_configurations/plugins/maya/load.json new file mode 100644 index 0000000000..260fbb35ee --- /dev/null +++ b/pype/configurations/defaults/project_configurations/plugins/maya/load.json @@ -0,0 +1,18 @@ +{ + "colors": { + "model": [0.821, 0.518, 0.117], + "rig": [0.144, 0.443, 0.463], + "pointcache": [0.368, 0.821, 0.117], + "animation": [0.368, 0.821, 0.117], + "ass": [1.0, 0.332, 0.312], + "camera": [0.447, 0.312, 1.0], + "fbx": [1.0, 0.931, 0.312], + "mayaAscii": [0.312, 1.0, 0.747], + "setdress": [0.312, 1.0, 0.747], + "layout": [0.312, 1.0, 0.747], + "vdbcache": [0.312, 1.0, 0.428], + "vrayproxy": [0.258, 0.95, 0.541], + "yeticache": [0.2, 0.8, 0.3], + "yetiRig": [0, 0.8, 0.5] + } +} diff --git a/pype/configurations/defaults/project_configurations/plugins/maya/publish.json b/pype/configurations/defaults/project_configurations/plugins/maya/publish.json new file mode 100644 index 0000000000..2b3637ff80 --- /dev/null +++ b/pype/configurations/defaults/project_configurations/plugins/maya/publish.json @@ -0,0 +1,17 @@ +{ + "ValidateModelName": { + "enabled": false, + "material_file": "/path/to/shader_name_definition.txt", + "regex": "(.*)_(\\d)*_(?P.*)_(GEO)" + }, + "ValidateAssemblyName": { + "enabled": false + }, + "ValidateShaderName": { + "enabled": false, + "regex": "(?P.*)_(.*)_SHD" + }, + "ValidateMeshHasOverlappingUVs": { + "enabled": false + } +} \ No newline at end of file diff --git a/pype/configurations/defaults/project_configurations/plugins/maya/workfile_build.json b/pype/configurations/defaults/project_configurations/plugins/maya/workfile_build.json new file mode 100644 index 0000000000..443bc2cb2c --- /dev/null +++ b/pype/configurations/defaults/project_configurations/plugins/maya/workfile_build.json @@ -0,0 +1,136 @@ +[ + { + "tasks": [ + "lighting" + ], + "current_context": [ + { + "subset_name_filters": [ + ".+[Mm]ain" + ], + "families": [ + "model" + ], + "repre_names": [ + "abc", + "ma" + ], + "loaders": [ + "ReferenceLoader" + ] + }, + { + "families": [ + "animation", + "pointcache" + ], + "repre_names": [ + "abc" + ], + "loaders": [ + "ReferenceLoader" + ] + }, + { + "families": [ + "rendersetup" + ], + "repre_names": [ + "json" + ], + "loaders": [ + "RenderSetupLoader" + ] + }, + { + "families": [ + "camera" + ], + "repre_names": [ + "abc" + ], + "loaders": [ + "ReferenceLoader" + ] + } + ], + "linked_assets": [ + { + "families": [ + "setdress" + ], + "repre_names": [ + "ma" + ], + "loaders": [ + "ReferenceLoader" + ] + }, + { + "families": [ + "ass" + ], + "repre_names": [ + "ass" + ], + "loaders": [ + "assLoader" + ] + } + ] + }, + { + "tasks": [ + "animation" + ], + "current_context": [ + { + "families": [ + "camera" + ], + "repre_names": [ + "abc", + "ma" + ], + "loaders": [ + "ReferenceLoader" + ] + }, + { + "families": [ + "audio" + ], + "repre_names": [ + "wav" + ], + "loaders": [ + "RenderSetupLoader" + ] + } + ], + "linked_assets": [ + { + "families": [ + "setdress" + ], + "repre_names": [ + "proxy" + ], + "loaders": [ + "ReferenceLoader" + ] + }, + { + "families": [ + "rig" + ], + "repre_names": [ + "ass" + ], + "loaders": [ + "rigLoader" + ] + } + ] + } +] \ No newline at end of file diff --git a/pype/configurations/defaults/project_configurations/plugins/nuke/create.json b/pype/configurations/defaults/project_configurations/plugins/nuke/create.json new file mode 100644 index 0000000000..79ab665696 --- /dev/null +++ b/pype/configurations/defaults/project_configurations/plugins/nuke/create.json @@ -0,0 +1,8 @@ +{ + "CreateWriteRender": { + "fpath_template": "{work}/renders/nuke/{subset}/{subset}.{frame}.{ext}" + }, + "CreateWritePrerender": { + "fpath_template": "{work}/prerenders/nuke/{subset}/{subset}.{frame}.{ext}" + } +} \ No newline at end of file diff --git a/pype/configurations/defaults/project_configurations/plugins/nuke/load.json b/pype/configurations/defaults/project_configurations/plugins/nuke/load.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/pype/configurations/defaults/project_configurations/plugins/nuke/load.json @@ -0,0 +1 @@ +{} diff --git a/pype/configurations/defaults/project_configurations/plugins/nuke/publish.json b/pype/configurations/defaults/project_configurations/plugins/nuke/publish.json new file mode 100644 index 0000000000..08a099a0a0 --- /dev/null +++ b/pype/configurations/defaults/project_configurations/plugins/nuke/publish.json @@ -0,0 +1,53 @@ +{ + "ExtractThumbnail": { + "enabled": true, + "nodes": { + "Reformat": [ + [ + "type", + "to format" + ], + [ + "format", + "HD_1080" + ], + [ + "filter", + "Lanczos6" + ], + [ + "black_outside", + true + ], + [ + "pbb", + false + ] + ] + } + }, + "ValidateNukeWriteKnobs": { + "enabled": false, + "knobs": { + "render": { + "review": true + } + } + }, + "ExtractReviewDataLut": { + "enabled": false + }, + "ExtractReviewDataMov": { + "enabled": true, + "viewer_lut_raw": false + }, + "ExtractSlateFrame": { + "viewer_lut_raw": false + }, + "NukeSubmitDeadline": { + "deadline_priority": 50, + "deadline_pool": "", + "deadline_pool_secondary": "", + "deadline_chunk_size": 1 + } +} \ No newline at end of file diff --git a/pype/configurations/defaults/project_configurations/plugins/nuke/workfile_build.json b/pype/configurations/defaults/project_configurations/plugins/nuke/workfile_build.json new file mode 100644 index 0000000000..4b48b46184 --- /dev/null +++ b/pype/configurations/defaults/project_configurations/plugins/nuke/workfile_build.json @@ -0,0 +1,23 @@ +[ + { + "tasks": [ + "compositing" + ], + "current_context": [ + { + "families": [ + "render", + "plate" + ], + "repre_names": [ + "exr", + "dpx" + ], + "loaders": [ + "LoadSequence" + ] + } + ], + "linked_assets": [] + } +] \ No newline at end of file diff --git a/pype/configurations/defaults/project_configurations/plugins/nukestudio/filter.json b/pype/configurations/defaults/project_configurations/plugins/nukestudio/filter.json new file mode 100644 index 0000000000..bd6a0dc1bd --- /dev/null +++ b/pype/configurations/defaults/project_configurations/plugins/nukestudio/filter.json @@ -0,0 +1,10 @@ +{ + "strict": { + "ValidateVersion": true, + "VersionUpWorkfile": true + }, + "benevolent": { + "ValidateVersion": false, + "VersionUpWorkfile": false + } +} \ No newline at end of file diff --git a/pype/configurations/defaults/project_configurations/plugins/nukestudio/publish.json b/pype/configurations/defaults/project_configurations/plugins/nukestudio/publish.json new file mode 100644 index 0000000000..d99a878c35 --- /dev/null +++ b/pype/configurations/defaults/project_configurations/plugins/nukestudio/publish.json @@ -0,0 +1,9 @@ +{ + "CollectInstanceVersion": { + "enabled": false + }, + "ExtractReviewCutUpVideo": { + "enabled": true, + "tags_addition": [] + } +} \ No newline at end of file diff --git a/pype/configurations/defaults/project_configurations/plugins/resolve/create.json b/pype/configurations/defaults/project_configurations/plugins/resolve/create.json new file mode 100644 index 0000000000..8ff5b15714 --- /dev/null +++ b/pype/configurations/defaults/project_configurations/plugins/resolve/create.json @@ -0,0 +1,7 @@ +{ + "CreateShotClip": { + "clipName": "{track}{sequence}{shot}", + "folder": "takes", + "steps": 20 + } +} \ No newline at end of file diff --git a/pype/configurations/defaults/project_configurations/plugins/standalonepublisher/publish.json b/pype/configurations/defaults/project_configurations/plugins/standalonepublisher/publish.json new file mode 100644 index 0000000000..2f1a3e7aca --- /dev/null +++ b/pype/configurations/defaults/project_configurations/plugins/standalonepublisher/publish.json @@ -0,0 +1,27 @@ +{ + "ExtractThumbnailSP": { + "ffmpeg_args": { + "input": [ + "-gamma 2.2" + ], + "output": [] + } + }, + "ExtractReviewSP": { + "outputs": { + "h264": { + "input": [ + "-gamma 2.2" + ], + "output": [ + "-pix_fmt yuv420p", + "-crf 18" + ], + "tags": [ + "preview" + ], + "ext": "mov" + } + } + } +} \ No newline at end of file diff --git a/pype/configurations/defaults/project_configurations/plugins/test/create.json b/pype/configurations/defaults/project_configurations/plugins/test/create.json new file mode 100644 index 0000000000..fa0b2fc05f --- /dev/null +++ b/pype/configurations/defaults/project_configurations/plugins/test/create.json @@ -0,0 +1,8 @@ +{ + "MyTestCreator": { + "my_test_property": "B", + "active": false, + "new_property": "new", + "family": "new_family" + } +} diff --git a/pype/configurations/defaults/project_configurations/plugins/test/publish.json b/pype/configurations/defaults/project_configurations/plugins/test/publish.json new file mode 100644 index 0000000000..3180dd5d8a --- /dev/null +++ b/pype/configurations/defaults/project_configurations/plugins/test/publish.json @@ -0,0 +1,10 @@ +{ + "MyTestPlugin": { + "label": "loaded from preset", + "optional": true, + "families": ["changed", "by", "preset"] + }, + "MyTestRemovedPlugin": { + "enabled": false + } +} diff --git a/pype/configurations/defaults/studio_configurations/global/applications.json b/pype/configurations/defaults/studio_configurations/global/applications.json new file mode 100644 index 0000000000..8e27f11002 --- /dev/null +++ b/pype/configurations/defaults/studio_configurations/global/applications.json @@ -0,0 +1,39 @@ +{ + "blender_2.80": true, + "blender_2.81": true, + "blender_2.82": true, + "blender_2.83": true, + "celaction_local": true, + "celaction_remote": true, + "harmony_17": true, + "houdini_16": true, + "houdini_17": true, + "houdini_18": true, + "maya_2016": true, + "maya_2017": true, + "maya_2018": true, + "maya_2019": true, + "maya_2020": true, + "nukestudio_10.0": true, + "nukestudio_11.0": true, + "nukestudio_11.2": true, + "nukestudio_11.3": true, + "nukestudio_12.0": true, + "nukex_10.0": true, + "nukex_11.0": true, + "nukex_11.2": true, + "nukex_11.3": true, + "nukex_12.0": true, + "nuke_10.0": true, + "nuke_11.0": true, + "nuke_11.2": true, + "nuke_11.3": true, + "nuke_12.0": true, + "photoshop_2020": true, + "premiere_2019": true, + "premiere_2020": true, + "resolve_16": true, + "storyboardpro_7": true, + "unreal_4.24": true, + "houdini_16.5": false +} \ No newline at end of file diff --git a/pype/configurations/defaults/studio_configurations/global/intent.json b/pype/configurations/defaults/studio_configurations/global/intent.json new file mode 100644 index 0000000000..844bd1b518 --- /dev/null +++ b/pype/configurations/defaults/studio_configurations/global/intent.json @@ -0,0 +1,8 @@ +{ + "items": { + "wip": "WIP", + "test": "TEST", + "final": "FINAL" + }, + "default": "wip" +} \ No newline at end of file diff --git a/pype/configurations/defaults/studio_configurations/global/tools.json b/pype/configurations/defaults/studio_configurations/global/tools.json new file mode 100644 index 0000000000..93895c0e81 --- /dev/null +++ b/pype/configurations/defaults/studio_configurations/global/tools.json @@ -0,0 +1,6 @@ +{ + "mtoa_3.0.1": true, + "mtoa_3.1.1": true, + "mtoa_3.2.0": true, + "yeti_2.1.2": true +} \ No newline at end of file diff --git a/pype/configurations/defaults/studio_configurations/global/tray_modules.json b/pype/configurations/defaults/studio_configurations/global/tray_modules.json new file mode 100644 index 0000000000..0ff5b15552 --- /dev/null +++ b/pype/configurations/defaults/studio_configurations/global/tray_modules.json @@ -0,0 +1,28 @@ +{ + "item_usage": { + "User settings": false, + "Ftrack": true, + "Muster": false, + "Avalon": true, + "Clockify": false, + "Standalone Publish": true, + "Logging": true, + "Idle Manager": true, + "Timers Manager": true, + "Rest Api": true, + "Adobe Communicator": true + }, + "attributes": { + "Rest Api": { + "default_port": 8021, + "exclude_ports": [] + }, + "Timers Manager": { + "full_time": 15.0, + "message_time": 0.5 + }, + "Clockify": { + "workspace_name": "" + } + } +} \ No newline at end of file diff --git a/pype/configurations/defaults/studio_configurations/muster/templates_mapping.json b/pype/configurations/defaults/studio_configurations/muster/templates_mapping.json new file mode 100644 index 0000000000..0c09113515 --- /dev/null +++ b/pype/configurations/defaults/studio_configurations/muster/templates_mapping.json @@ -0,0 +1,19 @@ +{ + "3delight": 41, + "arnold": 46, + "arnold_sf": 57, + "gelato": 30, + "harware": 3, + "krakatoa": 51, + "file_layers": 7, + "mentalray": 2, + "mentalray_sf": 6, + "redshift": 55, + "renderman": 29, + "software": 1, + "software_sf": 5, + "turtle": 10, + "vector": 4, + "vray": 37, + "ffmpeg": 48 +} \ No newline at end of file diff --git a/pype/configurations/defaults/studio_configurations/standalone_publish/families.json b/pype/configurations/defaults/studio_configurations/standalone_publish/families.json new file mode 100644 index 0000000000..d05941cc26 --- /dev/null +++ b/pype/configurations/defaults/studio_configurations/standalone_publish/families.json @@ -0,0 +1,90 @@ +{ + "create_look": { + "name": "look", + "label": "Look", + "family": "look", + "icon": "paint-brush", + "defaults": ["Main"], + "help": "Shader connections defining shape look" + }, + "create_model": { + "name": "model", + "label": "Model", + "family": "model", + "icon": "cube", + "defaults": ["Main", "Proxy", "Sculpt"], + "help": "Polygonal static geometry" + }, + "create_workfile": { + "name": "workfile", + "label": "Workfile", + "family": "workfile", + "icon": "cube", + "defaults": ["Main"], + "help": "Working scene backup" + }, + "create_camera": { + "name": "camera", + "label": "Camera", + "family": "camera", + "icon": "video-camera", + "defaults": ["Main"], + "help": "Single baked camera" + }, + "create_pointcache": { + "name": "pointcache", + "label": "Pointcache", + "family": "pointcache", + "icon": "gears", + "defaults": ["Main"], + "help": "Alembic pointcache for animated data" + }, + "create_rig": { + "name": "rig", + "label": "Rig", + "family": "rig", + "icon": "wheelchair", + "defaults": ["Main"], + "help": "Artist-friendly rig with controls" + }, + "create_layout": { + "name": "layout", + "label": "Layout", + "family": "layout", + "icon": "cubes", + "defaults": ["Main"], + "help": "Simple scene for animators with camera" + }, + "create_plate": { + "name": "plate", + "label": "Plate", + "family": "plate", + "icon": "camera", + "defaults": ["Main", "BG", "Reference"], + "help": "Plates for compositors" + }, + "create_matchmove": { + "name": "matchmove", + "label": "Matchmove script", + "family": "matchmove", + "icon": "empire", + "defaults": ["Camera", "Object", "Mocap"], + "help": "Script exported from matchmoving application" + }, + "create_images": { + "name": "image", + "label": "Image file", + "family": "image", + "icon": "image", + "defaults": ["ConceptArt", "Reference", "Texture", "MattePaint"], + "help": "Holder for all kinds of image data" + }, + "create_editorial": { + "name": "editorial", + "label": "Editorial", + "family": "editorial", + "icon": "image", + "defaults": ["Main"], + "help": "Editorial files to generate shots." + } +} From 3e6cf0cdb8c329de03ef92b58cb6f467a75a8ec0 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 9 Sep 2020 10:34:14 +0200 Subject: [PATCH 467/813] basic reorganization of presets --- .../configurations/defaults/presets/init.json | 4 - .../presets/plugins/celaction/publish.json | 10 --- .../defaults/presets/plugins/config.json | 1 - .../presets/plugins/ftrack/publish.json | 6 -- .../presets/plugins/global/create.json | 1 - .../presets/plugins/global/filter.json | 1 - .../defaults/presets/plugins/global/load.json | 1 - .../presets/plugins/global/publish.json | 86 ------------------- .../defaults/presets/plugins/maya/create.json | 1 - .../defaults/presets/plugins/maya/filter.json | 9 -- .../defaults/presets/plugins/maya/load.json | 18 ---- .../presets/plugins/maya/publish.json | 17 ---- .../presets/plugins/maya/workfile_build.json | 54 ------------ .../defaults/presets/plugins/nuke/create.json | 8 -- .../defaults/presets/plugins/nuke/load.json | 1 - .../presets/plugins/nuke/publish.json | 48 ----------- .../presets/plugins/nuke/workfile_build.json | 11 --- .../presets/plugins/nukestudio/filter.json | 10 --- .../presets/plugins/nukestudio/publish.json | 8 -- .../presets/plugins/resolve/create.json | 7 -- .../plugins/standalonepublisher/publish.json | 25 ------ .../defaults/presets/plugins/test/create.json | 8 -- .../presets/plugins/test/publish.json | 10 --- .../defaults/presets/tools/pyblish.json | 17 ---- .../defaults/presets/tray/menu_items.json | 28 ------ .../anatomy/README.md | 0 .../anatomy/colorspace.json} | 0 .../anatomy}/colorspace/aces103-cg.json | 0 .../anatomy/dataflow.json} | 0 .../anatomy}/dataflow/aces-exr.json | 0 .../anatomy/default.yaml | 0 .../anatomy/roots.json | 0 .../anatomy/templates.json | 35 ++++++++ .../{plugins => }/celaction/publish.json | 0 .../{plugins => }/config.json | 0 .../ftrack/ftrack_config.json | 0 .../ftrack/ftrack_custom_attributes.json | 0 .../ftrack/partnership_ftrack_cred.json | 0 .../ftrack/plugins/server.json | 0 .../ftrack/plugins/user.json | 0 .../ftrack/project_defaults.json | 0 .../{plugins => }/ftrack/publish.json | 0 .../{plugins/maya => global}/create.json | 0 .../global}/creator.json | 0 .../{plugins => }/global/filter.json | 0 .../{plugins/nuke => global}/load.json | 0 .../global}/project_folder_structure.json | 0 .../{plugins => }/global/publish.json | 0 .../global}/sw_folders.json | 0 .../global}/workfiles.json | 0 .../maya/capture.json | 0 .../{plugins/global => maya}/create.json | 0 .../{plugins => }/maya/filter.json | 0 .../{plugins => }/maya/load.json | 0 .../{plugins => }/maya/publish.json | 0 .../{plugins => }/maya/workfile_build.json | 0 .../muster/templates_mapping.json | 0 .../{plugins => }/nuke/create.json | 0 .../{plugins/global => nuke}/load.json | 0 .../{plugins => }/nuke/publish.json | 0 .../{plugins => }/nuke/workfile_build.json | 0 .../{plugins => }/nukestudio/filter.json | 0 .../{plugins => }/nukestudio/publish.json | 0 .../nukestudio/tags.json | 0 .../premiere/asset_default.json | 0 .../premiere/rules_tasks.json | 0 .../{plugins => }/resolve/create.json | 0 .../standalonepublisher}/families.json | 0 .../standalonepublisher/publish.json | 0 .../{plugins => }/test/create.json | 0 .../{plugins => }/test/publish.json | 0 .../tools/slates/example_HD.json | 0 .../unreal/project_setup.json | 0 .../environments/avalon.json | 0 .../environments/blender.json | 0 .../environments/celaction.json | 0 .../environments/deadline.json | 0 .../environments/ftrack.json | 0 .../environments/global.json | 0 .../environments/harmony.json | 0 .../environments/houdini.json | 0 .../environments/maya.json | 0 .../environments/maya_2018.json | 0 .../environments/maya_2020.json | 0 .../environments/mayabatch.json | 0 .../environments/mayabatch_2019.json | 0 .../environments/mtoa_3.1.1.json | 0 .../environments/muster.json | 0 .../environments/nuke.json | 0 .../environments/nukestudio.json | 0 .../environments/nukestudio_10.0.json | 0 .../environments/nukex.json | 0 .../environments/nukex_10.0.json | 0 .../environments/photoshop.json | 0 .../environments/premiere.json | 0 .../environments/resolve.json | 0 .../environments/storyboardpro.json | 0 .../environments/unreal_4.24.json | 0 .../environments/vray_4300.json | 0 .../launchers/blender_2.80.toml | 0 .../launchers/blender_2.81.toml | 0 .../launchers/blender_2.82.toml | 0 .../launchers/blender_2.83.toml | 0 .../launchers/celaction_local.toml | 0 .../launchers/celaction_publish.toml | 0 .../launchers/darwin/blender_2.82 | 0 .../launchers/darwin/harmony_17 | 0 .../launchers/darwin/harmony_17_launch | 0 .../launchers/darwin/python3 | 0 .../launchers/harmony_17.toml | 0 .../launchers/houdini_16.toml | 0 .../launchers/houdini_17.toml | 0 .../launchers/houdini_18.toml | 0 .../launchers/linux/maya2016 | 0 .../launchers/linux/maya2017 | 0 .../launchers/linux/maya2018 | 0 .../launchers/linux/maya2019 | 0 .../launchers/linux/maya2020 | 0 .../launchers/linux/nuke11.3 | 0 .../launchers/linux/nuke12.0 | 0 .../launchers/linux/nukestudio11.3 | 0 .../launchers/linux/nukestudio12.0 | 0 .../launchers/linux/nukex11.3 | 0 .../launchers/linux/nukex12.0 | 0 .../launchers/maya_2016.toml | 0 .../launchers/maya_2017.toml | 0 .../launchers/maya_2018.toml | 0 .../launchers/maya_2019.toml | 0 .../launchers/maya_2020.toml | 0 .../launchers/mayabatch_2019.toml | 0 .../launchers/mayabatch_2020.toml | 0 .../launchers/mayapy2016.toml | 0 .../launchers/mayapy2017.toml | 0 .../launchers/mayapy2018.toml | 0 .../launchers/mayapy2019.toml | 0 .../launchers/mayapy2020.toml | 0 .../launchers/myapp.toml | 0 .../launchers/nuke_10.0.toml | 0 .../launchers/nuke_11.0.toml | 0 .../launchers/nuke_11.2.toml | 0 .../launchers/nuke_11.3.toml | 0 .../launchers/nuke_12.0.toml | 0 .../launchers/nukestudio_10.0.toml | 0 .../launchers/nukestudio_11.0.toml | 0 .../launchers/nukestudio_11.2.toml | 0 .../launchers/nukestudio_11.3.toml | 0 .../launchers/nukestudio_12.0.toml | 0 .../launchers/nukex_10.0.toml | 0 .../launchers/nukex_11.0.toml | 0 .../launchers/nukex_11.2.toml | 0 .../launchers/nukex_11.3.toml | 0 .../launchers/nukex_12.0.toml | 0 .../launchers/photoshop_2020.toml | 0 .../launchers/premiere_2019.toml | 0 .../launchers/premiere_2020.toml | 0 .../launchers/python_2.toml | 0 .../launchers/python_3.toml | 0 .../launchers/resolve_16.toml | 0 .../launchers/shell.toml | 0 .../launchers/storyboardpro_7.toml | 0 .../launchers/unreal_4.24.toml | 0 .../launchers/windows/blender_2.80.bat | 0 .../launchers/windows/blender_2.81.bat | 0 .../launchers/windows/blender_2.82.bat | 0 .../launchers/windows/blender_2.83.bat | 0 .../launchers/windows/celaction_local.bat | 0 .../launchers/windows/celaction_publish.bat | 0 .../launchers/windows/harmony_17.bat | 0 .../launchers/windows/houdini_16.bat | 0 .../launchers/windows/houdini_17.bat | 0 .../launchers/windows/houdini_18.bat | 0 .../launchers/windows/maya2016.bat | 0 .../launchers/windows/maya2017.bat | 0 .../launchers/windows/maya2018.bat | 0 .../launchers/windows/maya2019.bat | 0 .../launchers/windows/maya2020.bat | 0 .../launchers/windows/mayabatch2019.bat | 0 .../launchers/windows/mayabatch2020.bat | 0 .../launchers/windows/mayapy2016.bat | 0 .../launchers/windows/mayapy2017.bat | 0 .../launchers/windows/mayapy2018.bat | 0 .../launchers/windows/mayapy2019.bat | 0 .../launchers/windows/mayapy2020.bat | 0 .../launchers/windows/nuke10.0.bat | 0 .../launchers/windows/nuke11.0.bat | 0 .../launchers/windows/nuke11.2.bat | 0 .../launchers/windows/nuke11.3.bat | 0 .../launchers/windows/nuke12.0.bat | 0 .../launchers/windows/nukestudio10.0.bat | 0 .../launchers/windows/nukestudio11.0.bat | 0 .../launchers/windows/nukestudio11.2.bat | 0 .../launchers/windows/nukestudio11.3.bat | 0 .../launchers/windows/nukestudio12.0.bat | 0 .../launchers/windows/nukex10.0.bat | 0 .../launchers/windows/nukex11.0.bat | 0 .../launchers/windows/nukex11.2.bat | 0 .../launchers/windows/nukex11.3.bat | 0 .../launchers/windows/nukex12.0.bat | 0 .../launchers/windows/photoshop_2020.bat | 0 .../launchers/windows/premiere_pro_2019.bat | 0 .../launchers/windows/premiere_pro_2020.bat | 0 .../launchers/windows/python3.bat | 0 .../launchers/windows/resolve_16.bat | 0 .../launchers/windows/shell.bat | 0 .../launchers/windows/storyboardpro_7.bat | 0 .../launchers/windows/unreal.bat | 0 206 files changed, 35 insertions(+), 390 deletions(-) delete mode 100644 pype/configurations/defaults/presets/init.json delete mode 100644 pype/configurations/defaults/presets/plugins/celaction/publish.json delete mode 100644 pype/configurations/defaults/presets/plugins/config.json delete mode 100644 pype/configurations/defaults/presets/plugins/ftrack/publish.json delete mode 100644 pype/configurations/defaults/presets/plugins/global/create.json delete mode 100644 pype/configurations/defaults/presets/plugins/global/filter.json delete mode 100644 pype/configurations/defaults/presets/plugins/global/load.json delete mode 100644 pype/configurations/defaults/presets/plugins/global/publish.json delete mode 100644 pype/configurations/defaults/presets/plugins/maya/create.json delete mode 100644 pype/configurations/defaults/presets/plugins/maya/filter.json delete mode 100644 pype/configurations/defaults/presets/plugins/maya/load.json delete mode 100644 pype/configurations/defaults/presets/plugins/maya/publish.json delete mode 100644 pype/configurations/defaults/presets/plugins/maya/workfile_build.json delete mode 100644 pype/configurations/defaults/presets/plugins/nuke/create.json delete mode 100644 pype/configurations/defaults/presets/plugins/nuke/load.json delete mode 100644 pype/configurations/defaults/presets/plugins/nuke/publish.json delete mode 100644 pype/configurations/defaults/presets/plugins/nuke/workfile_build.json delete mode 100644 pype/configurations/defaults/presets/plugins/nukestudio/filter.json delete mode 100644 pype/configurations/defaults/presets/plugins/nukestudio/publish.json delete mode 100644 pype/configurations/defaults/presets/plugins/resolve/create.json delete mode 100644 pype/configurations/defaults/presets/plugins/standalonepublisher/publish.json delete mode 100644 pype/configurations/defaults/presets/plugins/test/create.json delete mode 100644 pype/configurations/defaults/presets/plugins/test/publish.json delete mode 100644 pype/configurations/defaults/presets/tools/pyblish.json delete mode 100644 pype/configurations/defaults/presets/tray/menu_items.json rename pype/configurations/defaults/{ => project_configurations}/anatomy/README.md (100%) rename pype/configurations/defaults/{presets/colorspace/default.json => project_configurations/anatomy/colorspace.json} (100%) rename pype/configurations/defaults/{presets => project_configurations/anatomy}/colorspace/aces103-cg.json (100%) rename pype/configurations/defaults/{presets/dataflow/default.json => project_configurations/anatomy/dataflow.json} (100%) rename pype/configurations/defaults/{presets => project_configurations/anatomy}/dataflow/aces-exr.json (100%) rename pype/configurations/defaults/{ => project_configurations}/anatomy/default.yaml (100%) rename pype/configurations/defaults/{ => project_configurations}/anatomy/roots.json (100%) create mode 100644 pype/configurations/defaults/project_configurations/anatomy/templates.json rename pype/configurations/defaults/project_configurations/{plugins => }/celaction/publish.json (100%) rename pype/configurations/defaults/project_configurations/{plugins => }/config.json (100%) rename pype/configurations/defaults/{presets => project_configurations}/ftrack/ftrack_config.json (100%) rename pype/configurations/defaults/{presets => project_configurations}/ftrack/ftrack_custom_attributes.json (100%) rename pype/configurations/defaults/{presets => project_configurations}/ftrack/partnership_ftrack_cred.json (100%) rename pype/configurations/defaults/{presets => project_configurations}/ftrack/plugins/server.json (100%) rename pype/configurations/defaults/{presets => project_configurations}/ftrack/plugins/user.json (100%) rename pype/configurations/defaults/{presets => project_configurations}/ftrack/project_defaults.json (100%) rename pype/configurations/defaults/project_configurations/{plugins => }/ftrack/publish.json (100%) rename pype/configurations/defaults/project_configurations/{plugins/maya => global}/create.json (100%) rename pype/configurations/defaults/{presets/tools => project_configurations/global}/creator.json (100%) rename pype/configurations/defaults/project_configurations/{plugins => }/global/filter.json (100%) rename pype/configurations/defaults/project_configurations/{plugins/nuke => global}/load.json (100%) rename pype/configurations/defaults/{presets/tools => project_configurations/global}/project_folder_structure.json (100%) rename pype/configurations/defaults/project_configurations/{plugins => }/global/publish.json (100%) rename pype/configurations/defaults/{presets/tools => project_configurations/global}/sw_folders.json (100%) rename pype/configurations/defaults/{presets/tools => project_configurations/global}/workfiles.json (100%) rename pype/configurations/defaults/{presets => project_configurations}/maya/capture.json (100%) rename pype/configurations/defaults/project_configurations/{plugins/global => maya}/create.json (100%) rename pype/configurations/defaults/project_configurations/{plugins => }/maya/filter.json (100%) rename pype/configurations/defaults/project_configurations/{plugins => }/maya/load.json (100%) rename pype/configurations/defaults/project_configurations/{plugins => }/maya/publish.json (100%) rename pype/configurations/defaults/project_configurations/{plugins => }/maya/workfile_build.json (100%) rename pype/configurations/defaults/{presets => project_configurations}/muster/templates_mapping.json (100%) rename pype/configurations/defaults/project_configurations/{plugins => }/nuke/create.json (100%) rename pype/configurations/defaults/project_configurations/{plugins/global => nuke}/load.json (100%) rename pype/configurations/defaults/project_configurations/{plugins => }/nuke/publish.json (100%) rename pype/configurations/defaults/project_configurations/{plugins => }/nuke/workfile_build.json (100%) rename pype/configurations/defaults/project_configurations/{plugins => }/nukestudio/filter.json (100%) rename pype/configurations/defaults/project_configurations/{plugins => }/nukestudio/publish.json (100%) rename pype/configurations/defaults/{presets => project_configurations}/nukestudio/tags.json (100%) rename pype/configurations/defaults/{presets => project_configurations}/premiere/asset_default.json (100%) rename pype/configurations/defaults/{presets => project_configurations}/premiere/rules_tasks.json (100%) rename pype/configurations/defaults/project_configurations/{plugins => }/resolve/create.json (100%) rename pype/configurations/defaults/{presets/standalone_publish => project_configurations/standalonepublisher}/families.json (100%) rename pype/configurations/defaults/project_configurations/{plugins => }/standalonepublisher/publish.json (100%) rename pype/configurations/defaults/project_configurations/{plugins => }/test/create.json (100%) rename pype/configurations/defaults/project_configurations/{plugins => }/test/publish.json (100%) rename pype/configurations/defaults/{presets => project_configurations}/tools/slates/example_HD.json (100%) rename pype/configurations/defaults/{presets => project_configurations}/unreal/project_setup.json (100%) rename pype/configurations/defaults/{ => studio_configurations}/environments/avalon.json (100%) rename pype/configurations/defaults/{ => studio_configurations}/environments/blender.json (100%) rename pype/configurations/defaults/{ => studio_configurations}/environments/celaction.json (100%) rename pype/configurations/defaults/{ => studio_configurations}/environments/deadline.json (100%) rename pype/configurations/defaults/{ => studio_configurations}/environments/ftrack.json (100%) rename pype/configurations/defaults/{ => studio_configurations}/environments/global.json (100%) rename pype/configurations/defaults/{ => studio_configurations}/environments/harmony.json (100%) rename pype/configurations/defaults/{ => studio_configurations}/environments/houdini.json (100%) rename pype/configurations/defaults/{ => studio_configurations}/environments/maya.json (100%) rename pype/configurations/defaults/{ => studio_configurations}/environments/maya_2018.json (100%) rename pype/configurations/defaults/{ => studio_configurations}/environments/maya_2020.json (100%) rename pype/configurations/defaults/{ => studio_configurations}/environments/mayabatch.json (100%) rename pype/configurations/defaults/{ => studio_configurations}/environments/mayabatch_2019.json (100%) rename pype/configurations/defaults/{ => studio_configurations}/environments/mtoa_3.1.1.json (100%) rename pype/configurations/defaults/{ => studio_configurations}/environments/muster.json (100%) rename pype/configurations/defaults/{ => studio_configurations}/environments/nuke.json (100%) rename pype/configurations/defaults/{ => studio_configurations}/environments/nukestudio.json (100%) rename pype/configurations/defaults/{ => studio_configurations}/environments/nukestudio_10.0.json (100%) rename pype/configurations/defaults/{ => studio_configurations}/environments/nukex.json (100%) rename pype/configurations/defaults/{ => studio_configurations}/environments/nukex_10.0.json (100%) rename pype/configurations/defaults/{ => studio_configurations}/environments/photoshop.json (100%) rename pype/configurations/defaults/{ => studio_configurations}/environments/premiere.json (100%) rename pype/configurations/defaults/{ => studio_configurations}/environments/resolve.json (100%) rename pype/configurations/defaults/{ => studio_configurations}/environments/storyboardpro.json (100%) rename pype/configurations/defaults/{ => studio_configurations}/environments/unreal_4.24.json (100%) rename pype/configurations/defaults/{ => studio_configurations}/environments/vray_4300.json (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/blender_2.80.toml (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/blender_2.81.toml (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/blender_2.82.toml (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/blender_2.83.toml (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/celaction_local.toml (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/celaction_publish.toml (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/darwin/blender_2.82 (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/darwin/harmony_17 (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/darwin/harmony_17_launch (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/darwin/python3 (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/harmony_17.toml (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/houdini_16.toml (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/houdini_17.toml (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/houdini_18.toml (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/linux/maya2016 (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/linux/maya2017 (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/linux/maya2018 (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/linux/maya2019 (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/linux/maya2020 (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/linux/nuke11.3 (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/linux/nuke12.0 (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/linux/nukestudio11.3 (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/linux/nukestudio12.0 (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/linux/nukex11.3 (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/linux/nukex12.0 (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/maya_2016.toml (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/maya_2017.toml (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/maya_2018.toml (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/maya_2019.toml (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/maya_2020.toml (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/mayabatch_2019.toml (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/mayabatch_2020.toml (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/mayapy2016.toml (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/mayapy2017.toml (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/mayapy2018.toml (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/mayapy2019.toml (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/mayapy2020.toml (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/myapp.toml (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/nuke_10.0.toml (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/nuke_11.0.toml (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/nuke_11.2.toml (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/nuke_11.3.toml (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/nuke_12.0.toml (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/nukestudio_10.0.toml (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/nukestudio_11.0.toml (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/nukestudio_11.2.toml (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/nukestudio_11.3.toml (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/nukestudio_12.0.toml (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/nukex_10.0.toml (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/nukex_11.0.toml (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/nukex_11.2.toml (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/nukex_11.3.toml (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/nukex_12.0.toml (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/photoshop_2020.toml (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/premiere_2019.toml (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/premiere_2020.toml (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/python_2.toml (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/python_3.toml (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/resolve_16.toml (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/shell.toml (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/storyboardpro_7.toml (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/unreal_4.24.toml (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/windows/blender_2.80.bat (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/windows/blender_2.81.bat (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/windows/blender_2.82.bat (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/windows/blender_2.83.bat (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/windows/celaction_local.bat (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/windows/celaction_publish.bat (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/windows/harmony_17.bat (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/windows/houdini_16.bat (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/windows/houdini_17.bat (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/windows/houdini_18.bat (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/windows/maya2016.bat (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/windows/maya2017.bat (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/windows/maya2018.bat (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/windows/maya2019.bat (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/windows/maya2020.bat (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/windows/mayabatch2019.bat (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/windows/mayabatch2020.bat (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/windows/mayapy2016.bat (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/windows/mayapy2017.bat (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/windows/mayapy2018.bat (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/windows/mayapy2019.bat (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/windows/mayapy2020.bat (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/windows/nuke10.0.bat (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/windows/nuke11.0.bat (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/windows/nuke11.2.bat (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/windows/nuke11.3.bat (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/windows/nuke12.0.bat (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/windows/nukestudio10.0.bat (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/windows/nukestudio11.0.bat (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/windows/nukestudio11.2.bat (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/windows/nukestudio11.3.bat (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/windows/nukestudio12.0.bat (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/windows/nukex10.0.bat (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/windows/nukex11.0.bat (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/windows/nukex11.2.bat (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/windows/nukex11.3.bat (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/windows/nukex12.0.bat (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/windows/photoshop_2020.bat (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/windows/premiere_pro_2019.bat (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/windows/premiere_pro_2020.bat (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/windows/python3.bat (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/windows/resolve_16.bat (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/windows/shell.bat (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/windows/storyboardpro_7.bat (100%) rename pype/configurations/defaults/{ => studio_configurations}/launchers/windows/unreal.bat (100%) diff --git a/pype/configurations/defaults/presets/init.json b/pype/configurations/defaults/presets/init.json deleted file mode 100644 index 361ee7445b..0000000000 --- a/pype/configurations/defaults/presets/init.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "colorspace": "default", - "dataflow": "default" -} diff --git a/pype/configurations/defaults/presets/plugins/celaction/publish.json b/pype/configurations/defaults/presets/plugins/celaction/publish.json deleted file mode 100644 index e791f574d9..0000000000 --- a/pype/configurations/defaults/presets/plugins/celaction/publish.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "ExtractCelactionDeadline": { - "deadline_department": "", - "deadline_priority": 50, - "deadline_pool": "", - "deadline_pool_secondary": "", - "deadline_group": "", - "deadline_chunk_size": 10 - } -} diff --git a/pype/configurations/defaults/presets/plugins/config.json b/pype/configurations/defaults/presets/plugins/config.json deleted file mode 100644 index 9e26dfeeb6..0000000000 --- a/pype/configurations/defaults/presets/plugins/config.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/pype/configurations/defaults/presets/plugins/ftrack/publish.json b/pype/configurations/defaults/presets/plugins/ftrack/publish.json deleted file mode 100644 index d0469ae4f7..0000000000 --- a/pype/configurations/defaults/presets/plugins/ftrack/publish.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "IntegrateFtrackNote": { - "note_with_intent_template": "{intent}: {comment}", - "note_labels": [] - } -} diff --git a/pype/configurations/defaults/presets/plugins/global/create.json b/pype/configurations/defaults/presets/plugins/global/create.json deleted file mode 100644 index 0967ef424b..0000000000 --- a/pype/configurations/defaults/presets/plugins/global/create.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/pype/configurations/defaults/presets/plugins/global/filter.json b/pype/configurations/defaults/presets/plugins/global/filter.json deleted file mode 100644 index 0967ef424b..0000000000 --- a/pype/configurations/defaults/presets/plugins/global/filter.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/pype/configurations/defaults/presets/plugins/global/load.json b/pype/configurations/defaults/presets/plugins/global/load.json deleted file mode 100644 index 0967ef424b..0000000000 --- a/pype/configurations/defaults/presets/plugins/global/load.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/pype/configurations/defaults/presets/plugins/global/publish.json b/pype/configurations/defaults/presets/plugins/global/publish.json deleted file mode 100644 index 016868fc92..0000000000 --- a/pype/configurations/defaults/presets/plugins/global/publish.json +++ /dev/null @@ -1,86 +0,0 @@ -{ - "IntegrateMasterVersion": { - "enabled": false - }, - "ExtractJpegEXR": { - "ffmpeg_args": { - "input": [ - "-gamma 2.2" - ], - "output": [] - } - }, - "ExtractReview": { - "__documentation__": "http://pype.club/docs/admin_presets_plugins", - "profiles": [ - { - "families": [], - "hosts": [], - "outputs": { - "h264": { - "filter": { - "families": ["render", "review", "ftrack"] - }, - "ext": "mp4", - "ffmpeg_args": { - "input": [ - "-gamma 2.2" - ], - "video_filters": [], - "audio_filters": [], - "output": [ - "-pix_fmt yuv420p", - "-crf 18", - "-intra" - ] - }, - "tags": ["burnin", "ftrackreview"] - } - } - } - ] - }, - "ExtractBurnin": { - "options": { - "opacity": 1, - "x_offset": 5, - "y_offset": 5, - "bg_padding": 5, - "bg_opacity": 0.5, - "font_size": 42 - }, - "fields": { - - }, - "profiles": [ - { - "burnins": { - "burnin": { - "TOP_LEFT": "{yy}-{mm}-{dd}", - "TOP_RIGHT": "{anatomy[version]}", - "TOP_CENTERED": "", - "BOTTOM_RIGHT": "{frame_start}-{current_frame}-{frame_end}", - "BOTTOM_CENTERED": "{asset}", - "BOTTOM_LEFT": "{username}" - } - } - } - ] - }, - "IntegrateAssetNew": { - "template_name_profiles": { - "publish": { - "families": [], - "tasks": [] - }, - "render": { - "families": ["review", "render", "prerender"] - } - } - }, - "ProcessSubmittedJobOnFarm": { - "deadline_department": "", - "deadline_pool": "", - "deadline_group": "" - } -} diff --git a/pype/configurations/defaults/presets/plugins/maya/create.json b/pype/configurations/defaults/presets/plugins/maya/create.json deleted file mode 100644 index 0967ef424b..0000000000 --- a/pype/configurations/defaults/presets/plugins/maya/create.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/pype/configurations/defaults/presets/plugins/maya/filter.json b/pype/configurations/defaults/presets/plugins/maya/filter.json deleted file mode 100644 index 83d6f05f31..0000000000 --- a/pype/configurations/defaults/presets/plugins/maya/filter.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "Preset n1": { - "ValidateNoAnimation": false, - "ValidateShapeDefaultNames": false - }, - "Preset n2": { - "ValidateNoAnimation": false - } -} diff --git a/pype/configurations/defaults/presets/plugins/maya/load.json b/pype/configurations/defaults/presets/plugins/maya/load.json deleted file mode 100644 index 260fbb35ee..0000000000 --- a/pype/configurations/defaults/presets/plugins/maya/load.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "colors": { - "model": [0.821, 0.518, 0.117], - "rig": [0.144, 0.443, 0.463], - "pointcache": [0.368, 0.821, 0.117], - "animation": [0.368, 0.821, 0.117], - "ass": [1.0, 0.332, 0.312], - "camera": [0.447, 0.312, 1.0], - "fbx": [1.0, 0.931, 0.312], - "mayaAscii": [0.312, 1.0, 0.747], - "setdress": [0.312, 1.0, 0.747], - "layout": [0.312, 1.0, 0.747], - "vdbcache": [0.312, 1.0, 0.428], - "vrayproxy": [0.258, 0.95, 0.541], - "yeticache": [0.2, 0.8, 0.3], - "yetiRig": [0, 0.8, 0.5] - } -} diff --git a/pype/configurations/defaults/presets/plugins/maya/publish.json b/pype/configurations/defaults/presets/plugins/maya/publish.json deleted file mode 100644 index 2e2b3164f3..0000000000 --- a/pype/configurations/defaults/presets/plugins/maya/publish.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "ValidateModelName": { - "enabled": false, - "material_file": "/path/to/shader_name_definition.txt", - "regex": "(.*)_(\\d)*_(?P.*)_(GEO)" - }, - "ValidateAssemblyName": { - "enabled": false - }, - "ValidateShaderName": { - "enabled": false, - "regex": "(?P.*)_(.*)_SHD" - }, - "ValidateMeshHasOverlappingUVs": { - "enabled": false - } -} diff --git a/pype/configurations/defaults/presets/plugins/maya/workfile_build.json b/pype/configurations/defaults/presets/plugins/maya/workfile_build.json deleted file mode 100644 index 2872b783cb..0000000000 --- a/pype/configurations/defaults/presets/plugins/maya/workfile_build.json +++ /dev/null @@ -1,54 +0,0 @@ -[{ - "tasks": ["lighting"], - - "current_context": [{ - "subset_name_filters": [".+[Mm]ain"], - "families": ["model"], - "repre_names": ["abc", "ma"], - "loaders": ["ReferenceLoader"] - }, { - "families": ["animation", "pointcache"], - "repre_names": ["abc"], - "loaders": ["ReferenceLoader"] - },{ - "families": ["rendersetup"], - "repre_names": ["json"], - "loaders": ["RenderSetupLoader"] - }, { - "families": ["camera"], - "repre_names": ["abc"], - "loaders": ["ReferenceLoader"] - }], - - "linked_assets": [{ - "families": ["setdress"], - "repre_names": ["ma"], - "loaders": ["ReferenceLoader"] - }, { - "families": ["ass"], - "repre_names": ["ass"], - "loaders":["assLoader"] - }] -}, { - "tasks": ["animation"], - - "current_context": [{ - "families": ["camera"], - "repre_names": ["abc", "ma"], - "loaders": ["ReferenceLoader"] - }, { - "families": ["audio"], - "repre_names": ["wav"], - "loaders": ["RenderSetupLoader"] - }], - - "linked_assets": [{ - "families": ["setdress"], - "repre_names": ["proxy"], - "loaders": ["ReferenceLoader"] - }, { - "families": ["rig"], - "repre_names": ["ass"], - "loaders": ["rigLoader"] - }] -}] diff --git a/pype/configurations/defaults/presets/plugins/nuke/create.json b/pype/configurations/defaults/presets/plugins/nuke/create.json deleted file mode 100644 index 4deb0b4ad5..0000000000 --- a/pype/configurations/defaults/presets/plugins/nuke/create.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "CreateWriteRender": { - "fpath_template": "{work}/renders/nuke/{subset}/{subset}.{frame}.{ext}" - }, - "CreateWritePrerender": { - "fpath_template": "{work}/prerenders/nuke/{subset}/{subset}.{frame}.{ext}" - } -} diff --git a/pype/configurations/defaults/presets/plugins/nuke/load.json b/pype/configurations/defaults/presets/plugins/nuke/load.json deleted file mode 100644 index 0967ef424b..0000000000 --- a/pype/configurations/defaults/presets/plugins/nuke/load.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/pype/configurations/defaults/presets/plugins/nuke/publish.json b/pype/configurations/defaults/presets/plugins/nuke/publish.json deleted file mode 100644 index ab0d0e76a5..0000000000 --- a/pype/configurations/defaults/presets/plugins/nuke/publish.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "ExtractThumbnail": { - "nodes": { - "Reformat": [ - ["type", "to format"], - ["format", "HD_1080"], - ["filter", "Lanczos6"], - ["black_outside", true], - ["pbb", false] - ] - } - }, - "ValidateNukeWriteKnobs": { - "enabled": false, - "knobs": { - "render": { - "review": true - } - } - }, - "ExtractReviewDataLut": { - "__documentation__": { - "viewer_lut_raw": "set to `true` if you Input_process node on viewer is used with baked screen space, so you have to look with RAW viewer lut. Else just keep it on `false`", - "enabled": "keep in on `false` if you want Nuke baking colorspace workflow applied else FFmpeg will convert imgsequence with baked LUT" - }, - "enabled": false - }, - "ExtractReviewDataMov": { - "__documentation__": { - "viewer_lut_raw": "set to `true` if you Input_process node on viewer is used with baked screen space, so you have to look with RAW viewer lut. Else just keep it on `false`", - "enabled": "keep in on `true` if you want Nuke baking colorspace workflow applied" - }, - "enabled": true, - "viewer_lut_raw": false - }, - "ExtractSlateFrame": { - "__documentation__": { - "viewer_lut_raw": "set to `true` if you Input_process node on viewer is used with baked screen space, so you have to look with RAW viewer lut. Else just keep it on `false`" - }, - "viewer_lut_raw": false - }, - "NukeSubmitDeadline": { - "deadline_priority": 50, - "deadline_pool": "", - "deadline_pool_secondary": "", - "deadline_chunk_size": 1 - } -} diff --git a/pype/configurations/defaults/presets/plugins/nuke/workfile_build.json b/pype/configurations/defaults/presets/plugins/nuke/workfile_build.json deleted file mode 100644 index d3613c929e..0000000000 --- a/pype/configurations/defaults/presets/plugins/nuke/workfile_build.json +++ /dev/null @@ -1,11 +0,0 @@ -[{ - "tasks": ["compositing"], - - "current_context": [{ - "families": ["render", "plate"], - "repre_names": ["exr" ,"dpx"], - "loaders": ["LoadSequence"] - }], - - "linked_assets": [] -}] diff --git a/pype/configurations/defaults/presets/plugins/nukestudio/filter.json b/pype/configurations/defaults/presets/plugins/nukestudio/filter.json deleted file mode 100644 index bd6a0dc1bd..0000000000 --- a/pype/configurations/defaults/presets/plugins/nukestudio/filter.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "strict": { - "ValidateVersion": true, - "VersionUpWorkfile": true - }, - "benevolent": { - "ValidateVersion": false, - "VersionUpWorkfile": false - } -} \ No newline at end of file diff --git a/pype/configurations/defaults/presets/plugins/nukestudio/publish.json b/pype/configurations/defaults/presets/plugins/nukestudio/publish.json deleted file mode 100644 index 8c4ad133f1..0000000000 --- a/pype/configurations/defaults/presets/plugins/nukestudio/publish.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "CollectInstanceVersion": { - "enabled": false - }, - "ExtractReviewCutUpVideo": { - "tags_addition": [] - } -} diff --git a/pype/configurations/defaults/presets/plugins/resolve/create.json b/pype/configurations/defaults/presets/plugins/resolve/create.json deleted file mode 100644 index 29ca5900fb..0000000000 --- a/pype/configurations/defaults/presets/plugins/resolve/create.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "CreateShotClip": { - "clipName": "{track}{sequence}{shot}", - "folder": "takes", - "steps": 20 - } -} diff --git a/pype/configurations/defaults/presets/plugins/standalonepublisher/publish.json b/pype/configurations/defaults/presets/plugins/standalonepublisher/publish.json deleted file mode 100644 index 2b2fb660c2..0000000000 --- a/pype/configurations/defaults/presets/plugins/standalonepublisher/publish.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "ExtractThumbnailSP": { - "ffmpeg_args": { - "input": [ - "-gamma 2.2" - ], - "output": [] - } - }, - "ExtractReviewSP": { - "outputs": { - "h264": { - "input": [ - "-gamma 2.2" - ], - "output": [ - "-pix_fmt yuv420p", - "-crf 18" - ], - "tags": ["preview"], - "ext": "mov" - } - } - } -} diff --git a/pype/configurations/defaults/presets/plugins/test/create.json b/pype/configurations/defaults/presets/plugins/test/create.json deleted file mode 100644 index fa0b2fc05f..0000000000 --- a/pype/configurations/defaults/presets/plugins/test/create.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "MyTestCreator": { - "my_test_property": "B", - "active": false, - "new_property": "new", - "family": "new_family" - } -} diff --git a/pype/configurations/defaults/presets/plugins/test/publish.json b/pype/configurations/defaults/presets/plugins/test/publish.json deleted file mode 100644 index 3180dd5d8a..0000000000 --- a/pype/configurations/defaults/presets/plugins/test/publish.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "MyTestPlugin": { - "label": "loaded from preset", - "optional": true, - "families": ["changed", "by", "preset"] - }, - "MyTestRemovedPlugin": { - "enabled": false - } -} diff --git a/pype/configurations/defaults/presets/tools/pyblish.json b/pype/configurations/defaults/presets/tools/pyblish.json deleted file mode 100644 index e81932ec45..0000000000 --- a/pype/configurations/defaults/presets/tools/pyblish.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "ui": { - "intents": { - "__description__": [ - "In items you can specify {label: value} of intents", - "`default` may be used for setting default value." - ], - "default": "wip", - "items": { - "": "", - "wip": "WIP", - "test": "TEST", - "final": "FINAL" - } - } - } -} diff --git a/pype/configurations/defaults/presets/tray/menu_items.json b/pype/configurations/defaults/presets/tray/menu_items.json deleted file mode 100644 index 6c6763848b..0000000000 --- a/pype/configurations/defaults/presets/tray/menu_items.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "item_usage": { - "User settings": false, - "Ftrack": true, - "Muster": false, - "Avalon": true, - "Clockify": false, - "Standalone Publish": true, - "Logging": true, - "Idle Manager": true, - "Timers Manager": true, - "Rest Api": true, - "Adobe Communicator": true - }, - "attributes": { - "Rest Api": { - "default_port": 8021, - "exclude_ports": [] - }, - "Timers Manager": { - "full_time": 15, - "message_time": 0.5 - }, - "Clockify": { - "workspace_name": null - } - } -} diff --git a/pype/configurations/defaults/anatomy/README.md b/pype/configurations/defaults/project_configurations/anatomy/README.md similarity index 100% rename from pype/configurations/defaults/anatomy/README.md rename to pype/configurations/defaults/project_configurations/anatomy/README.md diff --git a/pype/configurations/defaults/presets/colorspace/default.json b/pype/configurations/defaults/project_configurations/anatomy/colorspace.json similarity index 100% rename from pype/configurations/defaults/presets/colorspace/default.json rename to pype/configurations/defaults/project_configurations/anatomy/colorspace.json diff --git a/pype/configurations/defaults/presets/colorspace/aces103-cg.json b/pype/configurations/defaults/project_configurations/anatomy/colorspace/aces103-cg.json similarity index 100% rename from pype/configurations/defaults/presets/colorspace/aces103-cg.json rename to pype/configurations/defaults/project_configurations/anatomy/colorspace/aces103-cg.json diff --git a/pype/configurations/defaults/presets/dataflow/default.json b/pype/configurations/defaults/project_configurations/anatomy/dataflow.json similarity index 100% rename from pype/configurations/defaults/presets/dataflow/default.json rename to pype/configurations/defaults/project_configurations/anatomy/dataflow.json diff --git a/pype/configurations/defaults/presets/dataflow/aces-exr.json b/pype/configurations/defaults/project_configurations/anatomy/dataflow/aces-exr.json similarity index 100% rename from pype/configurations/defaults/presets/dataflow/aces-exr.json rename to pype/configurations/defaults/project_configurations/anatomy/dataflow/aces-exr.json diff --git a/pype/configurations/defaults/anatomy/default.yaml b/pype/configurations/defaults/project_configurations/anatomy/default.yaml similarity index 100% rename from pype/configurations/defaults/anatomy/default.yaml rename to pype/configurations/defaults/project_configurations/anatomy/default.yaml diff --git a/pype/configurations/defaults/anatomy/roots.json b/pype/configurations/defaults/project_configurations/anatomy/roots.json similarity index 100% rename from pype/configurations/defaults/anatomy/roots.json rename to pype/configurations/defaults/project_configurations/anatomy/roots.json diff --git a/pype/configurations/defaults/project_configurations/anatomy/templates.json b/pype/configurations/defaults/project_configurations/anatomy/templates.json new file mode 100644 index 0000000000..23e9f308f2 --- /dev/null +++ b/pype/configurations/defaults/project_configurations/anatomy/templates.json @@ -0,0 +1,35 @@ +{ + "version_padding": 3, + "version": "v{version:0>{@version_padding}}", + "frame_padding": 4, + "frame": "{frame:0>{@frame_padding}}", + + "work": { + "folder": "{root}/{project[name]}/{hierarchy}/{asset}/work/{task}", + "file": "{project[code]}_{asset}_{task}_{@version}<_{comment}>.{ext}", + "path": "{@folder}/{@file}" + }, + + "render": { + "folder": "{root}/{project[name]}/{hierarchy}/{asset}/publish/render/{subset}/{@version}", + "file": "{project[code]}_{asset}_{subset}_{@version}<_{output}><.{@frame}>.{representation}", + "path": "{@folder}/{@file}" + }, + + "texture": { + "path": "{root}/{project[name]}/{hierarchy}/{asset}/publish/{family}/{subset}" + }, + + "publish": { + "folder": "{root}/{project[name]}/{hierarchy}/{asset}/publish/{family}/{subset}/{@version}", + "file": "{project[code]}_{asset}_{subset}_{@version}<_{output}><.{@frame}>.{representation}", + "path": "{@folder}/{@file}", + "thumbnail": "{thumbnail_root}/{project[name]}/{_id}_{thumbnail_type}{ext}" + }, + + "master": { + "folder": "{root}/{project[name]}/{hierarchy}/{asset}/publish/{family}/{subset}/master", + "file": "{project[code]}_{asset}_{subset}_master<_{output}><.{frame}>.{representation}", + "path": "{@folder}/{@file}" + } +} diff --git a/pype/configurations/defaults/project_configurations/plugins/celaction/publish.json b/pype/configurations/defaults/project_configurations/celaction/publish.json similarity index 100% rename from pype/configurations/defaults/project_configurations/plugins/celaction/publish.json rename to pype/configurations/defaults/project_configurations/celaction/publish.json diff --git a/pype/configurations/defaults/project_configurations/plugins/config.json b/pype/configurations/defaults/project_configurations/config.json similarity index 100% rename from pype/configurations/defaults/project_configurations/plugins/config.json rename to pype/configurations/defaults/project_configurations/config.json diff --git a/pype/configurations/defaults/presets/ftrack/ftrack_config.json b/pype/configurations/defaults/project_configurations/ftrack/ftrack_config.json similarity index 100% rename from pype/configurations/defaults/presets/ftrack/ftrack_config.json rename to pype/configurations/defaults/project_configurations/ftrack/ftrack_config.json diff --git a/pype/configurations/defaults/presets/ftrack/ftrack_custom_attributes.json b/pype/configurations/defaults/project_configurations/ftrack/ftrack_custom_attributes.json similarity index 100% rename from pype/configurations/defaults/presets/ftrack/ftrack_custom_attributes.json rename to pype/configurations/defaults/project_configurations/ftrack/ftrack_custom_attributes.json diff --git a/pype/configurations/defaults/presets/ftrack/partnership_ftrack_cred.json b/pype/configurations/defaults/project_configurations/ftrack/partnership_ftrack_cred.json similarity index 100% rename from pype/configurations/defaults/presets/ftrack/partnership_ftrack_cred.json rename to pype/configurations/defaults/project_configurations/ftrack/partnership_ftrack_cred.json diff --git a/pype/configurations/defaults/presets/ftrack/plugins/server.json b/pype/configurations/defaults/project_configurations/ftrack/plugins/server.json similarity index 100% rename from pype/configurations/defaults/presets/ftrack/plugins/server.json rename to pype/configurations/defaults/project_configurations/ftrack/plugins/server.json diff --git a/pype/configurations/defaults/presets/ftrack/plugins/user.json b/pype/configurations/defaults/project_configurations/ftrack/plugins/user.json similarity index 100% rename from pype/configurations/defaults/presets/ftrack/plugins/user.json rename to pype/configurations/defaults/project_configurations/ftrack/plugins/user.json diff --git a/pype/configurations/defaults/presets/ftrack/project_defaults.json b/pype/configurations/defaults/project_configurations/ftrack/project_defaults.json similarity index 100% rename from pype/configurations/defaults/presets/ftrack/project_defaults.json rename to pype/configurations/defaults/project_configurations/ftrack/project_defaults.json diff --git a/pype/configurations/defaults/project_configurations/plugins/ftrack/publish.json b/pype/configurations/defaults/project_configurations/ftrack/publish.json similarity index 100% rename from pype/configurations/defaults/project_configurations/plugins/ftrack/publish.json rename to pype/configurations/defaults/project_configurations/ftrack/publish.json diff --git a/pype/configurations/defaults/project_configurations/plugins/maya/create.json b/pype/configurations/defaults/project_configurations/global/create.json similarity index 100% rename from pype/configurations/defaults/project_configurations/plugins/maya/create.json rename to pype/configurations/defaults/project_configurations/global/create.json diff --git a/pype/configurations/defaults/presets/tools/creator.json b/pype/configurations/defaults/project_configurations/global/creator.json similarity index 100% rename from pype/configurations/defaults/presets/tools/creator.json rename to pype/configurations/defaults/project_configurations/global/creator.json diff --git a/pype/configurations/defaults/project_configurations/plugins/global/filter.json b/pype/configurations/defaults/project_configurations/global/filter.json similarity index 100% rename from pype/configurations/defaults/project_configurations/plugins/global/filter.json rename to pype/configurations/defaults/project_configurations/global/filter.json diff --git a/pype/configurations/defaults/project_configurations/plugins/nuke/load.json b/pype/configurations/defaults/project_configurations/global/load.json similarity index 100% rename from pype/configurations/defaults/project_configurations/plugins/nuke/load.json rename to pype/configurations/defaults/project_configurations/global/load.json diff --git a/pype/configurations/defaults/presets/tools/project_folder_structure.json b/pype/configurations/defaults/project_configurations/global/project_folder_structure.json similarity index 100% rename from pype/configurations/defaults/presets/tools/project_folder_structure.json rename to pype/configurations/defaults/project_configurations/global/project_folder_structure.json diff --git a/pype/configurations/defaults/project_configurations/plugins/global/publish.json b/pype/configurations/defaults/project_configurations/global/publish.json similarity index 100% rename from pype/configurations/defaults/project_configurations/plugins/global/publish.json rename to pype/configurations/defaults/project_configurations/global/publish.json diff --git a/pype/configurations/defaults/presets/tools/sw_folders.json b/pype/configurations/defaults/project_configurations/global/sw_folders.json similarity index 100% rename from pype/configurations/defaults/presets/tools/sw_folders.json rename to pype/configurations/defaults/project_configurations/global/sw_folders.json diff --git a/pype/configurations/defaults/presets/tools/workfiles.json b/pype/configurations/defaults/project_configurations/global/workfiles.json similarity index 100% rename from pype/configurations/defaults/presets/tools/workfiles.json rename to pype/configurations/defaults/project_configurations/global/workfiles.json diff --git a/pype/configurations/defaults/presets/maya/capture.json b/pype/configurations/defaults/project_configurations/maya/capture.json similarity index 100% rename from pype/configurations/defaults/presets/maya/capture.json rename to pype/configurations/defaults/project_configurations/maya/capture.json diff --git a/pype/configurations/defaults/project_configurations/plugins/global/create.json b/pype/configurations/defaults/project_configurations/maya/create.json similarity index 100% rename from pype/configurations/defaults/project_configurations/plugins/global/create.json rename to pype/configurations/defaults/project_configurations/maya/create.json diff --git a/pype/configurations/defaults/project_configurations/plugins/maya/filter.json b/pype/configurations/defaults/project_configurations/maya/filter.json similarity index 100% rename from pype/configurations/defaults/project_configurations/plugins/maya/filter.json rename to pype/configurations/defaults/project_configurations/maya/filter.json diff --git a/pype/configurations/defaults/project_configurations/plugins/maya/load.json b/pype/configurations/defaults/project_configurations/maya/load.json similarity index 100% rename from pype/configurations/defaults/project_configurations/plugins/maya/load.json rename to pype/configurations/defaults/project_configurations/maya/load.json diff --git a/pype/configurations/defaults/project_configurations/plugins/maya/publish.json b/pype/configurations/defaults/project_configurations/maya/publish.json similarity index 100% rename from pype/configurations/defaults/project_configurations/plugins/maya/publish.json rename to pype/configurations/defaults/project_configurations/maya/publish.json diff --git a/pype/configurations/defaults/project_configurations/plugins/maya/workfile_build.json b/pype/configurations/defaults/project_configurations/maya/workfile_build.json similarity index 100% rename from pype/configurations/defaults/project_configurations/plugins/maya/workfile_build.json rename to pype/configurations/defaults/project_configurations/maya/workfile_build.json diff --git a/pype/configurations/defaults/presets/muster/templates_mapping.json b/pype/configurations/defaults/project_configurations/muster/templates_mapping.json similarity index 100% rename from pype/configurations/defaults/presets/muster/templates_mapping.json rename to pype/configurations/defaults/project_configurations/muster/templates_mapping.json diff --git a/pype/configurations/defaults/project_configurations/plugins/nuke/create.json b/pype/configurations/defaults/project_configurations/nuke/create.json similarity index 100% rename from pype/configurations/defaults/project_configurations/plugins/nuke/create.json rename to pype/configurations/defaults/project_configurations/nuke/create.json diff --git a/pype/configurations/defaults/project_configurations/plugins/global/load.json b/pype/configurations/defaults/project_configurations/nuke/load.json similarity index 100% rename from pype/configurations/defaults/project_configurations/plugins/global/load.json rename to pype/configurations/defaults/project_configurations/nuke/load.json diff --git a/pype/configurations/defaults/project_configurations/plugins/nuke/publish.json b/pype/configurations/defaults/project_configurations/nuke/publish.json similarity index 100% rename from pype/configurations/defaults/project_configurations/plugins/nuke/publish.json rename to pype/configurations/defaults/project_configurations/nuke/publish.json diff --git a/pype/configurations/defaults/project_configurations/plugins/nuke/workfile_build.json b/pype/configurations/defaults/project_configurations/nuke/workfile_build.json similarity index 100% rename from pype/configurations/defaults/project_configurations/plugins/nuke/workfile_build.json rename to pype/configurations/defaults/project_configurations/nuke/workfile_build.json diff --git a/pype/configurations/defaults/project_configurations/plugins/nukestudio/filter.json b/pype/configurations/defaults/project_configurations/nukestudio/filter.json similarity index 100% rename from pype/configurations/defaults/project_configurations/plugins/nukestudio/filter.json rename to pype/configurations/defaults/project_configurations/nukestudio/filter.json diff --git a/pype/configurations/defaults/project_configurations/plugins/nukestudio/publish.json b/pype/configurations/defaults/project_configurations/nukestudio/publish.json similarity index 100% rename from pype/configurations/defaults/project_configurations/plugins/nukestudio/publish.json rename to pype/configurations/defaults/project_configurations/nukestudio/publish.json diff --git a/pype/configurations/defaults/presets/nukestudio/tags.json b/pype/configurations/defaults/project_configurations/nukestudio/tags.json similarity index 100% rename from pype/configurations/defaults/presets/nukestudio/tags.json rename to pype/configurations/defaults/project_configurations/nukestudio/tags.json diff --git a/pype/configurations/defaults/presets/premiere/asset_default.json b/pype/configurations/defaults/project_configurations/premiere/asset_default.json similarity index 100% rename from pype/configurations/defaults/presets/premiere/asset_default.json rename to pype/configurations/defaults/project_configurations/premiere/asset_default.json diff --git a/pype/configurations/defaults/presets/premiere/rules_tasks.json b/pype/configurations/defaults/project_configurations/premiere/rules_tasks.json similarity index 100% rename from pype/configurations/defaults/presets/premiere/rules_tasks.json rename to pype/configurations/defaults/project_configurations/premiere/rules_tasks.json diff --git a/pype/configurations/defaults/project_configurations/plugins/resolve/create.json b/pype/configurations/defaults/project_configurations/resolve/create.json similarity index 100% rename from pype/configurations/defaults/project_configurations/plugins/resolve/create.json rename to pype/configurations/defaults/project_configurations/resolve/create.json diff --git a/pype/configurations/defaults/presets/standalone_publish/families.json b/pype/configurations/defaults/project_configurations/standalonepublisher/families.json similarity index 100% rename from pype/configurations/defaults/presets/standalone_publish/families.json rename to pype/configurations/defaults/project_configurations/standalonepublisher/families.json diff --git a/pype/configurations/defaults/project_configurations/plugins/standalonepublisher/publish.json b/pype/configurations/defaults/project_configurations/standalonepublisher/publish.json similarity index 100% rename from pype/configurations/defaults/project_configurations/plugins/standalonepublisher/publish.json rename to pype/configurations/defaults/project_configurations/standalonepublisher/publish.json diff --git a/pype/configurations/defaults/project_configurations/plugins/test/create.json b/pype/configurations/defaults/project_configurations/test/create.json similarity index 100% rename from pype/configurations/defaults/project_configurations/plugins/test/create.json rename to pype/configurations/defaults/project_configurations/test/create.json diff --git a/pype/configurations/defaults/project_configurations/plugins/test/publish.json b/pype/configurations/defaults/project_configurations/test/publish.json similarity index 100% rename from pype/configurations/defaults/project_configurations/plugins/test/publish.json rename to pype/configurations/defaults/project_configurations/test/publish.json diff --git a/pype/configurations/defaults/presets/tools/slates/example_HD.json b/pype/configurations/defaults/project_configurations/tools/slates/example_HD.json similarity index 100% rename from pype/configurations/defaults/presets/tools/slates/example_HD.json rename to pype/configurations/defaults/project_configurations/tools/slates/example_HD.json diff --git a/pype/configurations/defaults/presets/unreal/project_setup.json b/pype/configurations/defaults/project_configurations/unreal/project_setup.json similarity index 100% rename from pype/configurations/defaults/presets/unreal/project_setup.json rename to pype/configurations/defaults/project_configurations/unreal/project_setup.json diff --git a/pype/configurations/defaults/environments/avalon.json b/pype/configurations/defaults/studio_configurations/environments/avalon.json similarity index 100% rename from pype/configurations/defaults/environments/avalon.json rename to pype/configurations/defaults/studio_configurations/environments/avalon.json diff --git a/pype/configurations/defaults/environments/blender.json b/pype/configurations/defaults/studio_configurations/environments/blender.json similarity index 100% rename from pype/configurations/defaults/environments/blender.json rename to pype/configurations/defaults/studio_configurations/environments/blender.json diff --git a/pype/configurations/defaults/environments/celaction.json b/pype/configurations/defaults/studio_configurations/environments/celaction.json similarity index 100% rename from pype/configurations/defaults/environments/celaction.json rename to pype/configurations/defaults/studio_configurations/environments/celaction.json diff --git a/pype/configurations/defaults/environments/deadline.json b/pype/configurations/defaults/studio_configurations/environments/deadline.json similarity index 100% rename from pype/configurations/defaults/environments/deadline.json rename to pype/configurations/defaults/studio_configurations/environments/deadline.json diff --git a/pype/configurations/defaults/environments/ftrack.json b/pype/configurations/defaults/studio_configurations/environments/ftrack.json similarity index 100% rename from pype/configurations/defaults/environments/ftrack.json rename to pype/configurations/defaults/studio_configurations/environments/ftrack.json diff --git a/pype/configurations/defaults/environments/global.json b/pype/configurations/defaults/studio_configurations/environments/global.json similarity index 100% rename from pype/configurations/defaults/environments/global.json rename to pype/configurations/defaults/studio_configurations/environments/global.json diff --git a/pype/configurations/defaults/environments/harmony.json b/pype/configurations/defaults/studio_configurations/environments/harmony.json similarity index 100% rename from pype/configurations/defaults/environments/harmony.json rename to pype/configurations/defaults/studio_configurations/environments/harmony.json diff --git a/pype/configurations/defaults/environments/houdini.json b/pype/configurations/defaults/studio_configurations/environments/houdini.json similarity index 100% rename from pype/configurations/defaults/environments/houdini.json rename to pype/configurations/defaults/studio_configurations/environments/houdini.json diff --git a/pype/configurations/defaults/environments/maya.json b/pype/configurations/defaults/studio_configurations/environments/maya.json similarity index 100% rename from pype/configurations/defaults/environments/maya.json rename to pype/configurations/defaults/studio_configurations/environments/maya.json diff --git a/pype/configurations/defaults/environments/maya_2018.json b/pype/configurations/defaults/studio_configurations/environments/maya_2018.json similarity index 100% rename from pype/configurations/defaults/environments/maya_2018.json rename to pype/configurations/defaults/studio_configurations/environments/maya_2018.json diff --git a/pype/configurations/defaults/environments/maya_2020.json b/pype/configurations/defaults/studio_configurations/environments/maya_2020.json similarity index 100% rename from pype/configurations/defaults/environments/maya_2020.json rename to pype/configurations/defaults/studio_configurations/environments/maya_2020.json diff --git a/pype/configurations/defaults/environments/mayabatch.json b/pype/configurations/defaults/studio_configurations/environments/mayabatch.json similarity index 100% rename from pype/configurations/defaults/environments/mayabatch.json rename to pype/configurations/defaults/studio_configurations/environments/mayabatch.json diff --git a/pype/configurations/defaults/environments/mayabatch_2019.json b/pype/configurations/defaults/studio_configurations/environments/mayabatch_2019.json similarity index 100% rename from pype/configurations/defaults/environments/mayabatch_2019.json rename to pype/configurations/defaults/studio_configurations/environments/mayabatch_2019.json diff --git a/pype/configurations/defaults/environments/mtoa_3.1.1.json b/pype/configurations/defaults/studio_configurations/environments/mtoa_3.1.1.json similarity index 100% rename from pype/configurations/defaults/environments/mtoa_3.1.1.json rename to pype/configurations/defaults/studio_configurations/environments/mtoa_3.1.1.json diff --git a/pype/configurations/defaults/environments/muster.json b/pype/configurations/defaults/studio_configurations/environments/muster.json similarity index 100% rename from pype/configurations/defaults/environments/muster.json rename to pype/configurations/defaults/studio_configurations/environments/muster.json diff --git a/pype/configurations/defaults/environments/nuke.json b/pype/configurations/defaults/studio_configurations/environments/nuke.json similarity index 100% rename from pype/configurations/defaults/environments/nuke.json rename to pype/configurations/defaults/studio_configurations/environments/nuke.json diff --git a/pype/configurations/defaults/environments/nukestudio.json b/pype/configurations/defaults/studio_configurations/environments/nukestudio.json similarity index 100% rename from pype/configurations/defaults/environments/nukestudio.json rename to pype/configurations/defaults/studio_configurations/environments/nukestudio.json diff --git a/pype/configurations/defaults/environments/nukestudio_10.0.json b/pype/configurations/defaults/studio_configurations/environments/nukestudio_10.0.json similarity index 100% rename from pype/configurations/defaults/environments/nukestudio_10.0.json rename to pype/configurations/defaults/studio_configurations/environments/nukestudio_10.0.json diff --git a/pype/configurations/defaults/environments/nukex.json b/pype/configurations/defaults/studio_configurations/environments/nukex.json similarity index 100% rename from pype/configurations/defaults/environments/nukex.json rename to pype/configurations/defaults/studio_configurations/environments/nukex.json diff --git a/pype/configurations/defaults/environments/nukex_10.0.json b/pype/configurations/defaults/studio_configurations/environments/nukex_10.0.json similarity index 100% rename from pype/configurations/defaults/environments/nukex_10.0.json rename to pype/configurations/defaults/studio_configurations/environments/nukex_10.0.json diff --git a/pype/configurations/defaults/environments/photoshop.json b/pype/configurations/defaults/studio_configurations/environments/photoshop.json similarity index 100% rename from pype/configurations/defaults/environments/photoshop.json rename to pype/configurations/defaults/studio_configurations/environments/photoshop.json diff --git a/pype/configurations/defaults/environments/premiere.json b/pype/configurations/defaults/studio_configurations/environments/premiere.json similarity index 100% rename from pype/configurations/defaults/environments/premiere.json rename to pype/configurations/defaults/studio_configurations/environments/premiere.json diff --git a/pype/configurations/defaults/environments/resolve.json b/pype/configurations/defaults/studio_configurations/environments/resolve.json similarity index 100% rename from pype/configurations/defaults/environments/resolve.json rename to pype/configurations/defaults/studio_configurations/environments/resolve.json diff --git a/pype/configurations/defaults/environments/storyboardpro.json b/pype/configurations/defaults/studio_configurations/environments/storyboardpro.json similarity index 100% rename from pype/configurations/defaults/environments/storyboardpro.json rename to pype/configurations/defaults/studio_configurations/environments/storyboardpro.json diff --git a/pype/configurations/defaults/environments/unreal_4.24.json b/pype/configurations/defaults/studio_configurations/environments/unreal_4.24.json similarity index 100% rename from pype/configurations/defaults/environments/unreal_4.24.json rename to pype/configurations/defaults/studio_configurations/environments/unreal_4.24.json diff --git a/pype/configurations/defaults/environments/vray_4300.json b/pype/configurations/defaults/studio_configurations/environments/vray_4300.json similarity index 100% rename from pype/configurations/defaults/environments/vray_4300.json rename to pype/configurations/defaults/studio_configurations/environments/vray_4300.json diff --git a/pype/configurations/defaults/launchers/blender_2.80.toml b/pype/configurations/defaults/studio_configurations/launchers/blender_2.80.toml similarity index 100% rename from pype/configurations/defaults/launchers/blender_2.80.toml rename to pype/configurations/defaults/studio_configurations/launchers/blender_2.80.toml diff --git a/pype/configurations/defaults/launchers/blender_2.81.toml b/pype/configurations/defaults/studio_configurations/launchers/blender_2.81.toml similarity index 100% rename from pype/configurations/defaults/launchers/blender_2.81.toml rename to pype/configurations/defaults/studio_configurations/launchers/blender_2.81.toml diff --git a/pype/configurations/defaults/launchers/blender_2.82.toml b/pype/configurations/defaults/studio_configurations/launchers/blender_2.82.toml similarity index 100% rename from pype/configurations/defaults/launchers/blender_2.82.toml rename to pype/configurations/defaults/studio_configurations/launchers/blender_2.82.toml diff --git a/pype/configurations/defaults/launchers/blender_2.83.toml b/pype/configurations/defaults/studio_configurations/launchers/blender_2.83.toml similarity index 100% rename from pype/configurations/defaults/launchers/blender_2.83.toml rename to pype/configurations/defaults/studio_configurations/launchers/blender_2.83.toml diff --git a/pype/configurations/defaults/launchers/celaction_local.toml b/pype/configurations/defaults/studio_configurations/launchers/celaction_local.toml similarity index 100% rename from pype/configurations/defaults/launchers/celaction_local.toml rename to pype/configurations/defaults/studio_configurations/launchers/celaction_local.toml diff --git a/pype/configurations/defaults/launchers/celaction_publish.toml b/pype/configurations/defaults/studio_configurations/launchers/celaction_publish.toml similarity index 100% rename from pype/configurations/defaults/launchers/celaction_publish.toml rename to pype/configurations/defaults/studio_configurations/launchers/celaction_publish.toml diff --git a/pype/configurations/defaults/launchers/darwin/blender_2.82 b/pype/configurations/defaults/studio_configurations/launchers/darwin/blender_2.82 similarity index 100% rename from pype/configurations/defaults/launchers/darwin/blender_2.82 rename to pype/configurations/defaults/studio_configurations/launchers/darwin/blender_2.82 diff --git a/pype/configurations/defaults/launchers/darwin/harmony_17 b/pype/configurations/defaults/studio_configurations/launchers/darwin/harmony_17 similarity index 100% rename from pype/configurations/defaults/launchers/darwin/harmony_17 rename to pype/configurations/defaults/studio_configurations/launchers/darwin/harmony_17 diff --git a/pype/configurations/defaults/launchers/darwin/harmony_17_launch b/pype/configurations/defaults/studio_configurations/launchers/darwin/harmony_17_launch similarity index 100% rename from pype/configurations/defaults/launchers/darwin/harmony_17_launch rename to pype/configurations/defaults/studio_configurations/launchers/darwin/harmony_17_launch diff --git a/pype/configurations/defaults/launchers/darwin/python3 b/pype/configurations/defaults/studio_configurations/launchers/darwin/python3 similarity index 100% rename from pype/configurations/defaults/launchers/darwin/python3 rename to pype/configurations/defaults/studio_configurations/launchers/darwin/python3 diff --git a/pype/configurations/defaults/launchers/harmony_17.toml b/pype/configurations/defaults/studio_configurations/launchers/harmony_17.toml similarity index 100% rename from pype/configurations/defaults/launchers/harmony_17.toml rename to pype/configurations/defaults/studio_configurations/launchers/harmony_17.toml diff --git a/pype/configurations/defaults/launchers/houdini_16.toml b/pype/configurations/defaults/studio_configurations/launchers/houdini_16.toml similarity index 100% rename from pype/configurations/defaults/launchers/houdini_16.toml rename to pype/configurations/defaults/studio_configurations/launchers/houdini_16.toml diff --git a/pype/configurations/defaults/launchers/houdini_17.toml b/pype/configurations/defaults/studio_configurations/launchers/houdini_17.toml similarity index 100% rename from pype/configurations/defaults/launchers/houdini_17.toml rename to pype/configurations/defaults/studio_configurations/launchers/houdini_17.toml diff --git a/pype/configurations/defaults/launchers/houdini_18.toml b/pype/configurations/defaults/studio_configurations/launchers/houdini_18.toml similarity index 100% rename from pype/configurations/defaults/launchers/houdini_18.toml rename to pype/configurations/defaults/studio_configurations/launchers/houdini_18.toml diff --git a/pype/configurations/defaults/launchers/linux/maya2016 b/pype/configurations/defaults/studio_configurations/launchers/linux/maya2016 similarity index 100% rename from pype/configurations/defaults/launchers/linux/maya2016 rename to pype/configurations/defaults/studio_configurations/launchers/linux/maya2016 diff --git a/pype/configurations/defaults/launchers/linux/maya2017 b/pype/configurations/defaults/studio_configurations/launchers/linux/maya2017 similarity index 100% rename from pype/configurations/defaults/launchers/linux/maya2017 rename to pype/configurations/defaults/studio_configurations/launchers/linux/maya2017 diff --git a/pype/configurations/defaults/launchers/linux/maya2018 b/pype/configurations/defaults/studio_configurations/launchers/linux/maya2018 similarity index 100% rename from pype/configurations/defaults/launchers/linux/maya2018 rename to pype/configurations/defaults/studio_configurations/launchers/linux/maya2018 diff --git a/pype/configurations/defaults/launchers/linux/maya2019 b/pype/configurations/defaults/studio_configurations/launchers/linux/maya2019 similarity index 100% rename from pype/configurations/defaults/launchers/linux/maya2019 rename to pype/configurations/defaults/studio_configurations/launchers/linux/maya2019 diff --git a/pype/configurations/defaults/launchers/linux/maya2020 b/pype/configurations/defaults/studio_configurations/launchers/linux/maya2020 similarity index 100% rename from pype/configurations/defaults/launchers/linux/maya2020 rename to pype/configurations/defaults/studio_configurations/launchers/linux/maya2020 diff --git a/pype/configurations/defaults/launchers/linux/nuke11.3 b/pype/configurations/defaults/studio_configurations/launchers/linux/nuke11.3 similarity index 100% rename from pype/configurations/defaults/launchers/linux/nuke11.3 rename to pype/configurations/defaults/studio_configurations/launchers/linux/nuke11.3 diff --git a/pype/configurations/defaults/launchers/linux/nuke12.0 b/pype/configurations/defaults/studio_configurations/launchers/linux/nuke12.0 similarity index 100% rename from pype/configurations/defaults/launchers/linux/nuke12.0 rename to pype/configurations/defaults/studio_configurations/launchers/linux/nuke12.0 diff --git a/pype/configurations/defaults/launchers/linux/nukestudio11.3 b/pype/configurations/defaults/studio_configurations/launchers/linux/nukestudio11.3 similarity index 100% rename from pype/configurations/defaults/launchers/linux/nukestudio11.3 rename to pype/configurations/defaults/studio_configurations/launchers/linux/nukestudio11.3 diff --git a/pype/configurations/defaults/launchers/linux/nukestudio12.0 b/pype/configurations/defaults/studio_configurations/launchers/linux/nukestudio12.0 similarity index 100% rename from pype/configurations/defaults/launchers/linux/nukestudio12.0 rename to pype/configurations/defaults/studio_configurations/launchers/linux/nukestudio12.0 diff --git a/pype/configurations/defaults/launchers/linux/nukex11.3 b/pype/configurations/defaults/studio_configurations/launchers/linux/nukex11.3 similarity index 100% rename from pype/configurations/defaults/launchers/linux/nukex11.3 rename to pype/configurations/defaults/studio_configurations/launchers/linux/nukex11.3 diff --git a/pype/configurations/defaults/launchers/linux/nukex12.0 b/pype/configurations/defaults/studio_configurations/launchers/linux/nukex12.0 similarity index 100% rename from pype/configurations/defaults/launchers/linux/nukex12.0 rename to pype/configurations/defaults/studio_configurations/launchers/linux/nukex12.0 diff --git a/pype/configurations/defaults/launchers/maya_2016.toml b/pype/configurations/defaults/studio_configurations/launchers/maya_2016.toml similarity index 100% rename from pype/configurations/defaults/launchers/maya_2016.toml rename to pype/configurations/defaults/studio_configurations/launchers/maya_2016.toml diff --git a/pype/configurations/defaults/launchers/maya_2017.toml b/pype/configurations/defaults/studio_configurations/launchers/maya_2017.toml similarity index 100% rename from pype/configurations/defaults/launchers/maya_2017.toml rename to pype/configurations/defaults/studio_configurations/launchers/maya_2017.toml diff --git a/pype/configurations/defaults/launchers/maya_2018.toml b/pype/configurations/defaults/studio_configurations/launchers/maya_2018.toml similarity index 100% rename from pype/configurations/defaults/launchers/maya_2018.toml rename to pype/configurations/defaults/studio_configurations/launchers/maya_2018.toml diff --git a/pype/configurations/defaults/launchers/maya_2019.toml b/pype/configurations/defaults/studio_configurations/launchers/maya_2019.toml similarity index 100% rename from pype/configurations/defaults/launchers/maya_2019.toml rename to pype/configurations/defaults/studio_configurations/launchers/maya_2019.toml diff --git a/pype/configurations/defaults/launchers/maya_2020.toml b/pype/configurations/defaults/studio_configurations/launchers/maya_2020.toml similarity index 100% rename from pype/configurations/defaults/launchers/maya_2020.toml rename to pype/configurations/defaults/studio_configurations/launchers/maya_2020.toml diff --git a/pype/configurations/defaults/launchers/mayabatch_2019.toml b/pype/configurations/defaults/studio_configurations/launchers/mayabatch_2019.toml similarity index 100% rename from pype/configurations/defaults/launchers/mayabatch_2019.toml rename to pype/configurations/defaults/studio_configurations/launchers/mayabatch_2019.toml diff --git a/pype/configurations/defaults/launchers/mayabatch_2020.toml b/pype/configurations/defaults/studio_configurations/launchers/mayabatch_2020.toml similarity index 100% rename from pype/configurations/defaults/launchers/mayabatch_2020.toml rename to pype/configurations/defaults/studio_configurations/launchers/mayabatch_2020.toml diff --git a/pype/configurations/defaults/launchers/mayapy2016.toml b/pype/configurations/defaults/studio_configurations/launchers/mayapy2016.toml similarity index 100% rename from pype/configurations/defaults/launchers/mayapy2016.toml rename to pype/configurations/defaults/studio_configurations/launchers/mayapy2016.toml diff --git a/pype/configurations/defaults/launchers/mayapy2017.toml b/pype/configurations/defaults/studio_configurations/launchers/mayapy2017.toml similarity index 100% rename from pype/configurations/defaults/launchers/mayapy2017.toml rename to pype/configurations/defaults/studio_configurations/launchers/mayapy2017.toml diff --git a/pype/configurations/defaults/launchers/mayapy2018.toml b/pype/configurations/defaults/studio_configurations/launchers/mayapy2018.toml similarity index 100% rename from pype/configurations/defaults/launchers/mayapy2018.toml rename to pype/configurations/defaults/studio_configurations/launchers/mayapy2018.toml diff --git a/pype/configurations/defaults/launchers/mayapy2019.toml b/pype/configurations/defaults/studio_configurations/launchers/mayapy2019.toml similarity index 100% rename from pype/configurations/defaults/launchers/mayapy2019.toml rename to pype/configurations/defaults/studio_configurations/launchers/mayapy2019.toml diff --git a/pype/configurations/defaults/launchers/mayapy2020.toml b/pype/configurations/defaults/studio_configurations/launchers/mayapy2020.toml similarity index 100% rename from pype/configurations/defaults/launchers/mayapy2020.toml rename to pype/configurations/defaults/studio_configurations/launchers/mayapy2020.toml diff --git a/pype/configurations/defaults/launchers/myapp.toml b/pype/configurations/defaults/studio_configurations/launchers/myapp.toml similarity index 100% rename from pype/configurations/defaults/launchers/myapp.toml rename to pype/configurations/defaults/studio_configurations/launchers/myapp.toml diff --git a/pype/configurations/defaults/launchers/nuke_10.0.toml b/pype/configurations/defaults/studio_configurations/launchers/nuke_10.0.toml similarity index 100% rename from pype/configurations/defaults/launchers/nuke_10.0.toml rename to pype/configurations/defaults/studio_configurations/launchers/nuke_10.0.toml diff --git a/pype/configurations/defaults/launchers/nuke_11.0.toml b/pype/configurations/defaults/studio_configurations/launchers/nuke_11.0.toml similarity index 100% rename from pype/configurations/defaults/launchers/nuke_11.0.toml rename to pype/configurations/defaults/studio_configurations/launchers/nuke_11.0.toml diff --git a/pype/configurations/defaults/launchers/nuke_11.2.toml b/pype/configurations/defaults/studio_configurations/launchers/nuke_11.2.toml similarity index 100% rename from pype/configurations/defaults/launchers/nuke_11.2.toml rename to pype/configurations/defaults/studio_configurations/launchers/nuke_11.2.toml diff --git a/pype/configurations/defaults/launchers/nuke_11.3.toml b/pype/configurations/defaults/studio_configurations/launchers/nuke_11.3.toml similarity index 100% rename from pype/configurations/defaults/launchers/nuke_11.3.toml rename to pype/configurations/defaults/studio_configurations/launchers/nuke_11.3.toml diff --git a/pype/configurations/defaults/launchers/nuke_12.0.toml b/pype/configurations/defaults/studio_configurations/launchers/nuke_12.0.toml similarity index 100% rename from pype/configurations/defaults/launchers/nuke_12.0.toml rename to pype/configurations/defaults/studio_configurations/launchers/nuke_12.0.toml diff --git a/pype/configurations/defaults/launchers/nukestudio_10.0.toml b/pype/configurations/defaults/studio_configurations/launchers/nukestudio_10.0.toml similarity index 100% rename from pype/configurations/defaults/launchers/nukestudio_10.0.toml rename to pype/configurations/defaults/studio_configurations/launchers/nukestudio_10.0.toml diff --git a/pype/configurations/defaults/launchers/nukestudio_11.0.toml b/pype/configurations/defaults/studio_configurations/launchers/nukestudio_11.0.toml similarity index 100% rename from pype/configurations/defaults/launchers/nukestudio_11.0.toml rename to pype/configurations/defaults/studio_configurations/launchers/nukestudio_11.0.toml diff --git a/pype/configurations/defaults/launchers/nukestudio_11.2.toml b/pype/configurations/defaults/studio_configurations/launchers/nukestudio_11.2.toml similarity index 100% rename from pype/configurations/defaults/launchers/nukestudio_11.2.toml rename to pype/configurations/defaults/studio_configurations/launchers/nukestudio_11.2.toml diff --git a/pype/configurations/defaults/launchers/nukestudio_11.3.toml b/pype/configurations/defaults/studio_configurations/launchers/nukestudio_11.3.toml similarity index 100% rename from pype/configurations/defaults/launchers/nukestudio_11.3.toml rename to pype/configurations/defaults/studio_configurations/launchers/nukestudio_11.3.toml diff --git a/pype/configurations/defaults/launchers/nukestudio_12.0.toml b/pype/configurations/defaults/studio_configurations/launchers/nukestudio_12.0.toml similarity index 100% rename from pype/configurations/defaults/launchers/nukestudio_12.0.toml rename to pype/configurations/defaults/studio_configurations/launchers/nukestudio_12.0.toml diff --git a/pype/configurations/defaults/launchers/nukex_10.0.toml b/pype/configurations/defaults/studio_configurations/launchers/nukex_10.0.toml similarity index 100% rename from pype/configurations/defaults/launchers/nukex_10.0.toml rename to pype/configurations/defaults/studio_configurations/launchers/nukex_10.0.toml diff --git a/pype/configurations/defaults/launchers/nukex_11.0.toml b/pype/configurations/defaults/studio_configurations/launchers/nukex_11.0.toml similarity index 100% rename from pype/configurations/defaults/launchers/nukex_11.0.toml rename to pype/configurations/defaults/studio_configurations/launchers/nukex_11.0.toml diff --git a/pype/configurations/defaults/launchers/nukex_11.2.toml b/pype/configurations/defaults/studio_configurations/launchers/nukex_11.2.toml similarity index 100% rename from pype/configurations/defaults/launchers/nukex_11.2.toml rename to pype/configurations/defaults/studio_configurations/launchers/nukex_11.2.toml diff --git a/pype/configurations/defaults/launchers/nukex_11.3.toml b/pype/configurations/defaults/studio_configurations/launchers/nukex_11.3.toml similarity index 100% rename from pype/configurations/defaults/launchers/nukex_11.3.toml rename to pype/configurations/defaults/studio_configurations/launchers/nukex_11.3.toml diff --git a/pype/configurations/defaults/launchers/nukex_12.0.toml b/pype/configurations/defaults/studio_configurations/launchers/nukex_12.0.toml similarity index 100% rename from pype/configurations/defaults/launchers/nukex_12.0.toml rename to pype/configurations/defaults/studio_configurations/launchers/nukex_12.0.toml diff --git a/pype/configurations/defaults/launchers/photoshop_2020.toml b/pype/configurations/defaults/studio_configurations/launchers/photoshop_2020.toml similarity index 100% rename from pype/configurations/defaults/launchers/photoshop_2020.toml rename to pype/configurations/defaults/studio_configurations/launchers/photoshop_2020.toml diff --git a/pype/configurations/defaults/launchers/premiere_2019.toml b/pype/configurations/defaults/studio_configurations/launchers/premiere_2019.toml similarity index 100% rename from pype/configurations/defaults/launchers/premiere_2019.toml rename to pype/configurations/defaults/studio_configurations/launchers/premiere_2019.toml diff --git a/pype/configurations/defaults/launchers/premiere_2020.toml b/pype/configurations/defaults/studio_configurations/launchers/premiere_2020.toml similarity index 100% rename from pype/configurations/defaults/launchers/premiere_2020.toml rename to pype/configurations/defaults/studio_configurations/launchers/premiere_2020.toml diff --git a/pype/configurations/defaults/launchers/python_2.toml b/pype/configurations/defaults/studio_configurations/launchers/python_2.toml similarity index 100% rename from pype/configurations/defaults/launchers/python_2.toml rename to pype/configurations/defaults/studio_configurations/launchers/python_2.toml diff --git a/pype/configurations/defaults/launchers/python_3.toml b/pype/configurations/defaults/studio_configurations/launchers/python_3.toml similarity index 100% rename from pype/configurations/defaults/launchers/python_3.toml rename to pype/configurations/defaults/studio_configurations/launchers/python_3.toml diff --git a/pype/configurations/defaults/launchers/resolve_16.toml b/pype/configurations/defaults/studio_configurations/launchers/resolve_16.toml similarity index 100% rename from pype/configurations/defaults/launchers/resolve_16.toml rename to pype/configurations/defaults/studio_configurations/launchers/resolve_16.toml diff --git a/pype/configurations/defaults/launchers/shell.toml b/pype/configurations/defaults/studio_configurations/launchers/shell.toml similarity index 100% rename from pype/configurations/defaults/launchers/shell.toml rename to pype/configurations/defaults/studio_configurations/launchers/shell.toml diff --git a/pype/configurations/defaults/launchers/storyboardpro_7.toml b/pype/configurations/defaults/studio_configurations/launchers/storyboardpro_7.toml similarity index 100% rename from pype/configurations/defaults/launchers/storyboardpro_7.toml rename to pype/configurations/defaults/studio_configurations/launchers/storyboardpro_7.toml diff --git a/pype/configurations/defaults/launchers/unreal_4.24.toml b/pype/configurations/defaults/studio_configurations/launchers/unreal_4.24.toml similarity index 100% rename from pype/configurations/defaults/launchers/unreal_4.24.toml rename to pype/configurations/defaults/studio_configurations/launchers/unreal_4.24.toml diff --git a/pype/configurations/defaults/launchers/windows/blender_2.80.bat b/pype/configurations/defaults/studio_configurations/launchers/windows/blender_2.80.bat similarity index 100% rename from pype/configurations/defaults/launchers/windows/blender_2.80.bat rename to pype/configurations/defaults/studio_configurations/launchers/windows/blender_2.80.bat diff --git a/pype/configurations/defaults/launchers/windows/blender_2.81.bat b/pype/configurations/defaults/studio_configurations/launchers/windows/blender_2.81.bat similarity index 100% rename from pype/configurations/defaults/launchers/windows/blender_2.81.bat rename to pype/configurations/defaults/studio_configurations/launchers/windows/blender_2.81.bat diff --git a/pype/configurations/defaults/launchers/windows/blender_2.82.bat b/pype/configurations/defaults/studio_configurations/launchers/windows/blender_2.82.bat similarity index 100% rename from pype/configurations/defaults/launchers/windows/blender_2.82.bat rename to pype/configurations/defaults/studio_configurations/launchers/windows/blender_2.82.bat diff --git a/pype/configurations/defaults/launchers/windows/blender_2.83.bat b/pype/configurations/defaults/studio_configurations/launchers/windows/blender_2.83.bat similarity index 100% rename from pype/configurations/defaults/launchers/windows/blender_2.83.bat rename to pype/configurations/defaults/studio_configurations/launchers/windows/blender_2.83.bat diff --git a/pype/configurations/defaults/launchers/windows/celaction_local.bat b/pype/configurations/defaults/studio_configurations/launchers/windows/celaction_local.bat similarity index 100% rename from pype/configurations/defaults/launchers/windows/celaction_local.bat rename to pype/configurations/defaults/studio_configurations/launchers/windows/celaction_local.bat diff --git a/pype/configurations/defaults/launchers/windows/celaction_publish.bat b/pype/configurations/defaults/studio_configurations/launchers/windows/celaction_publish.bat similarity index 100% rename from pype/configurations/defaults/launchers/windows/celaction_publish.bat rename to pype/configurations/defaults/studio_configurations/launchers/windows/celaction_publish.bat diff --git a/pype/configurations/defaults/launchers/windows/harmony_17.bat b/pype/configurations/defaults/studio_configurations/launchers/windows/harmony_17.bat similarity index 100% rename from pype/configurations/defaults/launchers/windows/harmony_17.bat rename to pype/configurations/defaults/studio_configurations/launchers/windows/harmony_17.bat diff --git a/pype/configurations/defaults/launchers/windows/houdini_16.bat b/pype/configurations/defaults/studio_configurations/launchers/windows/houdini_16.bat similarity index 100% rename from pype/configurations/defaults/launchers/windows/houdini_16.bat rename to pype/configurations/defaults/studio_configurations/launchers/windows/houdini_16.bat diff --git a/pype/configurations/defaults/launchers/windows/houdini_17.bat b/pype/configurations/defaults/studio_configurations/launchers/windows/houdini_17.bat similarity index 100% rename from pype/configurations/defaults/launchers/windows/houdini_17.bat rename to pype/configurations/defaults/studio_configurations/launchers/windows/houdini_17.bat diff --git a/pype/configurations/defaults/launchers/windows/houdini_18.bat b/pype/configurations/defaults/studio_configurations/launchers/windows/houdini_18.bat similarity index 100% rename from pype/configurations/defaults/launchers/windows/houdini_18.bat rename to pype/configurations/defaults/studio_configurations/launchers/windows/houdini_18.bat diff --git a/pype/configurations/defaults/launchers/windows/maya2016.bat b/pype/configurations/defaults/studio_configurations/launchers/windows/maya2016.bat similarity index 100% rename from pype/configurations/defaults/launchers/windows/maya2016.bat rename to pype/configurations/defaults/studio_configurations/launchers/windows/maya2016.bat diff --git a/pype/configurations/defaults/launchers/windows/maya2017.bat b/pype/configurations/defaults/studio_configurations/launchers/windows/maya2017.bat similarity index 100% rename from pype/configurations/defaults/launchers/windows/maya2017.bat rename to pype/configurations/defaults/studio_configurations/launchers/windows/maya2017.bat diff --git a/pype/configurations/defaults/launchers/windows/maya2018.bat b/pype/configurations/defaults/studio_configurations/launchers/windows/maya2018.bat similarity index 100% rename from pype/configurations/defaults/launchers/windows/maya2018.bat rename to pype/configurations/defaults/studio_configurations/launchers/windows/maya2018.bat diff --git a/pype/configurations/defaults/launchers/windows/maya2019.bat b/pype/configurations/defaults/studio_configurations/launchers/windows/maya2019.bat similarity index 100% rename from pype/configurations/defaults/launchers/windows/maya2019.bat rename to pype/configurations/defaults/studio_configurations/launchers/windows/maya2019.bat diff --git a/pype/configurations/defaults/launchers/windows/maya2020.bat b/pype/configurations/defaults/studio_configurations/launchers/windows/maya2020.bat similarity index 100% rename from pype/configurations/defaults/launchers/windows/maya2020.bat rename to pype/configurations/defaults/studio_configurations/launchers/windows/maya2020.bat diff --git a/pype/configurations/defaults/launchers/windows/mayabatch2019.bat b/pype/configurations/defaults/studio_configurations/launchers/windows/mayabatch2019.bat similarity index 100% rename from pype/configurations/defaults/launchers/windows/mayabatch2019.bat rename to pype/configurations/defaults/studio_configurations/launchers/windows/mayabatch2019.bat diff --git a/pype/configurations/defaults/launchers/windows/mayabatch2020.bat b/pype/configurations/defaults/studio_configurations/launchers/windows/mayabatch2020.bat similarity index 100% rename from pype/configurations/defaults/launchers/windows/mayabatch2020.bat rename to pype/configurations/defaults/studio_configurations/launchers/windows/mayabatch2020.bat diff --git a/pype/configurations/defaults/launchers/windows/mayapy2016.bat b/pype/configurations/defaults/studio_configurations/launchers/windows/mayapy2016.bat similarity index 100% rename from pype/configurations/defaults/launchers/windows/mayapy2016.bat rename to pype/configurations/defaults/studio_configurations/launchers/windows/mayapy2016.bat diff --git a/pype/configurations/defaults/launchers/windows/mayapy2017.bat b/pype/configurations/defaults/studio_configurations/launchers/windows/mayapy2017.bat similarity index 100% rename from pype/configurations/defaults/launchers/windows/mayapy2017.bat rename to pype/configurations/defaults/studio_configurations/launchers/windows/mayapy2017.bat diff --git a/pype/configurations/defaults/launchers/windows/mayapy2018.bat b/pype/configurations/defaults/studio_configurations/launchers/windows/mayapy2018.bat similarity index 100% rename from pype/configurations/defaults/launchers/windows/mayapy2018.bat rename to pype/configurations/defaults/studio_configurations/launchers/windows/mayapy2018.bat diff --git a/pype/configurations/defaults/launchers/windows/mayapy2019.bat b/pype/configurations/defaults/studio_configurations/launchers/windows/mayapy2019.bat similarity index 100% rename from pype/configurations/defaults/launchers/windows/mayapy2019.bat rename to pype/configurations/defaults/studio_configurations/launchers/windows/mayapy2019.bat diff --git a/pype/configurations/defaults/launchers/windows/mayapy2020.bat b/pype/configurations/defaults/studio_configurations/launchers/windows/mayapy2020.bat similarity index 100% rename from pype/configurations/defaults/launchers/windows/mayapy2020.bat rename to pype/configurations/defaults/studio_configurations/launchers/windows/mayapy2020.bat diff --git a/pype/configurations/defaults/launchers/windows/nuke10.0.bat b/pype/configurations/defaults/studio_configurations/launchers/windows/nuke10.0.bat similarity index 100% rename from pype/configurations/defaults/launchers/windows/nuke10.0.bat rename to pype/configurations/defaults/studio_configurations/launchers/windows/nuke10.0.bat diff --git a/pype/configurations/defaults/launchers/windows/nuke11.0.bat b/pype/configurations/defaults/studio_configurations/launchers/windows/nuke11.0.bat similarity index 100% rename from pype/configurations/defaults/launchers/windows/nuke11.0.bat rename to pype/configurations/defaults/studio_configurations/launchers/windows/nuke11.0.bat diff --git a/pype/configurations/defaults/launchers/windows/nuke11.2.bat b/pype/configurations/defaults/studio_configurations/launchers/windows/nuke11.2.bat similarity index 100% rename from pype/configurations/defaults/launchers/windows/nuke11.2.bat rename to pype/configurations/defaults/studio_configurations/launchers/windows/nuke11.2.bat diff --git a/pype/configurations/defaults/launchers/windows/nuke11.3.bat b/pype/configurations/defaults/studio_configurations/launchers/windows/nuke11.3.bat similarity index 100% rename from pype/configurations/defaults/launchers/windows/nuke11.3.bat rename to pype/configurations/defaults/studio_configurations/launchers/windows/nuke11.3.bat diff --git a/pype/configurations/defaults/launchers/windows/nuke12.0.bat b/pype/configurations/defaults/studio_configurations/launchers/windows/nuke12.0.bat similarity index 100% rename from pype/configurations/defaults/launchers/windows/nuke12.0.bat rename to pype/configurations/defaults/studio_configurations/launchers/windows/nuke12.0.bat diff --git a/pype/configurations/defaults/launchers/windows/nukestudio10.0.bat b/pype/configurations/defaults/studio_configurations/launchers/windows/nukestudio10.0.bat similarity index 100% rename from pype/configurations/defaults/launchers/windows/nukestudio10.0.bat rename to pype/configurations/defaults/studio_configurations/launchers/windows/nukestudio10.0.bat diff --git a/pype/configurations/defaults/launchers/windows/nukestudio11.0.bat b/pype/configurations/defaults/studio_configurations/launchers/windows/nukestudio11.0.bat similarity index 100% rename from pype/configurations/defaults/launchers/windows/nukestudio11.0.bat rename to pype/configurations/defaults/studio_configurations/launchers/windows/nukestudio11.0.bat diff --git a/pype/configurations/defaults/launchers/windows/nukestudio11.2.bat b/pype/configurations/defaults/studio_configurations/launchers/windows/nukestudio11.2.bat similarity index 100% rename from pype/configurations/defaults/launchers/windows/nukestudio11.2.bat rename to pype/configurations/defaults/studio_configurations/launchers/windows/nukestudio11.2.bat diff --git a/pype/configurations/defaults/launchers/windows/nukestudio11.3.bat b/pype/configurations/defaults/studio_configurations/launchers/windows/nukestudio11.3.bat similarity index 100% rename from pype/configurations/defaults/launchers/windows/nukestudio11.3.bat rename to pype/configurations/defaults/studio_configurations/launchers/windows/nukestudio11.3.bat diff --git a/pype/configurations/defaults/launchers/windows/nukestudio12.0.bat b/pype/configurations/defaults/studio_configurations/launchers/windows/nukestudio12.0.bat similarity index 100% rename from pype/configurations/defaults/launchers/windows/nukestudio12.0.bat rename to pype/configurations/defaults/studio_configurations/launchers/windows/nukestudio12.0.bat diff --git a/pype/configurations/defaults/launchers/windows/nukex10.0.bat b/pype/configurations/defaults/studio_configurations/launchers/windows/nukex10.0.bat similarity index 100% rename from pype/configurations/defaults/launchers/windows/nukex10.0.bat rename to pype/configurations/defaults/studio_configurations/launchers/windows/nukex10.0.bat diff --git a/pype/configurations/defaults/launchers/windows/nukex11.0.bat b/pype/configurations/defaults/studio_configurations/launchers/windows/nukex11.0.bat similarity index 100% rename from pype/configurations/defaults/launchers/windows/nukex11.0.bat rename to pype/configurations/defaults/studio_configurations/launchers/windows/nukex11.0.bat diff --git a/pype/configurations/defaults/launchers/windows/nukex11.2.bat b/pype/configurations/defaults/studio_configurations/launchers/windows/nukex11.2.bat similarity index 100% rename from pype/configurations/defaults/launchers/windows/nukex11.2.bat rename to pype/configurations/defaults/studio_configurations/launchers/windows/nukex11.2.bat diff --git a/pype/configurations/defaults/launchers/windows/nukex11.3.bat b/pype/configurations/defaults/studio_configurations/launchers/windows/nukex11.3.bat similarity index 100% rename from pype/configurations/defaults/launchers/windows/nukex11.3.bat rename to pype/configurations/defaults/studio_configurations/launchers/windows/nukex11.3.bat diff --git a/pype/configurations/defaults/launchers/windows/nukex12.0.bat b/pype/configurations/defaults/studio_configurations/launchers/windows/nukex12.0.bat similarity index 100% rename from pype/configurations/defaults/launchers/windows/nukex12.0.bat rename to pype/configurations/defaults/studio_configurations/launchers/windows/nukex12.0.bat diff --git a/pype/configurations/defaults/launchers/windows/photoshop_2020.bat b/pype/configurations/defaults/studio_configurations/launchers/windows/photoshop_2020.bat similarity index 100% rename from pype/configurations/defaults/launchers/windows/photoshop_2020.bat rename to pype/configurations/defaults/studio_configurations/launchers/windows/photoshop_2020.bat diff --git a/pype/configurations/defaults/launchers/windows/premiere_pro_2019.bat b/pype/configurations/defaults/studio_configurations/launchers/windows/premiere_pro_2019.bat similarity index 100% rename from pype/configurations/defaults/launchers/windows/premiere_pro_2019.bat rename to pype/configurations/defaults/studio_configurations/launchers/windows/premiere_pro_2019.bat diff --git a/pype/configurations/defaults/launchers/windows/premiere_pro_2020.bat b/pype/configurations/defaults/studio_configurations/launchers/windows/premiere_pro_2020.bat similarity index 100% rename from pype/configurations/defaults/launchers/windows/premiere_pro_2020.bat rename to pype/configurations/defaults/studio_configurations/launchers/windows/premiere_pro_2020.bat diff --git a/pype/configurations/defaults/launchers/windows/python3.bat b/pype/configurations/defaults/studio_configurations/launchers/windows/python3.bat similarity index 100% rename from pype/configurations/defaults/launchers/windows/python3.bat rename to pype/configurations/defaults/studio_configurations/launchers/windows/python3.bat diff --git a/pype/configurations/defaults/launchers/windows/resolve_16.bat b/pype/configurations/defaults/studio_configurations/launchers/windows/resolve_16.bat similarity index 100% rename from pype/configurations/defaults/launchers/windows/resolve_16.bat rename to pype/configurations/defaults/studio_configurations/launchers/windows/resolve_16.bat diff --git a/pype/configurations/defaults/launchers/windows/shell.bat b/pype/configurations/defaults/studio_configurations/launchers/windows/shell.bat similarity index 100% rename from pype/configurations/defaults/launchers/windows/shell.bat rename to pype/configurations/defaults/studio_configurations/launchers/windows/shell.bat diff --git a/pype/configurations/defaults/launchers/windows/storyboardpro_7.bat b/pype/configurations/defaults/studio_configurations/launchers/windows/storyboardpro_7.bat similarity index 100% rename from pype/configurations/defaults/launchers/windows/storyboardpro_7.bat rename to pype/configurations/defaults/studio_configurations/launchers/windows/storyboardpro_7.bat diff --git a/pype/configurations/defaults/launchers/windows/unreal.bat b/pype/configurations/defaults/studio_configurations/launchers/windows/unreal.bat similarity index 100% rename from pype/configurations/defaults/launchers/windows/unreal.bat rename to pype/configurations/defaults/studio_configurations/launchers/windows/unreal.bat From 7d7a2e1b230584d4cc99b126ac155c4434f408c0 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 9 Sep 2020 10:37:11 +0200 Subject: [PATCH 468/813] initial commit to save studio configurations to different place --- pype/configurations/config.py | 43 +++++++++++++------ .../config_setting/widgets/base.py | 3 +- 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/pype/configurations/config.py b/pype/configurations/config.py index 416704649c..147570acd4 100644 --- a/pype/configurations/config.py +++ b/pype/configurations/config.py @@ -5,18 +5,37 @@ import copy log = logging.getLogger(__name__) -STUDIO_PRESETS_PATH = os.path.normpath( - os.path.join(os.environ["PYPE_CONFIG"], "studio_configurations") +# Metadata keys for work with studio and project overrides +OVERRIDEN_KEY = "__overriden_keys__" +# NOTE key popping not implemented yet +POP_KEY = "__pop_key__" + +# Paths to studio and project overrides +STUDIO_OVERRIDES_PATH = os.environ["PYPE_CONFIG"] + +SYSTEM_CONFIGURATIONS_DIR = "studio_configurations" +SYSTEM_CONFIGURATIONS_PATH = os.path.join( + STUDIO_OVERRIDES_PATH, SYSTEM_CONFIGURATIONS_DIR ) -PROJECT_CONFIGURATION_DIR = "project_configurations" -PROJECT_PRESETS_PATH = os.path.normpath(os.path.join( - os.environ["PYPE_CONFIG"], PROJECT_CONFIGURATION_DIR -)) +PROJECT_CONFIGURATIONS_DIR = "project_configurations" +PROJECT_CONFIGURATIONS_PATH = os.path.join( + STUDIO_OVERRIDES_PATH, PROJECT_CONFIGURATIONS_DIR +) + +# Variable where cache of default configurations are stored +_DEFAULT_CONFIGURATIONS = None + +# TODO remove this as is maybe deprecated first_run = False -# TODO key popping not implemented yet -POP_KEY = "__pop_key__" -OVERRIDEN_KEY = "__overriden_keys__" + +def default_configuration(): + global _DEFAULT_CONFIGURATIONS + if _DEFAULT_CONFIGURATIONS is None: + current_dir = os.path.dirname(__file__) + defaults_path = os.path.join(current_dir, "defaults") + _DEFAULT_CONFIGURATIONS = load_jsons_from_dir(defaults_path) + return _DEFAULT_CONFIGURATIONS def load_json(fpath): @@ -129,17 +148,17 @@ def load_jsons_from_dir(path, *args, **kwargs): def studio_configurations(*args, **kwargs): - return load_jsons_from_dir(STUDIO_PRESETS_PATH, *args, **kwargs) + return load_jsons_from_dir(SYSTEM_CONFIGURATIONS_PATH, *args, **kwargs) def global_project_configurations(**kwargs): - return load_jsons_from_dir(PROJECT_PRESETS_PATH, **kwargs) + return load_jsons_from_dir(PROJECT_CONFIGURATIONS_PATH, **kwargs) def path_to_project_overrides(project_name): project_configs_path = os.environ["PYPE_PROJECT_CONFIGS"] dirpath = os.path.join(project_configs_path, project_name) - return os.path.join(dirpath, PROJECT_CONFIGURATION_DIR + ".json") + return os.path.join(dirpath, PROJECT_CONFIGURATIONS_DIR + ".json") def project_configurations_overrides(project_name, **kwargs): diff --git a/pype/tools/config_setting/config_setting/widgets/base.py b/pype/tools/config_setting/config_setting/widgets/base.py index d39b5789d9..0660ab1e06 100644 --- a/pype/tools/config_setting/config_setting/widgets/base.py +++ b/pype/tools/config_setting/config_setting/widgets/base.py @@ -156,7 +156,7 @@ class SystemWidget(QtWidgets.QWidget): for key in key_sequence: new_values = new_values[key] origin_values.update(new_values) - + raise NotImplementedError("Output from global values has changed") output_path = os.path.join( config.STUDIO_PRESETS_PATH, subpath ) @@ -537,6 +537,7 @@ class ProjectWidget(QtWidgets.QWidget): else: origin_values = new_values + raise NotImplementedError("Output from global values has changed") output_path = os.path.join( config.PROJECT_PRESETS_PATH, subpath ) From 4fabd706e820d45755608b7605c32741ab4da5a6 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 9 Sep 2020 10:54:16 +0200 Subject: [PATCH 469/813] renamed studio to system --- .../studio_schema/0_studio_gui_schema.json | 3 +-- .../config_setting/widgets/base.py | 16 ++++++++-------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/0_studio_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/0_studio_gui_schema.json index bde340250e..de17328860 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/0_studio_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/0_studio_gui_schema.json @@ -1,7 +1,6 @@ { - "key": "studio", + "key": "system", "type": "dict-invisible", - "label": "Studio", "children": [ { "type": "dict-invisible", diff --git a/pype/tools/config_setting/config_setting/widgets/base.py b/pype/tools/config_setting/config_setting/widgets/base.py index 0660ab1e06..3d255b416e 100644 --- a/pype/tools/config_setting/config_setting/widgets/base.py +++ b/pype/tools/config_setting/config_setting/widgets/base.py @@ -95,7 +95,7 @@ class SystemWidget(QtWidgets.QWidget): self.schema = lib.gui_schema("studio_schema", "0_studio_gui_schema") self.keys = self.schema.get("keys", []) self.add_children_gui(self.schema) - self._update_global_values() + self._update_values() self.hierarchical_style_update() def _save(self): @@ -132,7 +132,7 @@ class SystemWidget(QtWidgets.QWidget): all_values = _all_values # Skip first key - all_values = all_values["studio"] + all_values = all_values["system"] # Load studio data with metadata current_configurations = config.studio_configurations() @@ -168,10 +168,10 @@ class SystemWidget(QtWidgets.QWidget): with open(output_path, "w") as file_stream: json.dump(origin_values, file_stream, indent=4) - self._update_global_values() + self._update_values() - def _update_global_values(self): - values = {"studio": config.studio_configurations()} + def _update_values(self): + values = {"system": config.studio_configurations()} for input_field in self.input_fields: input_field.update_global_values(values) @@ -408,7 +408,7 @@ class ProjectWidget(QtWidgets.QWidget): self.schema = lib.gui_schema("projects_schema", "0_project_gui_schema") self.keys = self.schema.get("keys", []) self.add_children_gui(self.schema) - self._update_global_values() + self._update_values() self.hierarchical_style_update() def add_children_gui(self, child_configuration): @@ -549,9 +549,9 @@ class ProjectWidget(QtWidgets.QWidget): with open(output_path, "w") as file_stream: json.dump(origin_values, file_stream, indent=4) - self._update_global_values() + self._update_values() - def _update_global_values(self): + def _update_values(self): values = {"project": config.global_project_configurations()} for input_field in self.input_fields: input_field.update_global_values(values) From 8ad8b00713e15556b346c75a31b38df2fb18a2f6 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 9 Sep 2020 10:55:00 +0200 Subject: [PATCH 470/813] renamed also files and folder from studio to system --- .../0_system_gui_schema.json} | 0 .../1_applications_gui_schema.json | 0 .../{studio_schema => system_schema}/1_intents_gui_schema.json | 0 .../{studio_schema => system_schema}/1_tools_gui_schema.json | 0 .../{studio_schema => system_schema}/1_tray_items.json | 0 pype/tools/config_setting/config_setting/widgets/base.py | 3 ++- 6 files changed, 2 insertions(+), 1 deletion(-) rename pype/tools/config_setting/config_setting/config_gui_schema/{studio_schema/0_studio_gui_schema.json => system_schema/0_system_gui_schema.json} (100%) rename pype/tools/config_setting/config_setting/config_gui_schema/{studio_schema => system_schema}/1_applications_gui_schema.json (100%) rename pype/tools/config_setting/config_setting/config_gui_schema/{studio_schema => system_schema}/1_intents_gui_schema.json (100%) rename pype/tools/config_setting/config_setting/config_gui_schema/{studio_schema => system_schema}/1_tools_gui_schema.json (100%) rename pype/tools/config_setting/config_setting/config_gui_schema/{studio_schema => system_schema}/1_tray_items.json (100%) diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/0_studio_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/0_system_gui_schema.json similarity index 100% rename from pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/0_studio_gui_schema.json rename to pype/tools/config_setting/config_setting/config_gui_schema/system_schema/0_system_gui_schema.json diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_applications_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/1_applications_gui_schema.json similarity index 100% rename from pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_applications_gui_schema.json rename to pype/tools/config_setting/config_setting/config_gui_schema/system_schema/1_applications_gui_schema.json diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_intents_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/1_intents_gui_schema.json similarity index 100% rename from pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_intents_gui_schema.json rename to pype/tools/config_setting/config_setting/config_gui_schema/system_schema/1_intents_gui_schema.json diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_tools_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/1_tools_gui_schema.json similarity index 100% rename from pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_tools_gui_schema.json rename to pype/tools/config_setting/config_setting/config_gui_schema/system_schema/1_tools_gui_schema.json diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_tray_items.json b/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/1_tray_items.json similarity index 100% rename from pype/tools/config_setting/config_setting/config_gui_schema/studio_schema/1_tray_items.json rename to pype/tools/config_setting/config_setting/config_gui_schema/system_schema/1_tray_items.json diff --git a/pype/tools/config_setting/config_setting/widgets/base.py b/pype/tools/config_setting/config_setting/widgets/base.py index 3d255b416e..e5bbe6d929 100644 --- a/pype/tools/config_setting/config_setting/widgets/base.py +++ b/pype/tools/config_setting/config_setting/widgets/base.py @@ -92,7 +92,7 @@ class SystemWidget(QtWidgets.QWidget): widget.deleteLater() self.input_fields.clear() - self.schema = lib.gui_schema("studio_schema", "0_studio_gui_schema") + self.schema = lib.gui_schema("system_schema", "0_system_gui_schema") self.keys = self.schema.get("keys", []) self.add_children_gui(self.schema) self._update_values() @@ -171,6 +171,7 @@ class SystemWidget(QtWidgets.QWidget): self._update_values() def _update_values(self): + config.default_configuration() values = {"system": config.studio_configurations()} for input_field in self.input_fields: input_field.update_global_values(values) From d2f25b0a0cce496f4fbcbceef2bf4ff30b5fdd58 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 9 Sep 2020 10:59:52 +0200 Subject: [PATCH 471/813] is_file removed from schemas --- .../projects_schema/0_project_gui_schema.json | 3 +-- .../1_ftrack_projects_gui_schema.json | 2 -- .../projects_schema/1_plugins_gui_schema.json | 15 ++------------- .../system_schema/0_system_gui_schema.json | 1 - .../system_schema/1_applications_gui_schema.json | 1 - .../system_schema/1_intents_gui_schema.json | 1 - .../system_schema/1_tools_gui_schema.json | 1 - .../system_schema/1_tray_items.json | 1 - 8 files changed, 3 insertions(+), 22 deletions(-) diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/0_project_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/0_project_gui_schema.json index 91bacf2e5a..86504eefd2 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/0_project_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/0_project_gui_schema.json @@ -4,8 +4,7 @@ "children": [ { "type": "anatomy", - "key": "anatomy", - "is_file": true + "key": "anatomy" }, { "type": "schema", "children": [ diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_ftrack_projects_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_ftrack_projects_gui_schema.json index 6608463100..e9937e64b8 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_ftrack_projects_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_ftrack_projects_gui_schema.json @@ -10,7 +10,6 @@ "expandable": true, "label": "Status updates", "is_group": true, - "is_file": true, "children": [ { "key": "Ready", @@ -28,7 +27,6 @@ "expandable": true, "label": "Version status to Task status", "is_group": true, - "is_file": true, "children": [ { "key": "in progress", diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json index 98fbfb206d..302d1dad0a 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json @@ -15,7 +15,6 @@ "expandable": true, "key": "publish", "label": "Publish plugins", - "is_file": true, "children": [ { "type": "dict", @@ -82,7 +81,6 @@ "expandable": true, "key": "publish", "label": "Publish plugins", - "is_file": true, "children": [ { "type": "dict", @@ -125,7 +123,6 @@ "expandable": true, "key": "publish", "label": "Publish plugins", - "is_file": true, "children": [ { "type": "dict", @@ -299,7 +296,6 @@ "expandable": true, "key": "publish", "label": "Publish plugins", - "is_file": true, "children": [ { "type": "dict", @@ -376,8 +372,7 @@ }, { "type": "raw-json", "key": "workfile_build", - "label": "Workfile Build logic", - "is_file": true + "label": "Workfile Build logic" } ] }, { @@ -391,7 +386,6 @@ "expandable": true, "key": "create", "label": "Create plugins", - "is_file": true, "children": [ { "type": "dict", @@ -428,7 +422,6 @@ "expandable": true, "key": "publish", "label": "Publish plugins", - "is_file": true, "children": [ { "type": "dict", @@ -551,8 +544,7 @@ }, { "type": "raw-json", "key": "workfile_build", - "label": "Workfile Build logic", - "is_file": true + "label": "Workfile Build logic" } ] }, { @@ -566,7 +558,6 @@ "expandable": true, "key": "publish", "label": "Publish plugins", - "is_file": true, "children": [ { "type": "dict", @@ -619,7 +610,6 @@ "expandable": true, "key": "create", "label": "Creator plugins", - "is_file": true, "children": [ { "type": "dict", @@ -661,7 +651,6 @@ "expandable": true, "key": "publish", "label": "Publish plugins", - "is_file": true, "children": [] } ] diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/0_system_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/0_system_gui_schema.json index de17328860..0b51267f4e 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/0_system_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/0_system_gui_schema.json @@ -26,7 +26,6 @@ "maximum": 300 }, "is_group": true, - "is_file": true, "key": "templates_mapping", "label": "Muster - Templates mapping" }] diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/1_applications_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/1_applications_gui_schema.json index bc2c9f239c..af128b138f 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/1_applications_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/1_applications_gui_schema.json @@ -3,7 +3,6 @@ "type": "dict", "label": "Applications", "expandable": true, - "is_file": true, "is_group": true, "children": [ { diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/1_intents_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/1_intents_gui_schema.json index a4b5e16fa1..11b45c8409 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/1_intents_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/1_intents_gui_schema.json @@ -4,7 +4,6 @@ "label": "Intent Setting", "expandable": true, "is_group": true, - "is_file": true, "children": [ { "type": "dict-modifiable", diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/1_tools_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/1_tools_gui_schema.json index bf35326d1c..a16f00547e 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/1_tools_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/1_tools_gui_schema.json @@ -4,7 +4,6 @@ "label": "Tools", "expandable": true, "is_group": true, - "is_file": true, "children": [ { "type": "dict-form", diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/1_tray_items.json b/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/1_tray_items.json index 13ab0293de..afa509254e 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/1_tray_items.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/1_tray_items.json @@ -3,7 +3,6 @@ "type": "dict", "label": "Modules", "expandable": true, - "is_file": true, "is_group": true, "children": [ { From 0c256fa30a16934cbc8a08da1841086abe1ca5eb Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 9 Sep 2020 11:00:03 +0200 Subject: [PATCH 472/813] skipped validation of is_file attribute --- .../config_setting/widgets/lib.py | 69 ------------------- 1 file changed, 69 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/lib.py b/pype/tools/config_setting/config_setting/widgets/lib.py index 08b0dfc3c4..b32fb734c6 100644 --- a/pype/tools/config_setting/config_setting/widgets/lib.py +++ b/pype/tools/config_setting/config_setting/widgets/lib.py @@ -81,19 +81,6 @@ def replace_inner_schemas(schema_data, schema_collection): return schema_data -class SchemaMissingFileInfo(Exception): - def __init__(self, invalid): - full_path_keys = [] - for item in invalid: - full_path_keys.append("\"{}\"".format("/".join(item))) - - msg = ( - "Schema has missing definition of output file (\"is_file\" key)" - " for keys. [{}]" - ).format(", ".join(full_path_keys)) - super(SchemaMissingFileInfo, self).__init__(msg) - - class SchemeGroupHierarchyBug(Exception): def __init__(self, invalid): full_path_keys = [] @@ -122,59 +109,6 @@ class SchemaDuplicatedKeys(Exception): super(SchemaDuplicatedKeys, self).__init__(msg) -def file_keys_from_schema(schema_data): - output = [] - keys = [] - key = schema_data.get("key") - if key: - keys.append(key) - - for child in schema_data["children"]: - if child.get("is_file"): - _keys = copy.deepcopy(keys) - _keys.append(child["key"]) - output.append(_keys) - continue - - for result in file_keys_from_schema(child): - _keys = copy.deepcopy(keys) - _keys.extend(result) - output.append(_keys) - return output - - -def validate_all_has_ending_file(schema_data, is_top=True): - if schema_data.get("is_file"): - return None - - children = schema_data.get("children") - if not children: - return [[schema_data["key"]]] - - invalid = [] - keyless = "key" not in schema_data - for child in children: - result = validate_all_has_ending_file(child, False) - if result is None: - continue - - if keyless: - invalid.extend(result) - else: - for item in result: - new_invalid = [schema_data["key"]] - new_invalid.extend(item) - invalid.append(new_invalid) - - if not invalid: - return None - - if not is_top: - return invalid - - raise SchemaMissingFileInfo(invalid) - - def validate_is_group_is_unique_in_hierarchy( schema_data, any_parent_is_group=False, keys=None ): @@ -270,9 +204,6 @@ def validate_keys_are_unique(schema_data, keys=None): def validate_schema(schema_data): - # TODO validator that is_group key is not before is_file child - # TODO validator that is_group or is_file is not on child without key - validate_all_has_ending_file(schema_data) validate_is_group_is_unique_in_hierarchy(schema_data) validate_keys_are_unique(schema_data) From a338b8c04e0e8c5445d1f0ca4533e7e97a66b8e4 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 9 Sep 2020 11:20:34 +0200 Subject: [PATCH 473/813] renamed function for inner schemas --- pype/tools/config_setting/config_setting/widgets/lib.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/lib.py b/pype/tools/config_setting/config_setting/widgets/lib.py index b32fb734c6..69c3259b3b 100644 --- a/pype/tools/config_setting/config_setting/widgets/lib.py +++ b/pype/tools/config_setting/config_setting/widgets/lib.py @@ -55,7 +55,7 @@ def convert_overrides_to_gui_data(data, first=True): return output -def replace_inner_schemas(schema_data, schema_collection): +def _fill_inner_schemas(schema_data, schema_collection): if schema_data["type"] == "schema": raise ValueError("First item in schema data can't be schema.") @@ -66,12 +66,12 @@ def replace_inner_schemas(schema_data, schema_collection): new_children = [] for child in children: if child["type"] != "schema": - new_child = replace_inner_schemas(child, schema_collection) + new_child = _fill_inner_schemas(child, schema_collection) new_children.append(new_child) continue for schema_name in child["children"]: - new_child = replace_inner_schemas( + new_child = _fill_inner_schemas( schema_collection[schema_name], schema_collection ) @@ -227,7 +227,7 @@ def gui_schema(subfolder, main_schema_name): schema_data = json.load(json_stream) loaded_schemas[basename] = schema_data - main_schema = replace_inner_schemas( + main_schema = _fill_inner_schemas( loaded_schemas[main_schema_name], loaded_schemas ) From afcd282ada6c5c8e3c0f1abe50bd800c04cad0ae Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 9 Sep 2020 11:20:50 +0200 Subject: [PATCH 474/813] ConfigObject has is from defaults --- pype/tools/config_setting/config_setting/widgets/inputs.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index f975567a6a..fed9c6b5c7 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -16,6 +16,7 @@ class ConfigObject(AbstractConfigObject): default_state = "" + _is_from_defaults = True _as_widget = False _is_overriden = False _is_modified = False @@ -32,6 +33,10 @@ class ConfigObject(AbstractConfigObject): self._log = logging.getLogger(self.__class__.__name__) return self._log + @property + def is_from_defaults(self): + return self._is_from_defaults + @property def is_modified(self): """Has object any changes that require saving.""" From 7198576e59c0f3ca487f3972a78f017024d8b724 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 9 Sep 2020 11:39:02 +0200 Subject: [PATCH 475/813] reverse logic of defaults, better is to know if has studi ooverrides --- pype/tools/config_setting/config_setting/widgets/inputs.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index fed9c6b5c7..14cf573398 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -16,7 +16,7 @@ class ConfigObject(AbstractConfigObject): default_state = "" - _is_from_defaults = True + _has_studio_override = True _as_widget = False _is_overriden = False _is_modified = False @@ -34,8 +34,8 @@ class ConfigObject(AbstractConfigObject): return self._log @property - def is_from_defaults(self): - return self._is_from_defaults + def has_studio_override(self): + return self._has_studio_override @property def is_modified(self): From 918d592777d41a5e87617acd5562cb4b3a80fcf1 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 9 Sep 2020 11:42:08 +0200 Subject: [PATCH 476/813] renamed global_value to studio_value --- .../config_setting/widgets/anatomy_inputs.py | 20 ++-- .../config_setting/widgets/base.py | 4 +- .../config_setting/widgets/inputs.py | 106 +++++++++--------- 3 files changed, 65 insertions(+), 65 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index e59de3980f..957691cbdc 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -75,7 +75,7 @@ class AnatomyWidget(QtWidgets.QWidget, ConfigObject): self.root_widget.value_changed.connect(self._on_value_change) - def update_global_values(self, parent_values): + def update_studio_values(self, parent_values): self._state = None self._child_state = None @@ -84,8 +84,8 @@ class AnatomyWidget(QtWidgets.QWidget, ConfigObject): else: value = NOT_SET - self.root_widget.update_global_values(value) - self.templates_widget.update_global_values(value) + self.root_widget.update_studio_values(value) + self.templates_widget.update_studio_values(value) def apply_overrides(self, parent_values): # Make sure this is set to False @@ -258,7 +258,7 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): def is_multiroot(self): return self.multiroot_checkbox.isChecked() - def update_global_values(self, parent_values): + def update_studio_values(self, parent_values): self._state = None self._multiroot_state = None @@ -279,11 +279,11 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): self.set_multiroot(is_multiroot) if is_multiroot: - self.singleroot_widget.update_global_values(NOT_SET) - self.multiroot_widget.update_global_values(value) + self.singleroot_widget.update_studio_values(NOT_SET) + self.multiroot_widget.update_studio_values(value) else: - self.singleroot_widget.update_global_values(value) - self.multiroot_widget.update_global_values(NOT_SET) + self.singleroot_widget.update_studio_values(value) + self.multiroot_widget.update_studio_values(NOT_SET) def apply_overrides(self, parent_values): # Make sure this is set to False @@ -489,9 +489,9 @@ class TemplatesWidget(QtWidgets.QWidget, ConfigObject): layout.addWidget(body_widget) - def update_global_values(self, values): + def update_studio_values(self, values): self._state = None - self.value_input.update_global_values(values) + self.value_input.update_studio_values(values) def apply_overrides(self, parent_values): self._state = None diff --git a/pype/tools/config_setting/config_setting/widgets/base.py b/pype/tools/config_setting/config_setting/widgets/base.py index e5bbe6d929..82a5d024b0 100644 --- a/pype/tools/config_setting/config_setting/widgets/base.py +++ b/pype/tools/config_setting/config_setting/widgets/base.py @@ -174,7 +174,7 @@ class SystemWidget(QtWidgets.QWidget): config.default_configuration() values = {"system": config.studio_configurations()} for input_field in self.input_fields: - input_field.update_global_values(values) + input_field.update_studio_values(values) for input_field in self.input_fields: input_field.hierarchical_style_update() @@ -555,7 +555,7 @@ class ProjectWidget(QtWidgets.QWidget): def _update_values(self): values = {"project": config.global_project_configurations()} for input_field in self.input_fields: - input_field.update_global_values(values) + input_field.update_studio_values(values) for input_field in self.input_fields: input_field.hierarchical_style_update() diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 14cf573398..e70b2a9b3a 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -221,7 +221,7 @@ class InputObject(ConfigObject): self.set_value(self.start_value) if not self.is_overidable: - self._is_modified = self.global_value != self.item_value() + self._is_modified = self.studio_value != self.item_value() self._is_overriden = False return @@ -273,7 +273,7 @@ class BooleanWidget(QtWidgets.QWidget, InputObject): self.default_value = input_data.get("default", NOT_SET) self.override_value = NOT_SET - self.global_value = NOT_SET + self.studio_value = NOT_SET self.start_value = NOT_SET layout = QtWidgets.QHBoxLayout(self) @@ -300,7 +300,7 @@ class BooleanWidget(QtWidgets.QWidget, InputObject): self.checkbox.stateChanged.connect(self._on_value_change) - def update_global_values(self, parent_values): + def update_studio_values(self, parent_values): value = NOT_SET if self._as_widget: value = parent_values @@ -313,10 +313,10 @@ class BooleanWidget(QtWidgets.QWidget, InputObject): elif self.default_value is not NOT_SET: self.set_value(self.default_value) - self.global_value = value + self.studio_value = value self.start_value = self.item_value() - self._is_modified = self.global_value != self.start_value + self._is_modified = self.studio_value != self.start_value def set_value(self, value): # Ignore value change because if `self.isChecked()` has same @@ -338,7 +338,7 @@ class BooleanWidget(QtWidgets.QWidget, InputObject): elif self._is_overriden: self._is_modified = self.item_value() != self.override_value else: - self._is_modified = self.item_value() != self.global_value + self._is_modified = self.item_value() != self.studio_value self.update_style() @@ -385,7 +385,7 @@ class NumberWidget(QtWidgets.QWidget, InputObject): self.default_value = input_data.get("default", NOT_SET) self.override_value = NOT_SET - self.global_value = NOT_SET + self.studio_value = NOT_SET self.start_value = NOT_SET layout = QtWidgets.QHBoxLayout(self) @@ -413,7 +413,7 @@ class NumberWidget(QtWidgets.QWidget, InputObject): self.input_field.valueChanged.connect(self._on_value_change) - def update_global_values(self, parent_values): + def update_studio_values(self, parent_values): value = NOT_SET if self._as_widget: value = parent_values @@ -427,10 +427,10 @@ class NumberWidget(QtWidgets.QWidget, InputObject): elif self.default_value is not NOT_SET: self.set_value(self.default_value) - self.global_value = value + self.studio_value = value self.start_value = self.item_value() - self._is_modified = self.global_value != self.start_value + self._is_modified = self.studio_value != self.start_value def set_value(self, value): self.input_field.setValue(value) @@ -453,7 +453,7 @@ class NumberWidget(QtWidgets.QWidget, InputObject): elif self._is_overriden: self._is_modified = self.item_value() != self.override_value else: - self._is_modified = self.item_value() != self.global_value + self._is_modified = self.item_value() != self.studio_value self.update_style() @@ -502,7 +502,7 @@ class TextWidget(QtWidgets.QWidget, InputObject): self.multiline = input_data.get("multiline", False) self.override_value = NOT_SET - self.global_value = NOT_SET + self.studio_value = NOT_SET self.start_value = NOT_SET layout = QtWidgets.QHBoxLayout(self) @@ -528,7 +528,7 @@ class TextWidget(QtWidgets.QWidget, InputObject): self.text_input.textChanged.connect(self._on_value_change) - def update_global_values(self, parent_values): + def update_studio_values(self, parent_values): value = NOT_SET if self._as_widget: value = parent_values @@ -541,10 +541,10 @@ class TextWidget(QtWidgets.QWidget, InputObject): elif self.default_value is not NOT_SET: self.set_value(self.default_value) - self.global_value = value + self.studio_value = value self.start_value = self.item_value() - self._is_modified = self.global_value != self.start_value + self._is_modified = self.studio_value != self.start_value def set_value(self, value): if self.multiline: @@ -570,7 +570,7 @@ class TextWidget(QtWidgets.QWidget, InputObject): elif self._is_overriden: self._is_modified = self.item_value() != self.override_value else: - self._is_modified = self.item_value() != self.global_value + self._is_modified = self.item_value() != self.studio_value self.update_style() @@ -620,7 +620,7 @@ class PathInputWidget(QtWidgets.QWidget, InputObject): self.default_value = input_data.get("default", NOT_SET) self.override_value = NOT_SET - self.global_value = NOT_SET + self.studio_value = NOT_SET self.start_value = NOT_SET layout = QtWidgets.QHBoxLayout(self) @@ -641,7 +641,7 @@ class PathInputWidget(QtWidgets.QWidget, InputObject): self.path_input.textChanged.connect(self._on_value_change) - def update_global_values(self, parent_values): + def update_studio_values(self, parent_values): value = NOT_SET if self._as_widget: value = parent_values @@ -654,10 +654,10 @@ class PathInputWidget(QtWidgets.QWidget, InputObject): elif self.default_value is not NOT_SET: self.set_value(self.default_value) - self.global_value = value + self.studio_value = value self.start_value = self.item_value() - self._is_modified = self.global_value != self.start_value + self._is_modified = self.studio_value != self.start_value def set_value(self, value): self.path_input.setText(value) @@ -684,7 +684,7 @@ class PathInputWidget(QtWidgets.QWidget, InputObject): elif self.is_overriden: self._is_modified = self.item_value() != self.override_value else: - self._is_modified = self.item_value() != self.global_value + self._is_modified = self.item_value() != self.studio_value self.update_style() @@ -789,7 +789,7 @@ class RawJsonWidget(QtWidgets.QWidget, InputObject): self.default_value = input_data.get("default", NOT_SET) self.override_value = NOT_SET - self.global_value = NOT_SET + self.studio_value = NOT_SET self.start_value = NOT_SET layout = QtWidgets.QVBoxLayout(self) @@ -815,7 +815,7 @@ class RawJsonWidget(QtWidgets.QWidget, InputObject): self.text_input.textChanged.connect(self._on_value_change) - def update_global_values(self, parent_values): + def update_studio_values(self, parent_values): value = NOT_SET if self._as_widget: value = parent_values @@ -830,10 +830,10 @@ class RawJsonWidget(QtWidgets.QWidget, InputObject): self._is_invalid = self.text_input.has_invalid_value() - self.global_value = value + self.studio_value = value self.start_value = self.item_value() - self._is_modified = self.global_value != self.start_value + self._is_modified = self.studio_value != self.start_value def set_value(self, value): self.text_input.set_value(value) @@ -857,7 +857,7 @@ class RawJsonWidget(QtWidgets.QWidget, InputObject): elif self._is_overriden: self._is_modified = self.item_value() != self.override_value else: - self._is_modified = self.item_value() != self.global_value + self._is_modified = self.item_value() != self.studio_value self.update_style() @@ -992,7 +992,7 @@ class ListWidget(QtWidgets.QWidget, InputObject): self.input_modifiers = input_data.get("input_modifiers") or {} self.override_value = NOT_SET - self.global_value = NOT_SET + self.studio_value = NOT_SET self.start_value = NOT_SET self.key = input_data["key"] @@ -1031,7 +1031,7 @@ class ListWidget(QtWidgets.QWidget, InputObject): def clear_value(self): self.set_value([]) - def update_global_values(self, parent_values): + def update_studio_values(self, parent_values): old_inputs = tuple(self.input_fields) value = NOT_SET @@ -1040,7 +1040,7 @@ class ListWidget(QtWidgets.QWidget, InputObject): elif parent_values is not NOT_SET: value = parent_values.get(self.key, NOT_SET) - self.global_value = value + self.studio_value = value if value is not NOT_SET: for item_value in value: @@ -1058,7 +1058,7 @@ class ListWidget(QtWidgets.QWidget, InputObject): self.start_value = self.item_value() - self._is_modified = self.global_value != self.start_value + self._is_modified = self.studio_value != self.start_value self.hierarchical_style_update() def set_value(self, value): @@ -1081,7 +1081,7 @@ class ListWidget(QtWidgets.QWidget, InputObject): elif self._is_overriden: self._is_modified = self.item_value() != self.override_value else: - self._is_modified = self.item_value() != self.global_value + self._is_modified = self.item_value() != self.studio_value self.update_style() @@ -1114,7 +1114,7 @@ class ListWidget(QtWidgets.QWidget, InputObject): # Set text if entered text is not None # else (when add button clicked) trigger `_on_value_change` if value is not None: - item_widget.value_input.update_global_values(value) + item_widget.value_input.update_studio_values(value) else: self._on_value_change() self.updateGeometry() @@ -1248,10 +1248,10 @@ class ModifiableDictItem(QtWidgets.QWidget, ConfigObject): self.update_style() self.value_changed.emit(self) - def update_global_values(self, key, value): + def update_studio_values(self, key, value): self.origin_key = key self.key_input.setText(key) - self.value_input.update_global_values(value) + self.value_input.update_studio_values(value) def apply_overrides(self, key, value): self.origin_key = key @@ -1341,7 +1341,7 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): self._as_widget = as_widget self.override_value = NOT_SET - self.global_value = NOT_SET + self.studio_value = NOT_SET self.start_value = NOT_SET any_parent_is_group = parent.is_group @@ -1397,7 +1397,7 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): def count(self): return len(self.input_fields) - def update_global_values(self, parent_values): + def update_studio_values(self, parent_values): old_inputs = tuple(self.input_fields) value = NOT_SET @@ -1406,7 +1406,7 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): elif parent_values is not NOT_SET: value = parent_values.get(self.key, NOT_SET) - self.global_value = value + self.studio_value = value if value is not NOT_SET: for item_key, item_value in value.items(): @@ -1424,7 +1424,7 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): self.start_value = self.item_value() - self._is_modified = self.global_value != self.start_value + self._is_modified = self.studio_value != self.start_value def set_value(self, value): previous_inputs = tuple(self.input_fields) @@ -1462,7 +1462,7 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): elif self._is_overriden: self._is_modified = self.item_value() != self.override_value else: - self._is_modified = self.item_value() != self.global_value + self._is_modified = self.item_value() != self.studio_value self.update_style() @@ -1534,7 +1534,7 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): if self._is_overriden: item_widget.apply_overrides(key, value) else: - item_widget.update_global_values(key, value) + item_widget.update_studio_values(key, value) self.hierarchical_style_update() else: self._on_value_change() @@ -1699,13 +1699,13 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): for item in self.input_fields: item.set_as_overriden() - def update_global_values(self, parent_values): + def update_studio_values(self, parent_values): value = NOT_SET if parent_values is not NOT_SET: value = parent_values.get(self.key, NOT_SET) for item in self.input_fields: - item.update_global_values(value) + item.update_studio_values(value) def apply_overrides(self, parent_values): # Make sure this is set to False @@ -1965,13 +1965,13 @@ class DictInvisible(QtWidgets.QWidget, ConfigObject): for item in self.input_fields: item.set_as_overriden() - def update_global_values(self, parent_values): + def update_studio_values(self, parent_values): value = NOT_SET if parent_values is not NOT_SET: value = parent_values.get(self.key, NOT_SET) for item in self.input_fields: - item.update_global_values(value) + item.update_studio_values(value) def apply_overrides(self, parent_values): # Make sure this is set to False @@ -2062,7 +2062,7 @@ class PathWidget(QtWidgets.QWidget, ConfigObject): self.multipath = input_data.get("multipath", False) self.override_value = NOT_SET - self.global_value = NOT_SET + self.studio_value = NOT_SET self.start_value = NOT_SET self.input_fields = [] @@ -2140,7 +2140,7 @@ class PathWidget(QtWidgets.QWidget, ConfigObject): self.setFocusProxy(self.input_fields[0]) self.content_layout.addWidget(proxy_widget) - def update_global_values(self, parent_values): + def update_studio_values(self, parent_values): value = NOT_SET if self._as_widget: value = parent_values @@ -2148,15 +2148,15 @@ class PathWidget(QtWidgets.QWidget, ConfigObject): value = parent_values.get(self.key, NOT_SET) if not self.multiplatform: - self.input_fields[0].update_global_values(parent_values) + self.input_fields[0].update_studio_values(parent_values) elif self.multiplatform: for input_field in self.input_fields: - input_field.update_global_values(value) + input_field.update_studio_values(value) - self.global_value = value + self.studio_value = value self.start_value = self.item_value() - self._is_modified = self.global_value != self.start_value + self._is_modified = self.studio_value != self.start_value def apply_overrides(self, parent_values): self._is_modified = False @@ -2215,7 +2215,7 @@ class PathWidget(QtWidgets.QWidget, ConfigObject): elif self._is_overriden: self._is_modified = self.item_value() != self.override_value else: - self._is_modified = self.item_value() != self.global_value + self._is_modified = self.item_value() != self.studio_value self.hierarchical_style_update() @@ -2408,9 +2408,9 @@ class DictFormWidget(QtWidgets.QWidget, ConfigObject): for item in self.input_fields: item.set_as_overriden() - def update_global_values(self, value): + def update_studio_values(self, value): for item in self.input_fields: - item.update_global_values(value) + item.update_studio_values(value) def _on_value_change(self, item=None): if self.ignore_value_changes: From 4d44eda38816b874bdb81581163dca28006d668c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 9 Sep 2020 11:47:53 +0200 Subject: [PATCH 477/813] added default_value next to studio_value and override_value --- .../config_setting/widgets/inputs.py | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index e70b2a9b3a..5ad97fb698 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -272,8 +272,9 @@ class BooleanWidget(QtWidgets.QWidget, InputObject): self._is_nullable = input_data.get("is_nullable", False) self.default_value = input_data.get("default", NOT_SET) - self.override_value = NOT_SET + self.default_value = NOT_SET self.studio_value = NOT_SET + self.override_value = NOT_SET self.start_value = NOT_SET layout = QtWidgets.QHBoxLayout(self) @@ -384,8 +385,9 @@ class NumberWidget(QtWidgets.QWidget, InputObject): self._is_nullable = input_data.get("is_nullable", False) self.default_value = input_data.get("default", NOT_SET) - self.override_value = NOT_SET + self.default_value = NOT_SET self.studio_value = NOT_SET + self.override_value = NOT_SET self.start_value = NOT_SET layout = QtWidgets.QHBoxLayout(self) @@ -501,8 +503,9 @@ class TextWidget(QtWidgets.QWidget, InputObject): self.multiline = input_data.get("multiline", False) - self.override_value = NOT_SET + self.default_value = NOT_SET self.studio_value = NOT_SET + self.override_value = NOT_SET self.start_value = NOT_SET layout = QtWidgets.QHBoxLayout(self) @@ -619,8 +622,9 @@ class PathInputWidget(QtWidgets.QWidget, InputObject): self._is_nullable = input_data.get("is_nullable", False) self.default_value = input_data.get("default", NOT_SET) - self.override_value = NOT_SET + self.default_value = NOT_SET self.studio_value = NOT_SET + self.override_value = NOT_SET self.start_value = NOT_SET layout = QtWidgets.QHBoxLayout(self) @@ -788,8 +792,9 @@ class RawJsonWidget(QtWidgets.QWidget, InputObject): self._is_nullable = input_data.get("is_nullable", False) self.default_value = input_data.get("default", NOT_SET) - self.override_value = NOT_SET + self.default_value = NOT_SET self.studio_value = NOT_SET + self.override_value = NOT_SET self.start_value = NOT_SET layout = QtWidgets.QVBoxLayout(self) @@ -991,8 +996,9 @@ class ListWidget(QtWidgets.QWidget, InputObject): self.default_value = input_data.get("default", NOT_SET) self.input_modifiers = input_data.get("input_modifiers") or {} - self.override_value = NOT_SET + self.default_value = NOT_SET self.studio_value = NOT_SET + self.override_value = NOT_SET self.start_value = NOT_SET self.key = input_data["key"] @@ -1340,8 +1346,9 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): self._state = None self._as_widget = as_widget - self.override_value = NOT_SET + self.default_value = NOT_SET self.studio_value = NOT_SET + self.override_value = NOT_SET self.start_value = NOT_SET any_parent_is_group = parent.is_group @@ -2061,8 +2068,9 @@ class PathWidget(QtWidgets.QWidget, ConfigObject): self.multiplatform = input_data.get("multiplatform", False) self.multipath = input_data.get("multipath", False) - self.override_value = NOT_SET + self.default_value = NOT_SET self.studio_value = NOT_SET + self.override_value = NOT_SET self.start_value = NOT_SET self.input_fields = [] From 782424139648021798517cd24a5949a7f292d1ba Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 9 Sep 2020 11:54:59 +0200 Subject: [PATCH 478/813] removed reset_value method --- .../config_setting/widgets/inputs.py | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 5ad97fb698..54b7bf96e4 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -440,9 +440,6 @@ class NumberWidget(QtWidgets.QWidget, InputObject): def clear_value(self): self.set_value(0) - def reset_value(self): - self.set_value(self.start_value) - def _on_value_change(self, item=None): if self.ignore_value_changes: return @@ -555,9 +552,6 @@ class TextWidget(QtWidgets.QWidget, InputObject): else: self.text_input.setText(value) - def reset_value(self): - self.set_value(self.start_value) - def clear_value(self): self.set_value("") @@ -666,9 +660,6 @@ class PathInputWidget(QtWidgets.QWidget, InputObject): def set_value(self, value): self.path_input.setText(value) - def reset_value(self): - self.set_value(self.start_value) - def clear_value(self): self.set_value("") @@ -843,9 +834,6 @@ class RawJsonWidget(QtWidgets.QWidget, InputObject): def set_value(self, value): self.text_input.set_value(value) - def reset_value(self): - self.set_value(self.start_value) - def clear_value(self): self.set_value("") @@ -1031,9 +1019,6 @@ class ListWidget(QtWidgets.QWidget, InputObject): def count(self): return len(self.input_fields) - def reset_value(self): - self.set_value(self.start_value) - def clear_value(self): self.set_value([]) @@ -2203,10 +2188,6 @@ class PathWidget(QtWidgets.QWidget, ConfigObject): _value = value[input_field.key] input_field.set_value(_value) - def reset_value(self): - for input_field in self.input_fields: - input_field.reset_value() - def clear_value(self): for input_field in self.input_fields: input_field.clear_value() From f263a71e6b02bd25ea804b741f70d2b7a8aef292 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 9 Sep 2020 11:56:24 +0200 Subject: [PATCH 479/813] removed clear value --- .../config_setting/widgets/anatomy_inputs.py | 3 --- .../config_setting/widgets/inputs.py | 22 ------------------- 2 files changed, 25 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index 957691cbdc..fbc3a3a2ed 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -102,9 +102,6 @@ class AnatomyWidget(QtWidgets.QWidget, ConfigObject): def set_value(self, value): raise TypeError("AnatomyWidget does not allow to use `set_value`") - def clear_value(self): - raise TypeError("AnatomyWidget does not allow to use `clear_value`") - def _on_value_change(self, item=None): if self.ignore_value_changes: return diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 54b7bf96e4..40de1c1347 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -324,9 +324,6 @@ class BooleanWidget(QtWidgets.QWidget, InputObject): # value as `value` the `_on_value_change` is not triggered self.checkbox.setChecked(value) - def clear_value(self): - self.set_value(False) - def _on_value_change(self, item=None): if self.ignore_value_changes: return @@ -437,9 +434,6 @@ class NumberWidget(QtWidgets.QWidget, InputObject): def set_value(self, value): self.input_field.setValue(value) - def clear_value(self): - self.set_value(0) - def _on_value_change(self, item=None): if self.ignore_value_changes: return @@ -552,9 +546,6 @@ class TextWidget(QtWidgets.QWidget, InputObject): else: self.text_input.setText(value) - def clear_value(self): - self.set_value("") - def _on_value_change(self, item=None): if self.ignore_value_changes: return @@ -660,9 +651,6 @@ class PathInputWidget(QtWidgets.QWidget, InputObject): def set_value(self, value): self.path_input.setText(value) - def clear_value(self): - self.set_value("") - def focusOutEvent(self, event): self.path_input.clear_end_path() super(PathInput, self).focusOutEvent(event) @@ -834,9 +822,6 @@ class RawJsonWidget(QtWidgets.QWidget, InputObject): def set_value(self, value): self.text_input.set_value(value) - def clear_value(self): - self.set_value("") - def _on_value_change(self, item=None): self._is_invalid = self.text_input.has_invalid_value() if self.ignore_value_changes: @@ -1019,9 +1004,6 @@ class ListWidget(QtWidgets.QWidget, InputObject): def count(self): return len(self.input_fields) - def clear_value(self): - self.set_value([]) - def update_studio_values(self, parent_values): old_inputs = tuple(self.input_fields) @@ -2188,10 +2170,6 @@ class PathWidget(QtWidgets.QWidget, ConfigObject): _value = value[input_field.key] input_field.set_value(_value) - def clear_value(self): - for input_field in self.input_fields: - input_field.clear_value() - def _on_value_change(self, item=None): if self.ignore_value_changes: return From 9924b2899e39a4c9c6d839430076184c865cdd22 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 9 Sep 2020 12:06:19 +0200 Subject: [PATCH 480/813] removed default from schemas and laoding from schemas --- .../projects_schema/1_plugins_gui_schema.json | 120 ++++++------------ .../config_setting/widgets/inputs.py | 8 -- 2 files changed, 40 insertions(+), 88 deletions(-) diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json index 302d1dad0a..597f51d6fd 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json @@ -27,41 +27,34 @@ { "type": "boolean", "key": "enabled", - "label": "Enabled", - "default": true + "label": "Enabled" }, { "type": "dict-form", "children": [ { "type": "text", "key": "deadline_department", - "label": "Deadline apartment", - "default": "" + "label": "Deadline apartment" }, { "type": "number", "key": "deadline_priority", - "label": "Deadline priority", - "default": 50 + "label": "Deadline priority" }, { "type": "text", "key": "deadline_pool", - "label": "Deadline pool", - "default": "" + "label": "Deadline pool" }, { "type": "text", "key": "deadline_pool_secondary", - "label": "Deadline pool (secondary)", - "default": "" + "label": "Deadline pool (secondary)" }, { "type": "text", "key": "deadline_group", - "label": "Deadline Group", - "default": "" + "label": "Deadline Group" }, { "type": "number", "key": "deadline_chunk_size", - "label": "Deadline Chunk size", - "default": 10 + "label": "Deadline Chunk size" } ] } @@ -93,19 +86,16 @@ { "type": "boolean", "key": "enabled", - "label": "Enabled", - "default": false + "label": "Enabled" }, { "type": "text", "key": "note_with_intent_template", - "label": "Note with intent template", - "default": "{intent}: {comment}" + "label": "Note with intent template" }, { "type": "list", "object_type": "text", "key": "note_labels", - "label": "Note labels", - "default": [] + "label": "Note labels" } ] } @@ -154,14 +144,12 @@ "type": "list", "object_type": "text", "key": "input", - "label": "FFmpeg input arguments", - "default": [] + "label": "FFmpeg input arguments" }, { "type": "list", "object_type": "text", "key": "output", - "label": "FFmpeg output arguments", - "default": [] + "label": "FFmpeg output arguments" } ] } @@ -177,13 +165,11 @@ { "type": "boolean", "key": "enabled", - "label": "Enabled", - "default": true + "label": "Enabled" }, { "type": "raw-json", "key": "profiles", - "label": "Profiles", - "default": [] + "label": "Profiles" } ] }, { @@ -207,33 +193,27 @@ { "type": "number", "key": "font_size", - "label": "Font size", - "default": 42 + "label": "Font size" }, { "type": "number", "key": "opacity", - "label": "Font opacity", - "default": 1 + "label": "Font opacity" }, { "type": "number", "key": "bg_opacity", - "label": "Background opacity", - "default": 1 + "label": "Background opacity" }, { "type": "number", "key": "x_offset", - "label": "X Offset", - "default": 5 + "label": "X Offset" }, { "type": "number", "key": "y_offset", - "label": "Y Offset", - "default": 5 + "label": "Y Offset" }, { "type": "number", "key": "bg_padding", - "label": "Padding aroung text", - "default": 5 + "label": "Padding aroung text" } ] }, { @@ -316,8 +296,7 @@ }, { "type": "text", "key": "regex", - "label": "Validation regex", - "default": "(.*)_(\\d)*_(?P.*)_(GEO)" + "label": "Validation regex" } ] }, { @@ -349,8 +328,7 @@ }, { "type": "text", "key": "regex", - "label": "Validation regex", - "default": "(?P.*)_(.*)_SHD" + "label": "Validation regex" } ] }, { @@ -397,8 +375,7 @@ { "type": "text", "key": "fpath_template", - "label": "Path template", - "default": "{work}/renders/nuke/{subset}/{subset}.{frame}.{ext}" + "label": "Path template" } ] }, { @@ -411,8 +388,7 @@ { "type": "text", "key": "fpath_template", - "label": "Path template", - "default": "{work}/prerenders/nuke/{subset}/{subset}.{frame}.{ext}" + "label": "Path template" } ] } @@ -434,8 +410,7 @@ { "type": "boolean", "key": "enabled", - "label": "Enabled", - "default": true + "label": "Enabled" }, { "type": "raw-json", "key": "nodes", @@ -453,8 +428,7 @@ { "type": "boolean", "key": "enabled", - "label": "Enabled", - "default": false + "label": "Enabled" }, { "type": "raw-json", "key": "knobs", @@ -472,8 +446,7 @@ { "type": "boolean", "key": "enabled", - "label": "Enabled", - "default": false + "label": "Enabled" } ] }, { @@ -487,13 +460,11 @@ { "type": "boolean", "key": "enabled", - "label": "Enabled", - "default": true + "label": "Enabled" }, { "type": "boolean", "key": "viewer_lut_raw", - "label": "Viewer LUT raw", - "default": false + "label": "Viewer LUT raw" } ] }, { @@ -506,8 +477,7 @@ { "type": "boolean", "key": "viewer_lut_raw", - "label": "Viewer LUT raw", - "default": false + "label": "Viewer LUT raw" } ] }, { @@ -520,23 +490,19 @@ { "type": "number", "key": "deadline_priority", - "label": "deadline_priority", - "default": 50 + "label": "deadline_priority" }, { "type": "text", "key": "deadline_pool", - "label": "deadline_pool", - "default": "" + "label": "deadline_pool" }, { "type": "text", "key": "deadline_pool_secondary", - "label": "deadline_pool_secondary", - "default": "" + "label": "deadline_pool_secondary" }, { "type": "number", "key": "deadline_chunk_size", - "label": "deadline_chunk_size", - "default": 1 + "label": "deadline_chunk_size" } ] } @@ -570,8 +536,7 @@ { "type": "boolean", "key": "enabled", - "label": "Enabled", - "default": false + "label": "Enabled" } ] }, { @@ -585,14 +550,12 @@ { "type": "boolean", "key": "enabled", - "label": "Enabled", - "default": true + "label": "Enabled" }, { "type": "list", "object_type": "text", "key": "tags_addition", - "label": "Tags addition", - "default": [] + "label": "Tags addition" } ] } @@ -621,18 +584,15 @@ { "type": "text", "key": "clipName", - "label": "Clip name template", - "default": "{track}{sequence}{shot}" + "label": "Clip name template" }, { "type": "text", "key": "folder", - "label": "Folder", - "default": "takes" + "label": "Folder" }, { "type": "number", "key": "steps", - "label": "Steps", - "default": 20 + "label": "Steps" } ] } diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 40de1c1347..16ecd5cd1a 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -270,7 +270,6 @@ class BooleanWidget(QtWidgets.QWidget, InputObject): self._is_group = input_data.get("is_group", False) self._is_nullable = input_data.get("is_nullable", False) - self.default_value = input_data.get("default", NOT_SET) self.default_value = NOT_SET self.studio_value = NOT_SET @@ -380,7 +379,6 @@ class NumberWidget(QtWidgets.QWidget, InputObject): self._is_group = input_data.get("is_group", False) self._is_nullable = input_data.get("is_nullable", False) - self.default_value = input_data.get("default", NOT_SET) self.default_value = NOT_SET self.studio_value = NOT_SET @@ -490,7 +488,6 @@ class TextWidget(QtWidgets.QWidget, InputObject): self._is_group = input_data.get("is_group", False) self._is_nullable = input_data.get("is_nullable", False) - self.default_value = input_data.get("default", NOT_SET) self.multiline = input_data.get("multiline", False) @@ -605,7 +602,6 @@ class PathInputWidget(QtWidgets.QWidget, InputObject): self._is_group = input_data.get("is_group", False) self._is_nullable = input_data.get("is_nullable", False) - self.default_value = input_data.get("default", NOT_SET) self.default_value = NOT_SET self.studio_value = NOT_SET @@ -769,7 +765,6 @@ class RawJsonWidget(QtWidgets.QWidget, InputObject): self._is_group = input_data.get("is_group", False) self._is_nullable = input_data.get("is_nullable", False) - self.default_value = input_data.get("default", NOT_SET) self.default_value = NOT_SET self.studio_value = NOT_SET @@ -966,7 +961,6 @@ class ListWidget(QtWidgets.QWidget, InputObject): self._is_nullable = input_data.get("is_nullable", False) self.object_type = input_data["object_type"] - self.default_value = input_data.get("default", NOT_SET) self.input_modifiers = input_data.get("input_modifiers") or {} self.default_value = NOT_SET @@ -1363,7 +1357,6 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): self.setAttribute(QtCore.Qt.WA_TranslucentBackground) self.object_type = input_data["object_type"] - self.default_value = input_data.get("default", NOT_SET) self.input_modifiers = input_data.get("input_modifiers") or {} self.add_row(is_empty=True) @@ -2031,7 +2024,6 @@ class PathWidget(QtWidgets.QWidget, ConfigObject): self._is_group = False self._is_nullable = input_data.get("is_nullable", False) - self.default_value = input_data.get("default", NOT_SET) self.multiplatform = input_data.get("multiplatform", False) self.multipath = input_data.get("multipath", False) From 022214a992e0468d16b1f73c41ac2da618b4aeb5 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 9 Sep 2020 12:08:48 +0200 Subject: [PATCH 481/813] has studi ooverrides is False by default --- pype/tools/config_setting/config_setting/widgets/inputs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 16ecd5cd1a..057ed3b584 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -16,7 +16,7 @@ class ConfigObject(AbstractConfigObject): default_state = "" - _has_studio_override = True + _has_studio_override = False _as_widget = False _is_overriden = False _is_modified = False From ef2b0c955e5c7466a45ebf3c58ff8e32e924c212 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 9 Sep 2020 12:12:08 +0200 Subject: [PATCH 482/813] studio_configuration folder renamed to system_configuration --- .../environments/avalon.json | 0 .../environments/blender.json | 0 .../environments/celaction.json | 0 .../environments/deadline.json | 0 .../environments/ftrack.json | 0 .../environments/global.json | 0 .../environments/harmony.json | 0 .../environments/houdini.json | 0 .../environments/maya.json | 0 .../environments/maya_2018.json | 0 .../environments/maya_2020.json | 0 .../environments/mayabatch.json | 0 .../environments/mayabatch_2019.json | 0 .../environments/mtoa_3.1.1.json | 0 .../environments/muster.json | 0 .../environments/nuke.json | 0 .../environments/nukestudio.json | 0 .../environments/nukestudio_10.0.json | 0 .../environments/nukex.json | 0 .../environments/nukex_10.0.json | 0 .../environments/photoshop.json | 0 .../environments/premiere.json | 0 .../environments/resolve.json | 0 .../environments/storyboardpro.json | 0 .../environments/unreal_4.24.json | 0 .../environments/vray_4300.json | 0 .../global/applications.json | 0 .../global/intent.json | 0 .../global/tools.json | 0 .../global/tray_modules.json | 0 .../launchers/blender_2.80.toml | 0 .../launchers/blender_2.81.toml | 0 .../launchers/blender_2.82.toml | 0 .../launchers/blender_2.83.toml | 0 .../launchers/celaction_local.toml | 0 .../launchers/celaction_publish.toml | 0 .../launchers/darwin/blender_2.82 | 0 .../launchers/darwin/harmony_17 | 0 .../launchers/darwin/harmony_17_launch | 0 .../launchers/darwin/python3 | 0 .../launchers/harmony_17.toml | 0 .../launchers/houdini_16.toml | 0 .../launchers/houdini_17.toml | 0 .../launchers/houdini_18.toml | 0 .../launchers/linux/maya2016 | 0 .../launchers/linux/maya2017 | 0 .../launchers/linux/maya2018 | 0 .../launchers/linux/maya2019 | 0 .../launchers/linux/maya2020 | 0 .../launchers/linux/nuke11.3 | 0 .../launchers/linux/nuke12.0 | 0 .../launchers/linux/nukestudio11.3 | 0 .../launchers/linux/nukestudio12.0 | 0 .../launchers/linux/nukex11.3 | 0 .../launchers/linux/nukex12.0 | 0 .../launchers/maya_2016.toml | 0 .../launchers/maya_2017.toml | 0 .../launchers/maya_2018.toml | 0 .../launchers/maya_2019.toml | 0 .../launchers/maya_2020.toml | 0 .../launchers/mayabatch_2019.toml | 0 .../launchers/mayabatch_2020.toml | 0 .../launchers/mayapy2016.toml | 0 .../launchers/mayapy2017.toml | 0 .../launchers/mayapy2018.toml | 0 .../launchers/mayapy2019.toml | 0 .../launchers/mayapy2020.toml | 0 .../launchers/myapp.toml | 0 .../launchers/nuke_10.0.toml | 0 .../launchers/nuke_11.0.toml | 0 .../launchers/nuke_11.2.toml | 0 .../launchers/nuke_11.3.toml | 0 .../launchers/nuke_12.0.toml | 0 .../launchers/nukestudio_10.0.toml | 0 .../launchers/nukestudio_11.0.toml | 0 .../launchers/nukestudio_11.2.toml | 0 .../launchers/nukestudio_11.3.toml | 0 .../launchers/nukestudio_12.0.toml | 0 .../launchers/nukex_10.0.toml | 0 .../launchers/nukex_11.0.toml | 0 .../launchers/nukex_11.2.toml | 0 .../launchers/nukex_11.3.toml | 0 .../launchers/nukex_12.0.toml | 0 .../launchers/photoshop_2020.toml | 0 .../launchers/premiere_2019.toml | 0 .../launchers/premiere_2020.toml | 0 .../launchers/python_2.toml | 0 .../launchers/python_3.toml | 0 .../launchers/resolve_16.toml | 0 .../launchers/shell.toml | 0 .../launchers/storyboardpro_7.toml | 0 .../launchers/unreal_4.24.toml | 0 .../launchers/windows/blender_2.80.bat | 0 .../launchers/windows/blender_2.81.bat | 0 .../launchers/windows/blender_2.82.bat | 0 .../launchers/windows/blender_2.83.bat | 0 .../launchers/windows/celaction_local.bat | 0 .../launchers/windows/celaction_publish.bat | 0 .../launchers/windows/harmony_17.bat | 0 .../launchers/windows/houdini_16.bat | 0 .../launchers/windows/houdini_17.bat | 0 .../launchers/windows/houdini_18.bat | 0 .../launchers/windows/maya2016.bat | 0 .../launchers/windows/maya2017.bat | 0 .../launchers/windows/maya2018.bat | 0 .../launchers/windows/maya2019.bat | 0 .../launchers/windows/maya2020.bat | 0 .../launchers/windows/mayabatch2019.bat | 0 .../launchers/windows/mayabatch2020.bat | 0 .../launchers/windows/mayapy2016.bat | 0 .../launchers/windows/mayapy2017.bat | 0 .../launchers/windows/mayapy2018.bat | 0 .../launchers/windows/mayapy2019.bat | 0 .../launchers/windows/mayapy2020.bat | 0 .../launchers/windows/nuke10.0.bat | 0 .../launchers/windows/nuke11.0.bat | 0 .../launchers/windows/nuke11.2.bat | 0 .../launchers/windows/nuke11.3.bat | 0 .../launchers/windows/nuke12.0.bat | 0 .../launchers/windows/nukestudio10.0.bat | 0 .../launchers/windows/nukestudio11.0.bat | 0 .../launchers/windows/nukestudio11.2.bat | 0 .../launchers/windows/nukestudio11.3.bat | 0 .../launchers/windows/nukestudio12.0.bat | 0 .../launchers/windows/nukex10.0.bat | 0 .../launchers/windows/nukex11.0.bat | 0 .../launchers/windows/nukex11.2.bat | 0 .../launchers/windows/nukex11.3.bat | 0 .../launchers/windows/nukex12.0.bat | 0 .../launchers/windows/photoshop_2020.bat | 0 .../launchers/windows/premiere_pro_2019.bat | 0 .../launchers/windows/premiere_pro_2020.bat | 0 .../launchers/windows/python3.bat | 0 .../launchers/windows/resolve_16.bat | 0 .../launchers/windows/shell.bat | 0 .../launchers/windows/storyboardpro_7.bat | 0 .../launchers/windows/unreal.bat | 0 .../muster/templates_mapping.json | 0 .../standalone_publish/families.json | 0 139 files changed, 0 insertions(+), 0 deletions(-) rename pype/configurations/defaults/{studio_configurations => system_configurations}/environments/avalon.json (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/environments/blender.json (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/environments/celaction.json (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/environments/deadline.json (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/environments/ftrack.json (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/environments/global.json (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/environments/harmony.json (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/environments/houdini.json (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/environments/maya.json (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/environments/maya_2018.json (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/environments/maya_2020.json (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/environments/mayabatch.json (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/environments/mayabatch_2019.json (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/environments/mtoa_3.1.1.json (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/environments/muster.json (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/environments/nuke.json (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/environments/nukestudio.json (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/environments/nukestudio_10.0.json (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/environments/nukex.json (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/environments/nukex_10.0.json (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/environments/photoshop.json (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/environments/premiere.json (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/environments/resolve.json (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/environments/storyboardpro.json (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/environments/unreal_4.24.json (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/environments/vray_4300.json (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/global/applications.json (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/global/intent.json (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/global/tools.json (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/global/tray_modules.json (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/blender_2.80.toml (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/blender_2.81.toml (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/blender_2.82.toml (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/blender_2.83.toml (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/celaction_local.toml (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/celaction_publish.toml (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/darwin/blender_2.82 (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/darwin/harmony_17 (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/darwin/harmony_17_launch (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/darwin/python3 (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/harmony_17.toml (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/houdini_16.toml (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/houdini_17.toml (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/houdini_18.toml (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/linux/maya2016 (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/linux/maya2017 (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/linux/maya2018 (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/linux/maya2019 (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/linux/maya2020 (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/linux/nuke11.3 (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/linux/nuke12.0 (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/linux/nukestudio11.3 (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/linux/nukestudio12.0 (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/linux/nukex11.3 (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/linux/nukex12.0 (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/maya_2016.toml (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/maya_2017.toml (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/maya_2018.toml (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/maya_2019.toml (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/maya_2020.toml (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/mayabatch_2019.toml (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/mayabatch_2020.toml (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/mayapy2016.toml (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/mayapy2017.toml (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/mayapy2018.toml (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/mayapy2019.toml (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/mayapy2020.toml (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/myapp.toml (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/nuke_10.0.toml (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/nuke_11.0.toml (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/nuke_11.2.toml (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/nuke_11.3.toml (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/nuke_12.0.toml (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/nukestudio_10.0.toml (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/nukestudio_11.0.toml (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/nukestudio_11.2.toml (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/nukestudio_11.3.toml (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/nukestudio_12.0.toml (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/nukex_10.0.toml (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/nukex_11.0.toml (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/nukex_11.2.toml (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/nukex_11.3.toml (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/nukex_12.0.toml (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/photoshop_2020.toml (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/premiere_2019.toml (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/premiere_2020.toml (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/python_2.toml (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/python_3.toml (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/resolve_16.toml (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/shell.toml (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/storyboardpro_7.toml (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/unreal_4.24.toml (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/windows/blender_2.80.bat (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/windows/blender_2.81.bat (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/windows/blender_2.82.bat (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/windows/blender_2.83.bat (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/windows/celaction_local.bat (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/windows/celaction_publish.bat (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/windows/harmony_17.bat (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/windows/houdini_16.bat (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/windows/houdini_17.bat (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/windows/houdini_18.bat (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/windows/maya2016.bat (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/windows/maya2017.bat (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/windows/maya2018.bat (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/windows/maya2019.bat (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/windows/maya2020.bat (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/windows/mayabatch2019.bat (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/windows/mayabatch2020.bat (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/windows/mayapy2016.bat (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/windows/mayapy2017.bat (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/windows/mayapy2018.bat (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/windows/mayapy2019.bat (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/windows/mayapy2020.bat (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/windows/nuke10.0.bat (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/windows/nuke11.0.bat (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/windows/nuke11.2.bat (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/windows/nuke11.3.bat (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/windows/nuke12.0.bat (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/windows/nukestudio10.0.bat (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/windows/nukestudio11.0.bat (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/windows/nukestudio11.2.bat (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/windows/nukestudio11.3.bat (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/windows/nukestudio12.0.bat (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/windows/nukex10.0.bat (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/windows/nukex11.0.bat (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/windows/nukex11.2.bat (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/windows/nukex11.3.bat (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/windows/nukex12.0.bat (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/windows/photoshop_2020.bat (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/windows/premiere_pro_2019.bat (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/windows/premiere_pro_2020.bat (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/windows/python3.bat (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/windows/resolve_16.bat (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/windows/shell.bat (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/windows/storyboardpro_7.bat (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/launchers/windows/unreal.bat (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/muster/templates_mapping.json (100%) rename pype/configurations/defaults/{studio_configurations => system_configurations}/standalone_publish/families.json (100%) diff --git a/pype/configurations/defaults/studio_configurations/environments/avalon.json b/pype/configurations/defaults/system_configurations/environments/avalon.json similarity index 100% rename from pype/configurations/defaults/studio_configurations/environments/avalon.json rename to pype/configurations/defaults/system_configurations/environments/avalon.json diff --git a/pype/configurations/defaults/studio_configurations/environments/blender.json b/pype/configurations/defaults/system_configurations/environments/blender.json similarity index 100% rename from pype/configurations/defaults/studio_configurations/environments/blender.json rename to pype/configurations/defaults/system_configurations/environments/blender.json diff --git a/pype/configurations/defaults/studio_configurations/environments/celaction.json b/pype/configurations/defaults/system_configurations/environments/celaction.json similarity index 100% rename from pype/configurations/defaults/studio_configurations/environments/celaction.json rename to pype/configurations/defaults/system_configurations/environments/celaction.json diff --git a/pype/configurations/defaults/studio_configurations/environments/deadline.json b/pype/configurations/defaults/system_configurations/environments/deadline.json similarity index 100% rename from pype/configurations/defaults/studio_configurations/environments/deadline.json rename to pype/configurations/defaults/system_configurations/environments/deadline.json diff --git a/pype/configurations/defaults/studio_configurations/environments/ftrack.json b/pype/configurations/defaults/system_configurations/environments/ftrack.json similarity index 100% rename from pype/configurations/defaults/studio_configurations/environments/ftrack.json rename to pype/configurations/defaults/system_configurations/environments/ftrack.json diff --git a/pype/configurations/defaults/studio_configurations/environments/global.json b/pype/configurations/defaults/system_configurations/environments/global.json similarity index 100% rename from pype/configurations/defaults/studio_configurations/environments/global.json rename to pype/configurations/defaults/system_configurations/environments/global.json diff --git a/pype/configurations/defaults/studio_configurations/environments/harmony.json b/pype/configurations/defaults/system_configurations/environments/harmony.json similarity index 100% rename from pype/configurations/defaults/studio_configurations/environments/harmony.json rename to pype/configurations/defaults/system_configurations/environments/harmony.json diff --git a/pype/configurations/defaults/studio_configurations/environments/houdini.json b/pype/configurations/defaults/system_configurations/environments/houdini.json similarity index 100% rename from pype/configurations/defaults/studio_configurations/environments/houdini.json rename to pype/configurations/defaults/system_configurations/environments/houdini.json diff --git a/pype/configurations/defaults/studio_configurations/environments/maya.json b/pype/configurations/defaults/system_configurations/environments/maya.json similarity index 100% rename from pype/configurations/defaults/studio_configurations/environments/maya.json rename to pype/configurations/defaults/system_configurations/environments/maya.json diff --git a/pype/configurations/defaults/studio_configurations/environments/maya_2018.json b/pype/configurations/defaults/system_configurations/environments/maya_2018.json similarity index 100% rename from pype/configurations/defaults/studio_configurations/environments/maya_2018.json rename to pype/configurations/defaults/system_configurations/environments/maya_2018.json diff --git a/pype/configurations/defaults/studio_configurations/environments/maya_2020.json b/pype/configurations/defaults/system_configurations/environments/maya_2020.json similarity index 100% rename from pype/configurations/defaults/studio_configurations/environments/maya_2020.json rename to pype/configurations/defaults/system_configurations/environments/maya_2020.json diff --git a/pype/configurations/defaults/studio_configurations/environments/mayabatch.json b/pype/configurations/defaults/system_configurations/environments/mayabatch.json similarity index 100% rename from pype/configurations/defaults/studio_configurations/environments/mayabatch.json rename to pype/configurations/defaults/system_configurations/environments/mayabatch.json diff --git a/pype/configurations/defaults/studio_configurations/environments/mayabatch_2019.json b/pype/configurations/defaults/system_configurations/environments/mayabatch_2019.json similarity index 100% rename from pype/configurations/defaults/studio_configurations/environments/mayabatch_2019.json rename to pype/configurations/defaults/system_configurations/environments/mayabatch_2019.json diff --git a/pype/configurations/defaults/studio_configurations/environments/mtoa_3.1.1.json b/pype/configurations/defaults/system_configurations/environments/mtoa_3.1.1.json similarity index 100% rename from pype/configurations/defaults/studio_configurations/environments/mtoa_3.1.1.json rename to pype/configurations/defaults/system_configurations/environments/mtoa_3.1.1.json diff --git a/pype/configurations/defaults/studio_configurations/environments/muster.json b/pype/configurations/defaults/system_configurations/environments/muster.json similarity index 100% rename from pype/configurations/defaults/studio_configurations/environments/muster.json rename to pype/configurations/defaults/system_configurations/environments/muster.json diff --git a/pype/configurations/defaults/studio_configurations/environments/nuke.json b/pype/configurations/defaults/system_configurations/environments/nuke.json similarity index 100% rename from pype/configurations/defaults/studio_configurations/environments/nuke.json rename to pype/configurations/defaults/system_configurations/environments/nuke.json diff --git a/pype/configurations/defaults/studio_configurations/environments/nukestudio.json b/pype/configurations/defaults/system_configurations/environments/nukestudio.json similarity index 100% rename from pype/configurations/defaults/studio_configurations/environments/nukestudio.json rename to pype/configurations/defaults/system_configurations/environments/nukestudio.json diff --git a/pype/configurations/defaults/studio_configurations/environments/nukestudio_10.0.json b/pype/configurations/defaults/system_configurations/environments/nukestudio_10.0.json similarity index 100% rename from pype/configurations/defaults/studio_configurations/environments/nukestudio_10.0.json rename to pype/configurations/defaults/system_configurations/environments/nukestudio_10.0.json diff --git a/pype/configurations/defaults/studio_configurations/environments/nukex.json b/pype/configurations/defaults/system_configurations/environments/nukex.json similarity index 100% rename from pype/configurations/defaults/studio_configurations/environments/nukex.json rename to pype/configurations/defaults/system_configurations/environments/nukex.json diff --git a/pype/configurations/defaults/studio_configurations/environments/nukex_10.0.json b/pype/configurations/defaults/system_configurations/environments/nukex_10.0.json similarity index 100% rename from pype/configurations/defaults/studio_configurations/environments/nukex_10.0.json rename to pype/configurations/defaults/system_configurations/environments/nukex_10.0.json diff --git a/pype/configurations/defaults/studio_configurations/environments/photoshop.json b/pype/configurations/defaults/system_configurations/environments/photoshop.json similarity index 100% rename from pype/configurations/defaults/studio_configurations/environments/photoshop.json rename to pype/configurations/defaults/system_configurations/environments/photoshop.json diff --git a/pype/configurations/defaults/studio_configurations/environments/premiere.json b/pype/configurations/defaults/system_configurations/environments/premiere.json similarity index 100% rename from pype/configurations/defaults/studio_configurations/environments/premiere.json rename to pype/configurations/defaults/system_configurations/environments/premiere.json diff --git a/pype/configurations/defaults/studio_configurations/environments/resolve.json b/pype/configurations/defaults/system_configurations/environments/resolve.json similarity index 100% rename from pype/configurations/defaults/studio_configurations/environments/resolve.json rename to pype/configurations/defaults/system_configurations/environments/resolve.json diff --git a/pype/configurations/defaults/studio_configurations/environments/storyboardpro.json b/pype/configurations/defaults/system_configurations/environments/storyboardpro.json similarity index 100% rename from pype/configurations/defaults/studio_configurations/environments/storyboardpro.json rename to pype/configurations/defaults/system_configurations/environments/storyboardpro.json diff --git a/pype/configurations/defaults/studio_configurations/environments/unreal_4.24.json b/pype/configurations/defaults/system_configurations/environments/unreal_4.24.json similarity index 100% rename from pype/configurations/defaults/studio_configurations/environments/unreal_4.24.json rename to pype/configurations/defaults/system_configurations/environments/unreal_4.24.json diff --git a/pype/configurations/defaults/studio_configurations/environments/vray_4300.json b/pype/configurations/defaults/system_configurations/environments/vray_4300.json similarity index 100% rename from pype/configurations/defaults/studio_configurations/environments/vray_4300.json rename to pype/configurations/defaults/system_configurations/environments/vray_4300.json diff --git a/pype/configurations/defaults/studio_configurations/global/applications.json b/pype/configurations/defaults/system_configurations/global/applications.json similarity index 100% rename from pype/configurations/defaults/studio_configurations/global/applications.json rename to pype/configurations/defaults/system_configurations/global/applications.json diff --git a/pype/configurations/defaults/studio_configurations/global/intent.json b/pype/configurations/defaults/system_configurations/global/intent.json similarity index 100% rename from pype/configurations/defaults/studio_configurations/global/intent.json rename to pype/configurations/defaults/system_configurations/global/intent.json diff --git a/pype/configurations/defaults/studio_configurations/global/tools.json b/pype/configurations/defaults/system_configurations/global/tools.json similarity index 100% rename from pype/configurations/defaults/studio_configurations/global/tools.json rename to pype/configurations/defaults/system_configurations/global/tools.json diff --git a/pype/configurations/defaults/studio_configurations/global/tray_modules.json b/pype/configurations/defaults/system_configurations/global/tray_modules.json similarity index 100% rename from pype/configurations/defaults/studio_configurations/global/tray_modules.json rename to pype/configurations/defaults/system_configurations/global/tray_modules.json diff --git a/pype/configurations/defaults/studio_configurations/launchers/blender_2.80.toml b/pype/configurations/defaults/system_configurations/launchers/blender_2.80.toml similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/blender_2.80.toml rename to pype/configurations/defaults/system_configurations/launchers/blender_2.80.toml diff --git a/pype/configurations/defaults/studio_configurations/launchers/blender_2.81.toml b/pype/configurations/defaults/system_configurations/launchers/blender_2.81.toml similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/blender_2.81.toml rename to pype/configurations/defaults/system_configurations/launchers/blender_2.81.toml diff --git a/pype/configurations/defaults/studio_configurations/launchers/blender_2.82.toml b/pype/configurations/defaults/system_configurations/launchers/blender_2.82.toml similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/blender_2.82.toml rename to pype/configurations/defaults/system_configurations/launchers/blender_2.82.toml diff --git a/pype/configurations/defaults/studio_configurations/launchers/blender_2.83.toml b/pype/configurations/defaults/system_configurations/launchers/blender_2.83.toml similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/blender_2.83.toml rename to pype/configurations/defaults/system_configurations/launchers/blender_2.83.toml diff --git a/pype/configurations/defaults/studio_configurations/launchers/celaction_local.toml b/pype/configurations/defaults/system_configurations/launchers/celaction_local.toml similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/celaction_local.toml rename to pype/configurations/defaults/system_configurations/launchers/celaction_local.toml diff --git a/pype/configurations/defaults/studio_configurations/launchers/celaction_publish.toml b/pype/configurations/defaults/system_configurations/launchers/celaction_publish.toml similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/celaction_publish.toml rename to pype/configurations/defaults/system_configurations/launchers/celaction_publish.toml diff --git a/pype/configurations/defaults/studio_configurations/launchers/darwin/blender_2.82 b/pype/configurations/defaults/system_configurations/launchers/darwin/blender_2.82 similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/darwin/blender_2.82 rename to pype/configurations/defaults/system_configurations/launchers/darwin/blender_2.82 diff --git a/pype/configurations/defaults/studio_configurations/launchers/darwin/harmony_17 b/pype/configurations/defaults/system_configurations/launchers/darwin/harmony_17 similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/darwin/harmony_17 rename to pype/configurations/defaults/system_configurations/launchers/darwin/harmony_17 diff --git a/pype/configurations/defaults/studio_configurations/launchers/darwin/harmony_17_launch b/pype/configurations/defaults/system_configurations/launchers/darwin/harmony_17_launch similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/darwin/harmony_17_launch rename to pype/configurations/defaults/system_configurations/launchers/darwin/harmony_17_launch diff --git a/pype/configurations/defaults/studio_configurations/launchers/darwin/python3 b/pype/configurations/defaults/system_configurations/launchers/darwin/python3 similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/darwin/python3 rename to pype/configurations/defaults/system_configurations/launchers/darwin/python3 diff --git a/pype/configurations/defaults/studio_configurations/launchers/harmony_17.toml b/pype/configurations/defaults/system_configurations/launchers/harmony_17.toml similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/harmony_17.toml rename to pype/configurations/defaults/system_configurations/launchers/harmony_17.toml diff --git a/pype/configurations/defaults/studio_configurations/launchers/houdini_16.toml b/pype/configurations/defaults/system_configurations/launchers/houdini_16.toml similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/houdini_16.toml rename to pype/configurations/defaults/system_configurations/launchers/houdini_16.toml diff --git a/pype/configurations/defaults/studio_configurations/launchers/houdini_17.toml b/pype/configurations/defaults/system_configurations/launchers/houdini_17.toml similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/houdini_17.toml rename to pype/configurations/defaults/system_configurations/launchers/houdini_17.toml diff --git a/pype/configurations/defaults/studio_configurations/launchers/houdini_18.toml b/pype/configurations/defaults/system_configurations/launchers/houdini_18.toml similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/houdini_18.toml rename to pype/configurations/defaults/system_configurations/launchers/houdini_18.toml diff --git a/pype/configurations/defaults/studio_configurations/launchers/linux/maya2016 b/pype/configurations/defaults/system_configurations/launchers/linux/maya2016 similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/linux/maya2016 rename to pype/configurations/defaults/system_configurations/launchers/linux/maya2016 diff --git a/pype/configurations/defaults/studio_configurations/launchers/linux/maya2017 b/pype/configurations/defaults/system_configurations/launchers/linux/maya2017 similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/linux/maya2017 rename to pype/configurations/defaults/system_configurations/launchers/linux/maya2017 diff --git a/pype/configurations/defaults/studio_configurations/launchers/linux/maya2018 b/pype/configurations/defaults/system_configurations/launchers/linux/maya2018 similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/linux/maya2018 rename to pype/configurations/defaults/system_configurations/launchers/linux/maya2018 diff --git a/pype/configurations/defaults/studio_configurations/launchers/linux/maya2019 b/pype/configurations/defaults/system_configurations/launchers/linux/maya2019 similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/linux/maya2019 rename to pype/configurations/defaults/system_configurations/launchers/linux/maya2019 diff --git a/pype/configurations/defaults/studio_configurations/launchers/linux/maya2020 b/pype/configurations/defaults/system_configurations/launchers/linux/maya2020 similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/linux/maya2020 rename to pype/configurations/defaults/system_configurations/launchers/linux/maya2020 diff --git a/pype/configurations/defaults/studio_configurations/launchers/linux/nuke11.3 b/pype/configurations/defaults/system_configurations/launchers/linux/nuke11.3 similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/linux/nuke11.3 rename to pype/configurations/defaults/system_configurations/launchers/linux/nuke11.3 diff --git a/pype/configurations/defaults/studio_configurations/launchers/linux/nuke12.0 b/pype/configurations/defaults/system_configurations/launchers/linux/nuke12.0 similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/linux/nuke12.0 rename to pype/configurations/defaults/system_configurations/launchers/linux/nuke12.0 diff --git a/pype/configurations/defaults/studio_configurations/launchers/linux/nukestudio11.3 b/pype/configurations/defaults/system_configurations/launchers/linux/nukestudio11.3 similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/linux/nukestudio11.3 rename to pype/configurations/defaults/system_configurations/launchers/linux/nukestudio11.3 diff --git a/pype/configurations/defaults/studio_configurations/launchers/linux/nukestudio12.0 b/pype/configurations/defaults/system_configurations/launchers/linux/nukestudio12.0 similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/linux/nukestudio12.0 rename to pype/configurations/defaults/system_configurations/launchers/linux/nukestudio12.0 diff --git a/pype/configurations/defaults/studio_configurations/launchers/linux/nukex11.3 b/pype/configurations/defaults/system_configurations/launchers/linux/nukex11.3 similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/linux/nukex11.3 rename to pype/configurations/defaults/system_configurations/launchers/linux/nukex11.3 diff --git a/pype/configurations/defaults/studio_configurations/launchers/linux/nukex12.0 b/pype/configurations/defaults/system_configurations/launchers/linux/nukex12.0 similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/linux/nukex12.0 rename to pype/configurations/defaults/system_configurations/launchers/linux/nukex12.0 diff --git a/pype/configurations/defaults/studio_configurations/launchers/maya_2016.toml b/pype/configurations/defaults/system_configurations/launchers/maya_2016.toml similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/maya_2016.toml rename to pype/configurations/defaults/system_configurations/launchers/maya_2016.toml diff --git a/pype/configurations/defaults/studio_configurations/launchers/maya_2017.toml b/pype/configurations/defaults/system_configurations/launchers/maya_2017.toml similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/maya_2017.toml rename to pype/configurations/defaults/system_configurations/launchers/maya_2017.toml diff --git a/pype/configurations/defaults/studio_configurations/launchers/maya_2018.toml b/pype/configurations/defaults/system_configurations/launchers/maya_2018.toml similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/maya_2018.toml rename to pype/configurations/defaults/system_configurations/launchers/maya_2018.toml diff --git a/pype/configurations/defaults/studio_configurations/launchers/maya_2019.toml b/pype/configurations/defaults/system_configurations/launchers/maya_2019.toml similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/maya_2019.toml rename to pype/configurations/defaults/system_configurations/launchers/maya_2019.toml diff --git a/pype/configurations/defaults/studio_configurations/launchers/maya_2020.toml b/pype/configurations/defaults/system_configurations/launchers/maya_2020.toml similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/maya_2020.toml rename to pype/configurations/defaults/system_configurations/launchers/maya_2020.toml diff --git a/pype/configurations/defaults/studio_configurations/launchers/mayabatch_2019.toml b/pype/configurations/defaults/system_configurations/launchers/mayabatch_2019.toml similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/mayabatch_2019.toml rename to pype/configurations/defaults/system_configurations/launchers/mayabatch_2019.toml diff --git a/pype/configurations/defaults/studio_configurations/launchers/mayabatch_2020.toml b/pype/configurations/defaults/system_configurations/launchers/mayabatch_2020.toml similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/mayabatch_2020.toml rename to pype/configurations/defaults/system_configurations/launchers/mayabatch_2020.toml diff --git a/pype/configurations/defaults/studio_configurations/launchers/mayapy2016.toml b/pype/configurations/defaults/system_configurations/launchers/mayapy2016.toml similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/mayapy2016.toml rename to pype/configurations/defaults/system_configurations/launchers/mayapy2016.toml diff --git a/pype/configurations/defaults/studio_configurations/launchers/mayapy2017.toml b/pype/configurations/defaults/system_configurations/launchers/mayapy2017.toml similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/mayapy2017.toml rename to pype/configurations/defaults/system_configurations/launchers/mayapy2017.toml diff --git a/pype/configurations/defaults/studio_configurations/launchers/mayapy2018.toml b/pype/configurations/defaults/system_configurations/launchers/mayapy2018.toml similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/mayapy2018.toml rename to pype/configurations/defaults/system_configurations/launchers/mayapy2018.toml diff --git a/pype/configurations/defaults/studio_configurations/launchers/mayapy2019.toml b/pype/configurations/defaults/system_configurations/launchers/mayapy2019.toml similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/mayapy2019.toml rename to pype/configurations/defaults/system_configurations/launchers/mayapy2019.toml diff --git a/pype/configurations/defaults/studio_configurations/launchers/mayapy2020.toml b/pype/configurations/defaults/system_configurations/launchers/mayapy2020.toml similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/mayapy2020.toml rename to pype/configurations/defaults/system_configurations/launchers/mayapy2020.toml diff --git a/pype/configurations/defaults/studio_configurations/launchers/myapp.toml b/pype/configurations/defaults/system_configurations/launchers/myapp.toml similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/myapp.toml rename to pype/configurations/defaults/system_configurations/launchers/myapp.toml diff --git a/pype/configurations/defaults/studio_configurations/launchers/nuke_10.0.toml b/pype/configurations/defaults/system_configurations/launchers/nuke_10.0.toml similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/nuke_10.0.toml rename to pype/configurations/defaults/system_configurations/launchers/nuke_10.0.toml diff --git a/pype/configurations/defaults/studio_configurations/launchers/nuke_11.0.toml b/pype/configurations/defaults/system_configurations/launchers/nuke_11.0.toml similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/nuke_11.0.toml rename to pype/configurations/defaults/system_configurations/launchers/nuke_11.0.toml diff --git a/pype/configurations/defaults/studio_configurations/launchers/nuke_11.2.toml b/pype/configurations/defaults/system_configurations/launchers/nuke_11.2.toml similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/nuke_11.2.toml rename to pype/configurations/defaults/system_configurations/launchers/nuke_11.2.toml diff --git a/pype/configurations/defaults/studio_configurations/launchers/nuke_11.3.toml b/pype/configurations/defaults/system_configurations/launchers/nuke_11.3.toml similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/nuke_11.3.toml rename to pype/configurations/defaults/system_configurations/launchers/nuke_11.3.toml diff --git a/pype/configurations/defaults/studio_configurations/launchers/nuke_12.0.toml b/pype/configurations/defaults/system_configurations/launchers/nuke_12.0.toml similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/nuke_12.0.toml rename to pype/configurations/defaults/system_configurations/launchers/nuke_12.0.toml diff --git a/pype/configurations/defaults/studio_configurations/launchers/nukestudio_10.0.toml b/pype/configurations/defaults/system_configurations/launchers/nukestudio_10.0.toml similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/nukestudio_10.0.toml rename to pype/configurations/defaults/system_configurations/launchers/nukestudio_10.0.toml diff --git a/pype/configurations/defaults/studio_configurations/launchers/nukestudio_11.0.toml b/pype/configurations/defaults/system_configurations/launchers/nukestudio_11.0.toml similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/nukestudio_11.0.toml rename to pype/configurations/defaults/system_configurations/launchers/nukestudio_11.0.toml diff --git a/pype/configurations/defaults/studio_configurations/launchers/nukestudio_11.2.toml b/pype/configurations/defaults/system_configurations/launchers/nukestudio_11.2.toml similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/nukestudio_11.2.toml rename to pype/configurations/defaults/system_configurations/launchers/nukestudio_11.2.toml diff --git a/pype/configurations/defaults/studio_configurations/launchers/nukestudio_11.3.toml b/pype/configurations/defaults/system_configurations/launchers/nukestudio_11.3.toml similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/nukestudio_11.3.toml rename to pype/configurations/defaults/system_configurations/launchers/nukestudio_11.3.toml diff --git a/pype/configurations/defaults/studio_configurations/launchers/nukestudio_12.0.toml b/pype/configurations/defaults/system_configurations/launchers/nukestudio_12.0.toml similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/nukestudio_12.0.toml rename to pype/configurations/defaults/system_configurations/launchers/nukestudio_12.0.toml diff --git a/pype/configurations/defaults/studio_configurations/launchers/nukex_10.0.toml b/pype/configurations/defaults/system_configurations/launchers/nukex_10.0.toml similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/nukex_10.0.toml rename to pype/configurations/defaults/system_configurations/launchers/nukex_10.0.toml diff --git a/pype/configurations/defaults/studio_configurations/launchers/nukex_11.0.toml b/pype/configurations/defaults/system_configurations/launchers/nukex_11.0.toml similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/nukex_11.0.toml rename to pype/configurations/defaults/system_configurations/launchers/nukex_11.0.toml diff --git a/pype/configurations/defaults/studio_configurations/launchers/nukex_11.2.toml b/pype/configurations/defaults/system_configurations/launchers/nukex_11.2.toml similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/nukex_11.2.toml rename to pype/configurations/defaults/system_configurations/launchers/nukex_11.2.toml diff --git a/pype/configurations/defaults/studio_configurations/launchers/nukex_11.3.toml b/pype/configurations/defaults/system_configurations/launchers/nukex_11.3.toml similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/nukex_11.3.toml rename to pype/configurations/defaults/system_configurations/launchers/nukex_11.3.toml diff --git a/pype/configurations/defaults/studio_configurations/launchers/nukex_12.0.toml b/pype/configurations/defaults/system_configurations/launchers/nukex_12.0.toml similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/nukex_12.0.toml rename to pype/configurations/defaults/system_configurations/launchers/nukex_12.0.toml diff --git a/pype/configurations/defaults/studio_configurations/launchers/photoshop_2020.toml b/pype/configurations/defaults/system_configurations/launchers/photoshop_2020.toml similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/photoshop_2020.toml rename to pype/configurations/defaults/system_configurations/launchers/photoshop_2020.toml diff --git a/pype/configurations/defaults/studio_configurations/launchers/premiere_2019.toml b/pype/configurations/defaults/system_configurations/launchers/premiere_2019.toml similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/premiere_2019.toml rename to pype/configurations/defaults/system_configurations/launchers/premiere_2019.toml diff --git a/pype/configurations/defaults/studio_configurations/launchers/premiere_2020.toml b/pype/configurations/defaults/system_configurations/launchers/premiere_2020.toml similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/premiere_2020.toml rename to pype/configurations/defaults/system_configurations/launchers/premiere_2020.toml diff --git a/pype/configurations/defaults/studio_configurations/launchers/python_2.toml b/pype/configurations/defaults/system_configurations/launchers/python_2.toml similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/python_2.toml rename to pype/configurations/defaults/system_configurations/launchers/python_2.toml diff --git a/pype/configurations/defaults/studio_configurations/launchers/python_3.toml b/pype/configurations/defaults/system_configurations/launchers/python_3.toml similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/python_3.toml rename to pype/configurations/defaults/system_configurations/launchers/python_3.toml diff --git a/pype/configurations/defaults/studio_configurations/launchers/resolve_16.toml b/pype/configurations/defaults/system_configurations/launchers/resolve_16.toml similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/resolve_16.toml rename to pype/configurations/defaults/system_configurations/launchers/resolve_16.toml diff --git a/pype/configurations/defaults/studio_configurations/launchers/shell.toml b/pype/configurations/defaults/system_configurations/launchers/shell.toml similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/shell.toml rename to pype/configurations/defaults/system_configurations/launchers/shell.toml diff --git a/pype/configurations/defaults/studio_configurations/launchers/storyboardpro_7.toml b/pype/configurations/defaults/system_configurations/launchers/storyboardpro_7.toml similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/storyboardpro_7.toml rename to pype/configurations/defaults/system_configurations/launchers/storyboardpro_7.toml diff --git a/pype/configurations/defaults/studio_configurations/launchers/unreal_4.24.toml b/pype/configurations/defaults/system_configurations/launchers/unreal_4.24.toml similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/unreal_4.24.toml rename to pype/configurations/defaults/system_configurations/launchers/unreal_4.24.toml diff --git a/pype/configurations/defaults/studio_configurations/launchers/windows/blender_2.80.bat b/pype/configurations/defaults/system_configurations/launchers/windows/blender_2.80.bat similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/windows/blender_2.80.bat rename to pype/configurations/defaults/system_configurations/launchers/windows/blender_2.80.bat diff --git a/pype/configurations/defaults/studio_configurations/launchers/windows/blender_2.81.bat b/pype/configurations/defaults/system_configurations/launchers/windows/blender_2.81.bat similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/windows/blender_2.81.bat rename to pype/configurations/defaults/system_configurations/launchers/windows/blender_2.81.bat diff --git a/pype/configurations/defaults/studio_configurations/launchers/windows/blender_2.82.bat b/pype/configurations/defaults/system_configurations/launchers/windows/blender_2.82.bat similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/windows/blender_2.82.bat rename to pype/configurations/defaults/system_configurations/launchers/windows/blender_2.82.bat diff --git a/pype/configurations/defaults/studio_configurations/launchers/windows/blender_2.83.bat b/pype/configurations/defaults/system_configurations/launchers/windows/blender_2.83.bat similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/windows/blender_2.83.bat rename to pype/configurations/defaults/system_configurations/launchers/windows/blender_2.83.bat diff --git a/pype/configurations/defaults/studio_configurations/launchers/windows/celaction_local.bat b/pype/configurations/defaults/system_configurations/launchers/windows/celaction_local.bat similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/windows/celaction_local.bat rename to pype/configurations/defaults/system_configurations/launchers/windows/celaction_local.bat diff --git a/pype/configurations/defaults/studio_configurations/launchers/windows/celaction_publish.bat b/pype/configurations/defaults/system_configurations/launchers/windows/celaction_publish.bat similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/windows/celaction_publish.bat rename to pype/configurations/defaults/system_configurations/launchers/windows/celaction_publish.bat diff --git a/pype/configurations/defaults/studio_configurations/launchers/windows/harmony_17.bat b/pype/configurations/defaults/system_configurations/launchers/windows/harmony_17.bat similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/windows/harmony_17.bat rename to pype/configurations/defaults/system_configurations/launchers/windows/harmony_17.bat diff --git a/pype/configurations/defaults/studio_configurations/launchers/windows/houdini_16.bat b/pype/configurations/defaults/system_configurations/launchers/windows/houdini_16.bat similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/windows/houdini_16.bat rename to pype/configurations/defaults/system_configurations/launchers/windows/houdini_16.bat diff --git a/pype/configurations/defaults/studio_configurations/launchers/windows/houdini_17.bat b/pype/configurations/defaults/system_configurations/launchers/windows/houdini_17.bat similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/windows/houdini_17.bat rename to pype/configurations/defaults/system_configurations/launchers/windows/houdini_17.bat diff --git a/pype/configurations/defaults/studio_configurations/launchers/windows/houdini_18.bat b/pype/configurations/defaults/system_configurations/launchers/windows/houdini_18.bat similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/windows/houdini_18.bat rename to pype/configurations/defaults/system_configurations/launchers/windows/houdini_18.bat diff --git a/pype/configurations/defaults/studio_configurations/launchers/windows/maya2016.bat b/pype/configurations/defaults/system_configurations/launchers/windows/maya2016.bat similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/windows/maya2016.bat rename to pype/configurations/defaults/system_configurations/launchers/windows/maya2016.bat diff --git a/pype/configurations/defaults/studio_configurations/launchers/windows/maya2017.bat b/pype/configurations/defaults/system_configurations/launchers/windows/maya2017.bat similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/windows/maya2017.bat rename to pype/configurations/defaults/system_configurations/launchers/windows/maya2017.bat diff --git a/pype/configurations/defaults/studio_configurations/launchers/windows/maya2018.bat b/pype/configurations/defaults/system_configurations/launchers/windows/maya2018.bat similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/windows/maya2018.bat rename to pype/configurations/defaults/system_configurations/launchers/windows/maya2018.bat diff --git a/pype/configurations/defaults/studio_configurations/launchers/windows/maya2019.bat b/pype/configurations/defaults/system_configurations/launchers/windows/maya2019.bat similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/windows/maya2019.bat rename to pype/configurations/defaults/system_configurations/launchers/windows/maya2019.bat diff --git a/pype/configurations/defaults/studio_configurations/launchers/windows/maya2020.bat b/pype/configurations/defaults/system_configurations/launchers/windows/maya2020.bat similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/windows/maya2020.bat rename to pype/configurations/defaults/system_configurations/launchers/windows/maya2020.bat diff --git a/pype/configurations/defaults/studio_configurations/launchers/windows/mayabatch2019.bat b/pype/configurations/defaults/system_configurations/launchers/windows/mayabatch2019.bat similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/windows/mayabatch2019.bat rename to pype/configurations/defaults/system_configurations/launchers/windows/mayabatch2019.bat diff --git a/pype/configurations/defaults/studio_configurations/launchers/windows/mayabatch2020.bat b/pype/configurations/defaults/system_configurations/launchers/windows/mayabatch2020.bat similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/windows/mayabatch2020.bat rename to pype/configurations/defaults/system_configurations/launchers/windows/mayabatch2020.bat diff --git a/pype/configurations/defaults/studio_configurations/launchers/windows/mayapy2016.bat b/pype/configurations/defaults/system_configurations/launchers/windows/mayapy2016.bat similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/windows/mayapy2016.bat rename to pype/configurations/defaults/system_configurations/launchers/windows/mayapy2016.bat diff --git a/pype/configurations/defaults/studio_configurations/launchers/windows/mayapy2017.bat b/pype/configurations/defaults/system_configurations/launchers/windows/mayapy2017.bat similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/windows/mayapy2017.bat rename to pype/configurations/defaults/system_configurations/launchers/windows/mayapy2017.bat diff --git a/pype/configurations/defaults/studio_configurations/launchers/windows/mayapy2018.bat b/pype/configurations/defaults/system_configurations/launchers/windows/mayapy2018.bat similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/windows/mayapy2018.bat rename to pype/configurations/defaults/system_configurations/launchers/windows/mayapy2018.bat diff --git a/pype/configurations/defaults/studio_configurations/launchers/windows/mayapy2019.bat b/pype/configurations/defaults/system_configurations/launchers/windows/mayapy2019.bat similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/windows/mayapy2019.bat rename to pype/configurations/defaults/system_configurations/launchers/windows/mayapy2019.bat diff --git a/pype/configurations/defaults/studio_configurations/launchers/windows/mayapy2020.bat b/pype/configurations/defaults/system_configurations/launchers/windows/mayapy2020.bat similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/windows/mayapy2020.bat rename to pype/configurations/defaults/system_configurations/launchers/windows/mayapy2020.bat diff --git a/pype/configurations/defaults/studio_configurations/launchers/windows/nuke10.0.bat b/pype/configurations/defaults/system_configurations/launchers/windows/nuke10.0.bat similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/windows/nuke10.0.bat rename to pype/configurations/defaults/system_configurations/launchers/windows/nuke10.0.bat diff --git a/pype/configurations/defaults/studio_configurations/launchers/windows/nuke11.0.bat b/pype/configurations/defaults/system_configurations/launchers/windows/nuke11.0.bat similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/windows/nuke11.0.bat rename to pype/configurations/defaults/system_configurations/launchers/windows/nuke11.0.bat diff --git a/pype/configurations/defaults/studio_configurations/launchers/windows/nuke11.2.bat b/pype/configurations/defaults/system_configurations/launchers/windows/nuke11.2.bat similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/windows/nuke11.2.bat rename to pype/configurations/defaults/system_configurations/launchers/windows/nuke11.2.bat diff --git a/pype/configurations/defaults/studio_configurations/launchers/windows/nuke11.3.bat b/pype/configurations/defaults/system_configurations/launchers/windows/nuke11.3.bat similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/windows/nuke11.3.bat rename to pype/configurations/defaults/system_configurations/launchers/windows/nuke11.3.bat diff --git a/pype/configurations/defaults/studio_configurations/launchers/windows/nuke12.0.bat b/pype/configurations/defaults/system_configurations/launchers/windows/nuke12.0.bat similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/windows/nuke12.0.bat rename to pype/configurations/defaults/system_configurations/launchers/windows/nuke12.0.bat diff --git a/pype/configurations/defaults/studio_configurations/launchers/windows/nukestudio10.0.bat b/pype/configurations/defaults/system_configurations/launchers/windows/nukestudio10.0.bat similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/windows/nukestudio10.0.bat rename to pype/configurations/defaults/system_configurations/launchers/windows/nukestudio10.0.bat diff --git a/pype/configurations/defaults/studio_configurations/launchers/windows/nukestudio11.0.bat b/pype/configurations/defaults/system_configurations/launchers/windows/nukestudio11.0.bat similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/windows/nukestudio11.0.bat rename to pype/configurations/defaults/system_configurations/launchers/windows/nukestudio11.0.bat diff --git a/pype/configurations/defaults/studio_configurations/launchers/windows/nukestudio11.2.bat b/pype/configurations/defaults/system_configurations/launchers/windows/nukestudio11.2.bat similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/windows/nukestudio11.2.bat rename to pype/configurations/defaults/system_configurations/launchers/windows/nukestudio11.2.bat diff --git a/pype/configurations/defaults/studio_configurations/launchers/windows/nukestudio11.3.bat b/pype/configurations/defaults/system_configurations/launchers/windows/nukestudio11.3.bat similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/windows/nukestudio11.3.bat rename to pype/configurations/defaults/system_configurations/launchers/windows/nukestudio11.3.bat diff --git a/pype/configurations/defaults/studio_configurations/launchers/windows/nukestudio12.0.bat b/pype/configurations/defaults/system_configurations/launchers/windows/nukestudio12.0.bat similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/windows/nukestudio12.0.bat rename to pype/configurations/defaults/system_configurations/launchers/windows/nukestudio12.0.bat diff --git a/pype/configurations/defaults/studio_configurations/launchers/windows/nukex10.0.bat b/pype/configurations/defaults/system_configurations/launchers/windows/nukex10.0.bat similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/windows/nukex10.0.bat rename to pype/configurations/defaults/system_configurations/launchers/windows/nukex10.0.bat diff --git a/pype/configurations/defaults/studio_configurations/launchers/windows/nukex11.0.bat b/pype/configurations/defaults/system_configurations/launchers/windows/nukex11.0.bat similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/windows/nukex11.0.bat rename to pype/configurations/defaults/system_configurations/launchers/windows/nukex11.0.bat diff --git a/pype/configurations/defaults/studio_configurations/launchers/windows/nukex11.2.bat b/pype/configurations/defaults/system_configurations/launchers/windows/nukex11.2.bat similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/windows/nukex11.2.bat rename to pype/configurations/defaults/system_configurations/launchers/windows/nukex11.2.bat diff --git a/pype/configurations/defaults/studio_configurations/launchers/windows/nukex11.3.bat b/pype/configurations/defaults/system_configurations/launchers/windows/nukex11.3.bat similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/windows/nukex11.3.bat rename to pype/configurations/defaults/system_configurations/launchers/windows/nukex11.3.bat diff --git a/pype/configurations/defaults/studio_configurations/launchers/windows/nukex12.0.bat b/pype/configurations/defaults/system_configurations/launchers/windows/nukex12.0.bat similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/windows/nukex12.0.bat rename to pype/configurations/defaults/system_configurations/launchers/windows/nukex12.0.bat diff --git a/pype/configurations/defaults/studio_configurations/launchers/windows/photoshop_2020.bat b/pype/configurations/defaults/system_configurations/launchers/windows/photoshop_2020.bat similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/windows/photoshop_2020.bat rename to pype/configurations/defaults/system_configurations/launchers/windows/photoshop_2020.bat diff --git a/pype/configurations/defaults/studio_configurations/launchers/windows/premiere_pro_2019.bat b/pype/configurations/defaults/system_configurations/launchers/windows/premiere_pro_2019.bat similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/windows/premiere_pro_2019.bat rename to pype/configurations/defaults/system_configurations/launchers/windows/premiere_pro_2019.bat diff --git a/pype/configurations/defaults/studio_configurations/launchers/windows/premiere_pro_2020.bat b/pype/configurations/defaults/system_configurations/launchers/windows/premiere_pro_2020.bat similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/windows/premiere_pro_2020.bat rename to pype/configurations/defaults/system_configurations/launchers/windows/premiere_pro_2020.bat diff --git a/pype/configurations/defaults/studio_configurations/launchers/windows/python3.bat b/pype/configurations/defaults/system_configurations/launchers/windows/python3.bat similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/windows/python3.bat rename to pype/configurations/defaults/system_configurations/launchers/windows/python3.bat diff --git a/pype/configurations/defaults/studio_configurations/launchers/windows/resolve_16.bat b/pype/configurations/defaults/system_configurations/launchers/windows/resolve_16.bat similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/windows/resolve_16.bat rename to pype/configurations/defaults/system_configurations/launchers/windows/resolve_16.bat diff --git a/pype/configurations/defaults/studio_configurations/launchers/windows/shell.bat b/pype/configurations/defaults/system_configurations/launchers/windows/shell.bat similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/windows/shell.bat rename to pype/configurations/defaults/system_configurations/launchers/windows/shell.bat diff --git a/pype/configurations/defaults/studio_configurations/launchers/windows/storyboardpro_7.bat b/pype/configurations/defaults/system_configurations/launchers/windows/storyboardpro_7.bat similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/windows/storyboardpro_7.bat rename to pype/configurations/defaults/system_configurations/launchers/windows/storyboardpro_7.bat diff --git a/pype/configurations/defaults/studio_configurations/launchers/windows/unreal.bat b/pype/configurations/defaults/system_configurations/launchers/windows/unreal.bat similarity index 100% rename from pype/configurations/defaults/studio_configurations/launchers/windows/unreal.bat rename to pype/configurations/defaults/system_configurations/launchers/windows/unreal.bat diff --git a/pype/configurations/defaults/studio_configurations/muster/templates_mapping.json b/pype/configurations/defaults/system_configurations/muster/templates_mapping.json similarity index 100% rename from pype/configurations/defaults/studio_configurations/muster/templates_mapping.json rename to pype/configurations/defaults/system_configurations/muster/templates_mapping.json diff --git a/pype/configurations/defaults/studio_configurations/standalone_publish/families.json b/pype/configurations/defaults/system_configurations/standalone_publish/families.json similarity index 100% rename from pype/configurations/defaults/studio_configurations/standalone_publish/families.json rename to pype/configurations/defaults/system_configurations/standalone_publish/families.json From 0cca3c68d10ca993cee378b3b9b52fc26eb80e77 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 9 Sep 2020 12:19:46 +0200 Subject: [PATCH 483/813] fixed last place where is used stuido instead of system --- pype/configurations/config.py | 4 ++-- pype/tools/config_setting/config_setting/widgets/base.py | 7 +++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/pype/configurations/config.py b/pype/configurations/config.py index 147570acd4..de310645ac 100644 --- a/pype/configurations/config.py +++ b/pype/configurations/config.py @@ -13,7 +13,7 @@ POP_KEY = "__pop_key__" # Paths to studio and project overrides STUDIO_OVERRIDES_PATH = os.environ["PYPE_CONFIG"] -SYSTEM_CONFIGURATIONS_DIR = "studio_configurations" +SYSTEM_CONFIGURATIONS_DIR = "system_configurations" SYSTEM_CONFIGURATIONS_PATH = os.path.join( STUDIO_OVERRIDES_PATH, SYSTEM_CONFIGURATIONS_DIR ) @@ -147,7 +147,7 @@ def load_jsons_from_dir(path, *args, **kwargs): return output -def studio_configurations(*args, **kwargs): +def system_configurations(*args, **kwargs): return load_jsons_from_dir(SYSTEM_CONFIGURATIONS_PATH, *args, **kwargs) diff --git a/pype/tools/config_setting/config_setting/widgets/base.py b/pype/tools/config_setting/config_setting/widgets/base.py index 82a5d024b0..aaa9831c61 100644 --- a/pype/tools/config_setting/config_setting/widgets/base.py +++ b/pype/tools/config_setting/config_setting/widgets/base.py @@ -135,7 +135,7 @@ class SystemWidget(QtWidgets.QWidget): all_values = all_values["system"] # Load studio data with metadata - current_configurations = config.studio_configurations() + current_configurations = config.system_configurations() keys_to_file = lib.file_keys_from_schema(self.schema) for key_sequence in keys_to_file: @@ -171,10 +171,9 @@ class SystemWidget(QtWidgets.QWidget): self._update_values() def _update_values(self): - config.default_configuration() - values = {"system": config.studio_configurations()} + system_values = {"system": config.system_configurations()} for input_field in self.input_fields: - input_field.update_studio_values(values) + input_field.update_studio_values(system_values) for input_field in self.input_fields: input_field.hierarchical_style_update() From 35bc72014e5046cdeb9774b7995ab02bb4cd81ed Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 9 Sep 2020 12:21:22 +0200 Subject: [PATCH 484/813] bases sets also default values --- .../config_setting/config_setting/widgets/base.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/base.py b/pype/tools/config_setting/config_setting/widgets/base.py index aaa9831c61..55ca6096de 100644 --- a/pype/tools/config_setting/config_setting/widgets/base.py +++ b/pype/tools/config_setting/config_setting/widgets/base.py @@ -171,6 +171,11 @@ class SystemWidget(QtWidgets.QWidget): self._update_values() def _update_values(self): + default_values = config.default_configuration() + default_values = {"system": default_values["system_configurations"]} + for input_field in self.input_fields: + input_field.update_default_values(default_values) + system_values = {"system": config.system_configurations()} for input_field in self.input_fields: input_field.update_studio_values(system_values) @@ -552,9 +557,14 @@ class ProjectWidget(QtWidgets.QWidget): self._update_values() def _update_values(self): - values = {"project": config.global_project_configurations()} + default_values = config.default_configuration() + default_values = {"project": default_values["project_configurations"]} for input_field in self.input_fields: - input_field.update_studio_values(values) + input_field.update_default_values(default_values) + + studio_values = {"project": config.global_project_configurations()} + for input_field in self.input_fields: + input_field.update_studio_values(studio_values) for input_field in self.input_fields: input_field.hierarchical_style_update() From 3504f06d7b14f26e6068279bf3e238e03f555d47 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 9 Sep 2020 12:32:26 +0200 Subject: [PATCH 485/813] removed log from abstract attributes --- pype/tools/config_setting/config_setting/widgets/widgets.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/widgets.py b/pype/tools/config_setting/config_setting/widgets/widgets.py index a76f4f6f35..9e814a81e5 100644 --- a/pype/tools/config_setting/config_setting/widgets/widgets.py +++ b/pype/tools/config_setting/config_setting/widgets/widgets.py @@ -234,12 +234,6 @@ class AbstractConfigObject: ) return super(AbstractConfigObject, self).__getattribute__(name) - @property - def log(self): - raise NotImplementedError( - "{} does not have implemented `log`".format(self) - ) - @property def is_modified(self): """Has object any changes that require saving.""" From 2eb16374dc69c3118738c515f18a52092c181186 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 9 Sep 2020 12:43:54 +0200 Subject: [PATCH 486/813] modified configurations files to match more current schemas for able to test --- .../nukestudio/tags.json | 262 ------------------ .../{ => plugins}/celaction/publish.json | 0 .../{ => plugins}/config.json | 0 .../{ => plugins}/ftrack/publish.json | 0 .../{ => plugins}/global/create.json | 0 .../{ => plugins}/global/filter.json | 0 .../{ => plugins}/global/load.json | 0 .../{ => plugins}/global/publish.json | 4 +- .../{ => plugins}/maya/create.json | 0 .../{ => plugins}/maya/filter.json | 0 .../{ => plugins}/maya/load.json | 0 .../{ => plugins}/maya/publish.json | 0 .../{ => plugins}/maya/workfile_build.json | 0 .../{ => plugins}/nuke/create.json | 0 .../{ => plugins}/nuke/load.json | 0 .../{ => plugins}/nuke/publish.json | 0 .../{ => plugins}/nuke/workfile_build.json | 0 .../{ => plugins}/nukestudio/filter.json | 0 .../{ => plugins}/nukestudio/publish.json | 0 .../{ => plugins}/resolve/create.json | 0 .../standalonepublisher/publish.json | 0 .../{ => plugins}/test/create.json | 0 .../{ => plugins}/test/publish.json | 0 23 files changed, 3 insertions(+), 263 deletions(-) delete mode 100644 pype/configurations/defaults/project_configurations/nukestudio/tags.json rename pype/configurations/defaults/project_configurations/{ => plugins}/celaction/publish.json (100%) rename pype/configurations/defaults/project_configurations/{ => plugins}/config.json (100%) rename pype/configurations/defaults/project_configurations/{ => plugins}/ftrack/publish.json (100%) rename pype/configurations/defaults/project_configurations/{ => plugins}/global/create.json (100%) rename pype/configurations/defaults/project_configurations/{ => plugins}/global/filter.json (100%) rename pype/configurations/defaults/project_configurations/{ => plugins}/global/load.json (100%) rename pype/configurations/defaults/project_configurations/{ => plugins}/global/publish.json (98%) rename pype/configurations/defaults/project_configurations/{ => plugins}/maya/create.json (100%) rename pype/configurations/defaults/project_configurations/{ => plugins}/maya/filter.json (100%) rename pype/configurations/defaults/project_configurations/{ => plugins}/maya/load.json (100%) rename pype/configurations/defaults/project_configurations/{ => plugins}/maya/publish.json (100%) rename pype/configurations/defaults/project_configurations/{ => plugins}/maya/workfile_build.json (100%) rename pype/configurations/defaults/project_configurations/{ => plugins}/nuke/create.json (100%) rename pype/configurations/defaults/project_configurations/{ => plugins}/nuke/load.json (100%) rename pype/configurations/defaults/project_configurations/{ => plugins}/nuke/publish.json (100%) rename pype/configurations/defaults/project_configurations/{ => plugins}/nuke/workfile_build.json (100%) rename pype/configurations/defaults/project_configurations/{ => plugins}/nukestudio/filter.json (100%) rename pype/configurations/defaults/project_configurations/{ => plugins}/nukestudio/publish.json (100%) rename pype/configurations/defaults/project_configurations/{ => plugins}/resolve/create.json (100%) rename pype/configurations/defaults/project_configurations/{ => plugins}/standalonepublisher/publish.json (100%) rename pype/configurations/defaults/project_configurations/{ => plugins}/test/create.json (100%) rename pype/configurations/defaults/project_configurations/{ => plugins}/test/publish.json (100%) diff --git a/pype/configurations/defaults/project_configurations/nukestudio/tags.json b/pype/configurations/defaults/project_configurations/nukestudio/tags.json deleted file mode 100644 index 56fcfcbce9..0000000000 --- a/pype/configurations/defaults/project_configurations/nukestudio/tags.json +++ /dev/null @@ -1,262 +0,0 @@ -{ - "Hierarchy": { - "editable": "1", - "note": "{folder}/{sequence}/{shot}", - "icon": { - "path": "hierarchy.png" - }, - "metadata": { - "folder": "FOLDER_NAME", - "shot": "{clip}", - "track": "{track}", - "sequence": "{sequence}", - "episode": "EPISODE_NAME", - "root": "{projectroot}" - } - }, - "Source Resolution": { - "editable": "1", - "note": "Use source resolution", - "icon": { - "path": "resolution.png" - }, - "metadata": { - "family": "resolution" - } - }, - "Retiming": { - "editable": "1", - "note": "Clip has retime or TimeWarp effects (or multiple effects stacked on the clip)", - "icon": { - "path": "retiming.png" - }, - "metadata": { - "family": "retiming", - "marginIn": 1, - "marginOut": 1 - } - }, - "Frame start": { - "editable": "1", - "note": "Starting frame for comps. \n\n> Use `value` and add either number or write `source` (if you want to preserve source frame numbering)", - "icon": { - "path": "icons:TagBackground.png" - }, - "metadata": { - "family": "frameStart", - "value": "1001" - } - }, - "[Lenses]": { - "Set lense here": { - "editable": "1", - "note": "Adjust parameters of your lense and then drop to clip. Remember! You can always overwrite on clip", - "icon": { - "path": "lense.png" - }, - "metadata": { - "focalLengthMm": 57 - - } - } - }, - "[Subsets]": { - "Audio": { - "editable": "1", - "note": "Export with Audio", - "icon": { - "path": "volume.png" - }, - "metadata": { - "family": "audio", - "subset": "main" - } - }, - "plateFg": { - "editable": "1", - "note": "Add to publish to \"forground\" subset. Change metadata subset name if different order number", - "icon": { - "path": "z_layer_fg.png" - }, - "metadata": { - "family": "plate", - "subset": "Fg01" - } - }, - "plateBg": { - "editable": "1", - "note": "Add to publish to \"background\" subset. Change metadata subset name if different order number", - "icon": { - "path": "z_layer_bg.png" - }, - "metadata": { - "family": "plate", - "subset": "Bg01" - } - }, - "plateRef": { - "editable": "1", - "note": "Add to publish to \"reference\" subset.", - "icon": { - "path": "icons:Reference.png" - }, - "metadata": { - "family": "plate", - "subset": "Ref" - } - }, - "plateMain": { - "editable": "1", - "note": "Add to publish to \"main\" subset.", - "icon": { - "path": "z_layer_main.png" - }, - "metadata": { - "family": "plate", - "subset": "main" - } - }, - "plateProxy": { - "editable": "1", - "note": "Add to publish to \"proxy\" subset.", - "icon": { - "path": "z_layer_main.png" - }, - "metadata": { - "family": "plate", - "subset": "proxy" - } - }, - "review": { - "editable": "1", - "note": "Upload to Ftrack as review component.", - "icon": { - "path": "review.png" - }, - "metadata": { - "family": "review", - "track": "review" - } - } - }, - "[Handles]": { - "start: add 20 frames": { - "editable": "1", - "note": "Adding frames to start of selected clip", - "icon": { - "path": "3_add_handles_start.png" - }, - "metadata": { - "family": "handles", - "value": "20", - "args": "{'op':'add','where':'start'}" - } - }, - "start: add 10 frames": { - "editable": "1", - "note": "Adding frames to start of selected clip", - "icon": { - "path": "3_add_handles_start.png" - }, - "metadata": { - "family": "handles", - "value": "10", - "args": "{'op':'add','where':'start'}" - } - }, - "start: add 5 frames": { - "editable": "1", - "note": "Adding frames to start of selected clip", - "icon": { - "path": "3_add_handles_start.png" - }, - "metadata": { - "family": "handles", - "value": "5", - "args": "{'op':'add','where':'start'}" - } - }, - "start: add 0 frames": { - "editable": "1", - "note": "Adding frames to start of selected clip", - "icon": { - "path": "3_add_handles_start.png" - }, - "metadata": { - "family": "handles", - "value": "0", - "args": "{'op':'add','where':'start'}" - } - }, - "end: add 20 frames": { - "editable": "1", - "note": "Adding frames to end of selected clip", - "icon": { - "path": "1_add_handles_end.png" - }, - "metadata": { - "family": "handles", - "value": "20", - "args": "{'op':'add','where':'end'}" - } - }, - "end: add 10 frames": { - "editable": "1", - "note": "Adding frames to end of selected clip", - "icon": { - "path": "1_add_handles_end.png" - }, - "metadata": { - "family": "handles", - "value": "10", - "args": "{'op':'add','where':'end'}" - } - }, - "end: add 5 frames": { - "editable": "1", - "note": "Adding frames to end of selected clip", - "icon": { - "path": "1_add_handles_end.png" - }, - "metadata": { - "family": "handles", - "value": "5", - "args": "{'op':'add','where':'end'}" - } - }, - "end: add 0 frames": { - "editable": "1", - "note": "Adding frames to end of selected clip", - "icon": { - "path": "1_add_handles_end.png" - }, - "metadata": { - "family": "handles", - "value": "0", - "args": "{'op':'add','where':'end'}" - } - } - }, - "NukeScript": { - "editable": "1", - "note": "Collecting track items to Nuke scripts.", - "icon": { - "path": "icons:TagNuke.png" - }, - "metadata": { - "family": "nukescript", - "subset": "main" - } - }, - "Comment": { - "editable": "1", - "note": "Comment on a shot.", - "icon": { - "path": "icons:TagComment.png" - }, - "metadata": { - "family": "comment", - "subset": "main" - } - } -} diff --git a/pype/configurations/defaults/project_configurations/celaction/publish.json b/pype/configurations/defaults/project_configurations/plugins/celaction/publish.json similarity index 100% rename from pype/configurations/defaults/project_configurations/celaction/publish.json rename to pype/configurations/defaults/project_configurations/plugins/celaction/publish.json diff --git a/pype/configurations/defaults/project_configurations/config.json b/pype/configurations/defaults/project_configurations/plugins/config.json similarity index 100% rename from pype/configurations/defaults/project_configurations/config.json rename to pype/configurations/defaults/project_configurations/plugins/config.json diff --git a/pype/configurations/defaults/project_configurations/ftrack/publish.json b/pype/configurations/defaults/project_configurations/plugins/ftrack/publish.json similarity index 100% rename from pype/configurations/defaults/project_configurations/ftrack/publish.json rename to pype/configurations/defaults/project_configurations/plugins/ftrack/publish.json diff --git a/pype/configurations/defaults/project_configurations/global/create.json b/pype/configurations/defaults/project_configurations/plugins/global/create.json similarity index 100% rename from pype/configurations/defaults/project_configurations/global/create.json rename to pype/configurations/defaults/project_configurations/plugins/global/create.json diff --git a/pype/configurations/defaults/project_configurations/global/filter.json b/pype/configurations/defaults/project_configurations/plugins/global/filter.json similarity index 100% rename from pype/configurations/defaults/project_configurations/global/filter.json rename to pype/configurations/defaults/project_configurations/plugins/global/filter.json diff --git a/pype/configurations/defaults/project_configurations/global/load.json b/pype/configurations/defaults/project_configurations/plugins/global/load.json similarity index 100% rename from pype/configurations/defaults/project_configurations/global/load.json rename to pype/configurations/defaults/project_configurations/plugins/global/load.json diff --git a/pype/configurations/defaults/project_configurations/global/publish.json b/pype/configurations/defaults/project_configurations/plugins/global/publish.json similarity index 98% rename from pype/configurations/defaults/project_configurations/global/publish.json rename to pype/configurations/defaults/project_configurations/plugins/global/publish.json index 3c5de85e68..d531f1aa47 100644 --- a/pype/configurations/defaults/project_configurations/global/publish.json +++ b/pype/configurations/defaults/project_configurations/plugins/global/publish.json @@ -3,6 +3,7 @@ "enabled": false }, "ExtractJpegEXR": { + "enabled": true, "ffmpeg_args": { "input": [ "-gamma 2.2" @@ -74,6 +75,7 @@ ] }, "IntegrateAssetNew": { + "enabled": true, "template_name_profiles": { "publish": { "families": [], @@ -94,4 +96,4 @@ "deadline_pool": "", "deadline_group": "" } -} \ No newline at end of file +} diff --git a/pype/configurations/defaults/project_configurations/maya/create.json b/pype/configurations/defaults/project_configurations/plugins/maya/create.json similarity index 100% rename from pype/configurations/defaults/project_configurations/maya/create.json rename to pype/configurations/defaults/project_configurations/plugins/maya/create.json diff --git a/pype/configurations/defaults/project_configurations/maya/filter.json b/pype/configurations/defaults/project_configurations/plugins/maya/filter.json similarity index 100% rename from pype/configurations/defaults/project_configurations/maya/filter.json rename to pype/configurations/defaults/project_configurations/plugins/maya/filter.json diff --git a/pype/configurations/defaults/project_configurations/maya/load.json b/pype/configurations/defaults/project_configurations/plugins/maya/load.json similarity index 100% rename from pype/configurations/defaults/project_configurations/maya/load.json rename to pype/configurations/defaults/project_configurations/plugins/maya/load.json diff --git a/pype/configurations/defaults/project_configurations/maya/publish.json b/pype/configurations/defaults/project_configurations/plugins/maya/publish.json similarity index 100% rename from pype/configurations/defaults/project_configurations/maya/publish.json rename to pype/configurations/defaults/project_configurations/plugins/maya/publish.json diff --git a/pype/configurations/defaults/project_configurations/maya/workfile_build.json b/pype/configurations/defaults/project_configurations/plugins/maya/workfile_build.json similarity index 100% rename from pype/configurations/defaults/project_configurations/maya/workfile_build.json rename to pype/configurations/defaults/project_configurations/plugins/maya/workfile_build.json diff --git a/pype/configurations/defaults/project_configurations/nuke/create.json b/pype/configurations/defaults/project_configurations/plugins/nuke/create.json similarity index 100% rename from pype/configurations/defaults/project_configurations/nuke/create.json rename to pype/configurations/defaults/project_configurations/plugins/nuke/create.json diff --git a/pype/configurations/defaults/project_configurations/nuke/load.json b/pype/configurations/defaults/project_configurations/plugins/nuke/load.json similarity index 100% rename from pype/configurations/defaults/project_configurations/nuke/load.json rename to pype/configurations/defaults/project_configurations/plugins/nuke/load.json diff --git a/pype/configurations/defaults/project_configurations/nuke/publish.json b/pype/configurations/defaults/project_configurations/plugins/nuke/publish.json similarity index 100% rename from pype/configurations/defaults/project_configurations/nuke/publish.json rename to pype/configurations/defaults/project_configurations/plugins/nuke/publish.json diff --git a/pype/configurations/defaults/project_configurations/nuke/workfile_build.json b/pype/configurations/defaults/project_configurations/plugins/nuke/workfile_build.json similarity index 100% rename from pype/configurations/defaults/project_configurations/nuke/workfile_build.json rename to pype/configurations/defaults/project_configurations/plugins/nuke/workfile_build.json diff --git a/pype/configurations/defaults/project_configurations/nukestudio/filter.json b/pype/configurations/defaults/project_configurations/plugins/nukestudio/filter.json similarity index 100% rename from pype/configurations/defaults/project_configurations/nukestudio/filter.json rename to pype/configurations/defaults/project_configurations/plugins/nukestudio/filter.json diff --git a/pype/configurations/defaults/project_configurations/nukestudio/publish.json b/pype/configurations/defaults/project_configurations/plugins/nukestudio/publish.json similarity index 100% rename from pype/configurations/defaults/project_configurations/nukestudio/publish.json rename to pype/configurations/defaults/project_configurations/plugins/nukestudio/publish.json diff --git a/pype/configurations/defaults/project_configurations/resolve/create.json b/pype/configurations/defaults/project_configurations/plugins/resolve/create.json similarity index 100% rename from pype/configurations/defaults/project_configurations/resolve/create.json rename to pype/configurations/defaults/project_configurations/plugins/resolve/create.json diff --git a/pype/configurations/defaults/project_configurations/standalonepublisher/publish.json b/pype/configurations/defaults/project_configurations/plugins/standalonepublisher/publish.json similarity index 100% rename from pype/configurations/defaults/project_configurations/standalonepublisher/publish.json rename to pype/configurations/defaults/project_configurations/plugins/standalonepublisher/publish.json diff --git a/pype/configurations/defaults/project_configurations/test/create.json b/pype/configurations/defaults/project_configurations/plugins/test/create.json similarity index 100% rename from pype/configurations/defaults/project_configurations/test/create.json rename to pype/configurations/defaults/project_configurations/plugins/test/create.json diff --git a/pype/configurations/defaults/project_configurations/test/publish.json b/pype/configurations/defaults/project_configurations/plugins/test/publish.json similarity index 100% rename from pype/configurations/defaults/project_configurations/test/publish.json rename to pype/configurations/defaults/project_configurations/plugins/test/publish.json From 9f9bad6c5425c97b944be59926db94450705571a Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 9 Sep 2020 12:44:16 +0200 Subject: [PATCH 487/813] first commit of update_default_values --- .../config_setting/widgets/anatomy_inputs.py | 35 +++++++++++++++ .../config_setting/widgets/inputs.py | 44 +++++++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index fbc3a3a2ed..43146f7442 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -75,6 +75,10 @@ class AnatomyWidget(QtWidgets.QWidget, ConfigObject): self.root_widget.value_changed.connect(self._on_value_change) + def update_default_values(self, value): + self.root_widget.update_default_values(value) + self.templates_widget.update_default_values(value) + def update_studio_values(self, parent_values): self._state = None self._child_state = None @@ -255,6 +259,33 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): def is_multiroot(self): return self.multiroot_checkbox.isChecked() + def update_default_values(self, parent_values): + self._state = None + self._multiroot_state = None + + if isinstance(parent_values, dict): + value = parent_values.get(self.key, NOT_SET) + else: + value = NOT_SET + + is_multiroot = False + if isinstance(value, dict): + for _value in value.values(): + if isinstance(_value, dict): + is_multiroot = True + break + + self.global_is_multiroot = is_multiroot + self.was_multiroot = is_multiroot + self.set_multiroot(is_multiroot) + + if is_multiroot: + self.singleroot_widget.update_studio_values(NOT_SET) + self.multiroot_widget.update_studio_values(value) + else: + self.singleroot_widget.update_studio_values(value) + self.multiroot_widget.update_studio_values(NOT_SET) + def update_studio_values(self, parent_values): self._state = None self._multiroot_state = None @@ -486,6 +517,10 @@ class TemplatesWidget(QtWidgets.QWidget, ConfigObject): layout.addWidget(body_widget) + def update_default_values(self, values): + self._state = None + self.value_input.update_default_values(values) + def update_studio_values(self, values): self._state = None self.value_input.update_studio_values(values) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 057ed3b584..27de95c402 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -174,6 +174,26 @@ class ConfigObject(AbstractConfigObject): class InputObject(ConfigObject): + def update_default_values(self, parent_values): + value = NOT_SET + if self._as_widget: + value = parent_values + elif parent_values is not NOT_SET: + value = parent_values.get(self.key, NOT_SET) + + if value is NOT_SET: + if self._as_widget: + print(self) + elif hasattr(self, "_parent"): + print(self._parent.key, self.key, self) + raise ValueError( + "Default value is not set. This is implementation BUG." + ) + self.default_value = value + if not self.has_studio_override: + print(value) + self.set_value(value) + def overrides(self): if not self.is_overriden: return NOT_SET, False @@ -1666,6 +1686,16 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): for item in self.input_fields: item.set_as_overriden() + def update_default_values(self, parent_values): + value = NOT_SET + if self._as_widget: + value = parent_values + elif parent_values is not NOT_SET: + value = parent_values.get(self.key, NOT_SET) + + for item in self.input_fields: + item.update_default_values(value) + def update_studio_values(self, parent_values): value = NOT_SET if parent_values is not NOT_SET: @@ -1932,6 +1962,16 @@ class DictInvisible(QtWidgets.QWidget, ConfigObject): for item in self.input_fields: item.set_as_overriden() + def update_default_values(self, parent_values): + value = NOT_SET + if self._as_widget: + value = parent_values + elif parent_values is not NOT_SET: + value = parent_values.get(self.key, NOT_SET) + + for item in self.input_fields: + item.update_default_values(value) + def update_studio_values(self, parent_values): value = NOT_SET if parent_values is not NOT_SET: @@ -2367,6 +2407,10 @@ class DictFormWidget(QtWidgets.QWidget, ConfigObject): for item in self.input_fields: item.set_as_overriden() + def update_default_values(self, value): + for item in self.input_fields: + item.update_default_values(value) + def update_studio_values(self, value): for item in self.input_fields: item.update_studio_values(value) From 24753d6af787e511768c1d759ae8693fa9485162 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 9 Sep 2020 13:34:27 +0200 Subject: [PATCH 488/813] cleaned debug part --- pype/tools/config_setting/config_setting/widgets/inputs.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 27de95c402..989980f2a0 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -182,16 +182,12 @@ class InputObject(ConfigObject): value = parent_values.get(self.key, NOT_SET) if value is NOT_SET: - if self._as_widget: - print(self) - elif hasattr(self, "_parent"): - print(self._parent.key, self.key, self) raise ValueError( "Default value is not set. This is implementation BUG." ) + self.default_value = value if not self.has_studio_override: - print(value) self.set_value(value) def overrides(self): From dbfe43e821372445a0bceb48d8890e534d7c9d7b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 9 Sep 2020 14:03:17 +0200 Subject: [PATCH 489/813] update studio_values should work with new default_values --- .../config_setting/widgets/inputs.py | 216 ++++-------------- 1 file changed, 42 insertions(+), 174 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 989980f2a0..fa07419fde 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -190,6 +190,24 @@ class InputObject(ConfigObject): if not self.has_studio_override: self.set_value(value) + def update_studio_values(self, parent_values): + value = NOT_SET + if self._as_widget: + value = parent_values + elif parent_values is not NOT_SET: + value = parent_values.get(self.key, NOT_SET) + + self.studio_value = value + if value is not NOT_SET: + self.set_value(value) + self._has_studio_override = True + + else: + self.set_value(self.default_value) + self._has_studio_override = False + + self._is_modified = False + def overrides(self): if not self.is_overriden: return NOT_SET, False @@ -199,7 +217,10 @@ class InputObject(ConfigObject): self.update_style() def remove_overrides(self): - self.set_value(self.start_value) + if self.has_studio_override: + self.set_value(self.studio_value) + else: + self.set_value(self.default_value) self._is_overriden = False self._is_modified = False @@ -217,7 +238,10 @@ class InputObject(ConfigObject): if override_value is NOT_SET: self._is_overriden = False self._was_overriden = False - value = self.start_value + if self.has_studio_override: + value = self.studio_value + else: + value = self.default_value else: self._is_overriden = True self._was_overriden = True @@ -234,7 +258,10 @@ class InputObject(ConfigObject): ): self.set_value(self.override_value) else: - self.set_value(self.start_value) + if self.has_studio_override: + self.set_value(self.studio_value) + else: + self.set_value(self.defaul_value) if not self.is_overidable: self._is_modified = self.studio_value != self.item_value() @@ -290,7 +317,6 @@ class BooleanWidget(QtWidgets.QWidget, InputObject): self.default_value = NOT_SET self.studio_value = NOT_SET self.override_value = NOT_SET - self.start_value = NOT_SET layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) @@ -316,24 +342,6 @@ class BooleanWidget(QtWidgets.QWidget, InputObject): self.checkbox.stateChanged.connect(self._on_value_change) - def update_studio_values(self, parent_values): - value = NOT_SET - if self._as_widget: - value = parent_values - elif parent_values is not NOT_SET: - value = parent_values.get(self.key, NOT_SET) - - if value is not NOT_SET: - self.set_value(value) - - elif self.default_value is not NOT_SET: - self.set_value(self.default_value) - - self.studio_value = value - self.start_value = self.item_value() - - self._is_modified = self.studio_value != self.start_value - def set_value(self, value): # Ignore value change because if `self.isChecked()` has same # value as `value` the `_on_value_change` is not triggered @@ -399,7 +407,6 @@ class NumberWidget(QtWidgets.QWidget, InputObject): self.default_value = NOT_SET self.studio_value = NOT_SET self.override_value = NOT_SET - self.start_value = NOT_SET layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) @@ -426,25 +433,6 @@ class NumberWidget(QtWidgets.QWidget, InputObject): self.input_field.valueChanged.connect(self._on_value_change) - def update_studio_values(self, parent_values): - value = NOT_SET - if self._as_widget: - value = parent_values - else: - if parent_values is not NOT_SET: - value = parent_values.get(self.key, NOT_SET) - - if value is not NOT_SET: - self.set_value(value) - - elif self.default_value is not NOT_SET: - self.set_value(self.default_value) - - self.studio_value = value - self.start_value = self.item_value() - - self._is_modified = self.studio_value != self.start_value - def set_value(self, value): self.input_field.setValue(value) @@ -510,7 +498,6 @@ class TextWidget(QtWidgets.QWidget, InputObject): self.default_value = NOT_SET self.studio_value = NOT_SET self.override_value = NOT_SET - self.start_value = NOT_SET layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) @@ -535,24 +522,6 @@ class TextWidget(QtWidgets.QWidget, InputObject): self.text_input.textChanged.connect(self._on_value_change) - def update_studio_values(self, parent_values): - value = NOT_SET - if self._as_widget: - value = parent_values - elif parent_values is not NOT_SET: - value = parent_values.get(self.key, NOT_SET) - - if value is not NOT_SET: - self.set_value(value) - - elif self.default_value is not NOT_SET: - self.set_value(self.default_value) - - self.studio_value = value - self.start_value = self.item_value() - - self._is_modified = self.studio_value != self.start_value - def set_value(self, value): if self.multiline: self.text_input.setPlainText(value) @@ -622,7 +591,6 @@ class PathInputWidget(QtWidgets.QWidget, InputObject): self.default_value = NOT_SET self.studio_value = NOT_SET self.override_value = NOT_SET - self.start_value = NOT_SET layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) @@ -642,24 +610,6 @@ class PathInputWidget(QtWidgets.QWidget, InputObject): self.path_input.textChanged.connect(self._on_value_change) - def update_studio_values(self, parent_values): - value = NOT_SET - if self._as_widget: - value = parent_values - elif parent_values is not NOT_SET: - value = parent_values.get(self.key, NOT_SET) - - if value is not NOT_SET: - self.set_value(value) - - elif self.default_value is not NOT_SET: - self.set_value(self.default_value) - - self.studio_value = value - self.start_value = self.item_value() - - self._is_modified = self.studio_value != self.start_value - def set_value(self, value): self.path_input.setText(value) @@ -785,7 +735,6 @@ class RawJsonWidget(QtWidgets.QWidget, InputObject): self.default_value = NOT_SET self.studio_value = NOT_SET self.override_value = NOT_SET - self.start_value = NOT_SET layout = QtWidgets.QVBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) @@ -811,25 +760,10 @@ class RawJsonWidget(QtWidgets.QWidget, InputObject): self.text_input.textChanged.connect(self._on_value_change) def update_studio_values(self, parent_values): - value = NOT_SET - if self._as_widget: - value = parent_values - elif parent_values is not NOT_SET: - value = parent_values.get(self.key, NOT_SET) - - if value is not NOT_SET: - self.set_value(value) - - elif self.default_value is not NOT_SET: - self.set_value(self.default_value) + super(RawJsonWidget, self).update_studio_values(parent_values) self._is_invalid = self.text_input.has_invalid_value() - self.studio_value = value - self.start_value = self.item_value() - - self._is_modified = self.studio_value != self.start_value - def set_value(self, value): self.text_input.set_value(value) @@ -982,7 +916,6 @@ class ListWidget(QtWidgets.QWidget, InputObject): self.default_value = NOT_SET self.studio_value = NOT_SET self.override_value = NOT_SET - self.start_value = NOT_SET self.key = input_data["key"] @@ -1015,33 +948,8 @@ class ListWidget(QtWidgets.QWidget, InputObject): return len(self.input_fields) def update_studio_values(self, parent_values): - old_inputs = tuple(self.input_fields) + super(ListWidget, self).update_studio_values(parent_values) - value = NOT_SET - if self._as_widget: - value = parent_values - elif parent_values is not NOT_SET: - value = parent_values.get(self.key, NOT_SET) - - self.studio_value = value - - if value is not NOT_SET: - for item_value in value: - self.add_row(value=item_value) - - elif self.default_value is not NOT_SET: - for item_value in self.default_value: - self.add_row(value=item_value) - - for old_input in old_inputs: - self.remove_row(old_input) - - if self.count() == 0: - self.add_row(is_empty=True) - - self.start_value = self.item_value() - - self._is_modified = self.studio_value != self.start_value self.hierarchical_style_update() def set_value(self, value): @@ -1052,6 +960,9 @@ class ListWidget(QtWidgets.QWidget, InputObject): for input_field in previous_inputs: self.remove_row(input_field) + if self.count() == 0: + self.add_row(is_empty=True) + def _on_value_change(self, item=None): if self.ignore_value_changes: return @@ -1128,7 +1039,10 @@ class ListWidget(QtWidgets.QWidget, InputObject): if override_value is NOT_SET: self._is_overriden = False self._was_overriden = False - value = self.start_value + if self.has_studio_override: + value = self.studio_value + else: + value = self.default_value else: self._is_overriden = True self._was_overriden = True @@ -1326,7 +1240,6 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): self.default_value = NOT_SET self.studio_value = NOT_SET self.override_value = NOT_SET - self.start_value = NOT_SET any_parent_is_group = parent.is_group if not any_parent_is_group: @@ -1380,35 +1293,6 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): def count(self): return len(self.input_fields) - def update_studio_values(self, parent_values): - old_inputs = tuple(self.input_fields) - - value = NOT_SET - if self._as_widget: - value = parent_values - elif parent_values is not NOT_SET: - value = parent_values.get(self.key, NOT_SET) - - self.studio_value = value - - if value is not NOT_SET: - for item_key, item_value in value.items(): - self.add_row(key=item_key, value=item_value) - - elif self.default_value is not NOT_SET: - for item_key, item_value in self.default_value.items(): - self.add_row(key=item_key, value=item_value) - - for old_input in old_inputs: - self.remove_row(old_input) - - if self.count() == 0: - self.add_row(is_empty=True) - - self.start_value = self.item_value() - - self._is_modified = self.studio_value != self.start_value - def set_value(self, value): previous_inputs = tuple(self.input_fields) for item_key, item_value in value.items(): @@ -1417,6 +1301,9 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): for input_field in previous_inputs: self.remove_row(input_field) + if self.count() == 0: + self.add_row(is_empty=True) + def _on_value_change(self, item=None): fields_by_keys = collections.defaultdict(list) for input_field in self.input_fields: @@ -2066,7 +1953,6 @@ class PathWidget(QtWidgets.QWidget, ConfigObject): self.default_value = NOT_SET self.studio_value = NOT_SET self.override_value = NOT_SET - self.start_value = NOT_SET self.input_fields = [] @@ -2143,24 +2029,6 @@ class PathWidget(QtWidgets.QWidget, ConfigObject): self.setFocusProxy(self.input_fields[0]) self.content_layout.addWidget(proxy_widget) - def update_studio_values(self, parent_values): - value = NOT_SET - if self._as_widget: - value = parent_values - elif parent_values is not NOT_SET: - value = parent_values.get(self.key, NOT_SET) - - if not self.multiplatform: - self.input_fields[0].update_studio_values(parent_values) - - elif self.multiplatform: - for input_field in self.input_fields: - input_field.update_studio_values(value) - - self.studio_value = value - self.start_value = self.item_value() - self._is_modified = self.studio_value != self.start_value - def apply_overrides(self, parent_values): self._is_modified = False self._state = None From 46944e87c996d2ceb987f6eafbaf9b2dda705405 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 9 Sep 2020 14:25:14 +0200 Subject: [PATCH 490/813] simplified state stylesheets --- .../config_setting/style/style.css | 68 +++++-------------- 1 file changed, 17 insertions(+), 51 deletions(-) diff --git a/pype/tools/config_setting/config_setting/style/style.css b/pype/tools/config_setting/config_setting/style/style.css index fe3bba366a..fef1278de1 100644 --- a/pype/tools/config_setting/config_setting/style/style.css +++ b/pype/tools/config_setting/config_setting/style/style.css @@ -53,21 +53,11 @@ QLabel[state="overriden"]:hover {color: #ffa64d;} QLabel[state="invalid"] {color: #ad2e2e; font-weight: bold;} QLabel[state="invalid"]:hover {color: #ad2e2e; font-weight: bold;} -QWidget[input-state="modified"] { - border-color: #137cbd; -} -QWidget[input-state="overriden-modified"] { - border-color: #137cbd; -} - -QWidget[input-state="overriden"] { - border-color: #ff8c1a; -} - -QWidget[input-state="invalid"] { - border-color: #ad2e2e; -} +QWidget[input-state="modified"] {border-color: #137cbd;} +QWidget[input-state="overriden-modified"] {border-color: #137cbd;} +QWidget[input-state="overriden"] {border-color: #ff8c1a;} +QWidget[input-state="invalid"] {border-color: #ad2e2e;} QPushButton { border: 1px solid #aaaaaa; @@ -105,19 +95,10 @@ QPushButton[btn-type="expand-toggle"] { font-weight: bold; } -#DictKey[state="modified"] { - border-color: #137cbd; -} - -#DictKey[state="overriden"] { - border-color: #00f; -} -#DictKey[state="overriden-modified"] { - border-color: #0f0; -} -#DictKey[state="invalid"] { - border-color: #ad2e2e; -} +#DictKey[state="modified"] {border-color: #137cbd;} +#DictKey[state="overriden"] {border-color: #00f;} +#DictKey[state="overriden-modified"] {border-color: #0f0;} +#DictKey[state="invalid"] {border-color: #ad2e2e;} #DictLabel { font-weight: bold; @@ -144,33 +125,18 @@ QPushButton[btn-type="expand-toggle"] { border-color: #62839d; } -#SideLineWidget[state="child-modified"]{ - border-color: #106aa2; -} -#SideLineWidget[state="child-modified"]:hover{ - border-color: #137cbd; -} -#SideLineWidget[state="child-invalid"]{ - border-color: #ad2e2e; -} -#SideLineWidget[state="child-invalid"]:hover{ - border-color: #c93636; -} +#SideLineWidget[state="child-modified"] {border-color: #106aa2;} +#SideLineWidget[state="child-modified"]:hover {border-color: #137cbd;} -#SideLineWidget[state="child-overriden"]{ - border-color: #e67300; -} -#SideLineWidget[state="child-overriden"]:hover { - border-color: #ff8c1a; -} +#SideLineWidget[state="child-invalid"] {border-color: #ad2e2e;} +#SideLineWidget[state="child-invalid"]:hover {border-color: #c93636;} -#SideLineWidget[state="child-overriden-modified"] { - border-color: #106aa2; -} -#SideLineWidget[state="child-overriden-modified"]:hover { - border-color: #137cbd; -} +#SideLineWidget[state="child-overriden"] {border-color: #e67300;} +#SideLineWidget[state="child-overriden"]:hover {border-color: #ff8c1a;} + +#SideLineWidget[state="child-overriden-modified"] {border-color: #106aa2;} +#SideLineWidget[state="child-overriden-modified"]:hover {border-color: #137cbd;} QScrollBar:horizontal { height: 15px; From 5830b637d7b17c0bb0b18e5f9b59db9376bf50a8 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 9 Sep 2020 14:25:29 +0200 Subject: [PATCH 491/813] normla label is little bit darker --- pype/tools/config_setting/config_setting/style/style.css | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pype/tools/config_setting/config_setting/style/style.css b/pype/tools/config_setting/config_setting/style/style.css index fef1278de1..61581b9124 100644 --- a/pype/tools/config_setting/config_setting/style/style.css +++ b/pype/tools/config_setting/config_setting/style/style.css @@ -38,11 +38,15 @@ QLineEdit:disabled, QSpinBox:disabled, QDoubleSpinBox:disabled, QPlainTextEdit:d QLineEdit:focus, QSpinBox:focus, QDoubleSpinBox:focus, QPlainTextEdit:focus, QTextEdit:focus { border: 1px solid #ffffff; } -QLabel, QToolButton { +QToolButton { background: transparent; } -QLabel:hover {color: #ffffff;} +QLabel { + background: transparent; + color: #808080; +} +QLabel:hover {color: #999999;} QLabel[state="modified"] {color: #137cbd;} QLabel[state="modified"]:hover {color: #1798e8;} From ac116aaa02637ddf0efad6b27229eed8ad9288f2 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 9 Sep 2020 14:25:45 +0200 Subject: [PATCH 492/813] added child_has_studio_override to abstract methods --- .../config_setting/config_setting/widgets/widgets.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/widgets.py b/pype/tools/config_setting/config_setting/widgets/widgets.py index 9e814a81e5..5547f66597 100644 --- a/pype/tools/config_setting/config_setting/widgets/widgets.py +++ b/pype/tools/config_setting/config_setting/widgets/widgets.py @@ -302,6 +302,15 @@ class AbstractConfigObject: "{} does not have implemented setter method `ignore_value_changes`" ).format(self)) + @property + def child_has_studio_override(self): + """Any children item is modified.""" + raise NotImplementedError( + "{} does not have implemented `child_has_studio_override`".format( + self + ) + ) + @property def child_modified(self): """Any children item is modified.""" From 82a29d9c2734e1a23cebe4efcabcbb9fc6a9b945 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 9 Sep 2020 14:25:58 +0200 Subject: [PATCH 493/813] implemented child_has_studio_override for inputs --- .../config_setting/widgets/inputs.py | 108 +++++++++++++++--- 1 file changed, 93 insertions(+), 15 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index fa07419fde..a9cbc502e9 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -94,9 +94,13 @@ class ConfigObject(AbstractConfigObject): return {self.key: self.item_value()} @classmethod - def style_state(cls, is_invalid, is_overriden, is_modified): + def style_state( + cls, has_studio_override, is_invalid, is_overriden, is_modified + ): items = [] - if is_invalid: + if has_studio_override: + items.append("studio") + elif is_invalid: items.append("invalid") else: if is_overriden: @@ -261,10 +265,13 @@ class InputObject(ConfigObject): if self.has_studio_override: self.set_value(self.studio_value) else: - self.set_value(self.defaul_value) + self.set_value(self.default_value) if not self.is_overidable: - self._is_modified = self.studio_value != self.item_value() + if self.has_studio_override: + self._is_modified = self.studio_value != self.item_value() + else: + self._is_modified = self.default_value != self.item_value() self._is_overriden = False return @@ -274,6 +281,10 @@ class InputObject(ConfigObject): def set_as_overriden(self): self._is_overriden = True + @property + def child_has_studio_override(self): + return self.has_studio_override + @property def child_modified(self): return self.is_modified @@ -367,7 +378,10 @@ class BooleanWidget(QtWidgets.QWidget, InputObject): def update_style(self): state = self.style_state( - self.is_invalid, self.is_overriden, self.is_modified + self.has_studio_override, + self.is_invalid, + self.is_overriden, + self.is_modified ) if self._state == state: return @@ -456,7 +470,10 @@ class NumberWidget(QtWidgets.QWidget, InputObject): def update_style(self): state = self.style_state( - self.is_invalid, self.is_overriden, self.is_modified + self.has_studio_override, + self.is_invalid, + self.is_overriden, + self.is_modified ) if self._state == state: return @@ -548,7 +565,10 @@ class TextWidget(QtWidgets.QWidget, InputObject): def update_style(self): state = self.style_state( - self.is_invalid, self.is_overriden, self.is_modified + self.has_studio_override, + self.is_invalid, + self.is_overriden, + self.is_modified ) if self._state == state: return @@ -637,7 +657,10 @@ class PathInputWidget(QtWidgets.QWidget, InputObject): def update_style(self): state = self.style_state( - self.is_invalid, self.is_overriden, self.is_modified + self.has_studio_override, + self.is_invalid, + self.is_overriden, + self.is_modified ) if self._state == state: return @@ -788,7 +811,10 @@ class RawJsonWidget(QtWidgets.QWidget, InputObject): def update_style(self): state = self.style_state( - self.is_invalid, self.is_overriden, self.is_modified + self.has_studio_override, + self.is_invalid, + self.is_overriden, + self.is_modified ) if self._state == state: return @@ -876,6 +902,10 @@ class ListItem(QtWidgets.QWidget, ConfigObject): return self.value_input.item_value() return NOT_SET + @property + def child_has_studio_override(self): + return self.value_input.child_has_studio_override + @property def child_modified(self): return self.value_input.child_modified @@ -1060,7 +1090,10 @@ class ListWidget(QtWidgets.QWidget, InputObject): def update_style(self): state = self.style_state( - self.is_invalid, self.is_overriden, self.is_modified + self.has_studio_override, + self.is_invalid, + self.is_overriden, + self.is_modified ) if self._state == state: return @@ -1345,7 +1378,10 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): def update_style(self): state = self.style_state( - self.is_invalid, self.is_overriden, self.is_modified + self.has_studio_override, + self.is_invalid, + self.is_overriden, + self.is_modified ) if self._state == state: return @@ -1633,10 +1669,14 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): self.update_style() def update_style(self, is_overriden=None): + child_has_studio_override = self.child_has_studio_override child_modified = self.child_modified child_invalid = self.child_invalid child_state = self.style_state( - child_invalid, self.child_overriden, child_modified + child_has_studio_override, + child_invalid, + self.child_overriden, + child_modified ) if child_state: child_state = "child-{}".format(child_state) @@ -1649,7 +1689,10 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): self._child_state = child_state state = self.style_state( - child_invalid, self.is_overriden, self.is_modified + child_has_studio_override, + child_invalid, + self.is_overriden, + self.is_modified ) if self._state == state: return @@ -1665,6 +1708,13 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): return self._is_modified or self.child_modified return False + @property + def child_has_studio_override(self): + for input_field in self.input_fields: + if input_field.child_has_studio_override: + return True + return False + @property def child_modified(self): for input_field in self.input_fields: @@ -1768,6 +1818,13 @@ class DictInvisible(QtWidgets.QWidget, ConfigObject): def update_style(self, *args, **kwargs): return + @property + def child_has_studio_override(self): + for input_field in self.input_fields: + if input_field.child_has_studio_override: + return True + return False + @property def child_modified(self): for input_field in self.input_fields: @@ -2085,10 +2142,14 @@ class PathWidget(QtWidgets.QWidget, ConfigObject): self.value_changed.emit(self) def update_style(self, is_overriden=None): + child_has_studio_override = self.has_studio_override child_modified = self.child_modified child_invalid = self.child_invalid child_state = self.style_state( - child_invalid, self.child_overriden, child_modified + child_has_studio_override, + child_invalid, + self.child_overriden, + child_modified ) if child_state: child_state = "child-{}".format(child_state) @@ -2100,7 +2161,10 @@ class PathWidget(QtWidgets.QWidget, ConfigObject): if not self._as_widget: state = self.style_state( - child_invalid, self.is_overriden, self.is_modified + child_has_studio_override, + child_invalid, + self.is_overriden, + self.is_modified ) if self._state == state: return @@ -2128,6 +2192,13 @@ class PathWidget(QtWidgets.QWidget, ConfigObject): def set_as_overriden(self): self._is_overriden = True + @property + def child_has_studio_override(self): + for input_field in self.input_fields: + if input_field.child_has_studio_override: + return True + return False + @property def child_modified(self): for input_field in self.input_fields: @@ -2286,6 +2357,13 @@ class DictFormWidget(QtWidgets.QWidget, ConfigObject): if self.any_parent_is_group: self.hierarchical_style_update() + @property + def child_has_studio_override(self): + for input_field in self.input_fields: + if input_field.child_has_studio_override: + return True + return False + @property def child_modified(self): for input_field in self.input_fields: From 84108c5fda60808b0b2ea4f8424eccfa1b16aa6a Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 9 Sep 2020 14:46:44 +0200 Subject: [PATCH 494/813] fixed state values and added styles for studio overrides --- .../config_setting/config_setting/style/style.css | 10 ++++++++-- .../config_setting/config_setting/widgets/inputs.py | 8 +++++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/pype/tools/config_setting/config_setting/style/style.css b/pype/tools/config_setting/config_setting/style/style.css index 61581b9124..73c1854bee 100644 --- a/pype/tools/config_setting/config_setting/style/style.css +++ b/pype/tools/config_setting/config_setting/style/style.css @@ -48,6 +48,8 @@ QLabel { } QLabel:hover {color: #999999;} +QLabel[state="studio"] {color: #bfccd6;} +QLabel[state="studio"]:hover {color: #ffffff;} QLabel[state="modified"] {color: #137cbd;} QLabel[state="modified"]:hover {color: #1798e8;} QLabel[state="overriden-modified"] {color: #137cbd;} @@ -58,6 +60,7 @@ QLabel[state="invalid"] {color: #ad2e2e; font-weight: bold;} QLabel[state="invalid"]:hover {color: #ad2e2e; font-weight: bold;} +QWidget[input-state="studio"] {border-color: #bfccd6;} QWidget[input-state="modified"] {border-color: #137cbd;} QWidget[input-state="overriden-modified"] {border-color: #137cbd;} QWidget[input-state="overriden"] {border-color: #ff8c1a;} @@ -99,6 +102,7 @@ QPushButton[btn-type="expand-toggle"] { font-weight: bold; } +#DictKey[state="studio"] {border-color: #bfccd6;} #DictKey[state="modified"] {border-color: #137cbd;} #DictKey[state="overriden"] {border-color: #00f;} #DictKey[state="overriden-modified"] {border-color: #0f0;} @@ -118,7 +122,7 @@ QPushButton[btn-type="expand-toggle"] { #SideLineWidget { background-color: #31424e; border-style: solid; - border-color: #455c6e; + border-color: #808080; border-left-width: 3px; border-bottom-width: 0px; border-right-width: 0px; @@ -126,9 +130,11 @@ QPushButton[btn-type="expand-toggle"] { } #SideLineWidget:hover { - border-color: #62839d; + border-color: #999999; } +#SideLineWidget[state="child-studio"] {border-color: #455c6e;} +#SideLineWidget[state="child-studio"]:hover {border-color: #62839d;} #SideLineWidget[state="child-modified"] {border-color: #106aa2;} #SideLineWidget[state="child-modified"]:hover {border-color: #137cbd;} diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index a9cbc502e9..b0aaa5ed3d 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -98,15 +98,17 @@ class ConfigObject(AbstractConfigObject): cls, has_studio_override, is_invalid, is_overriden, is_modified ): items = [] - if has_studio_override: - items.append("studio") - elif is_invalid: + if is_invalid: items.append("invalid") else: if is_overriden: items.append("overriden") if is_modified: items.append("modified") + + if not items and has_studio_override: + items.append("studio") + return "-".join(items) or cls.default_state def _discard_changes(self): From 3af3e46f1018307697ac8f49b7c49b33e7f94109 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 9 Sep 2020 15:35:38 +0200 Subject: [PATCH 495/813] config split into lib and config file --- pype/configurations/config.py | 222 +++------------------------------- pype/configurations/lib.py | 208 +++++++++++++++++++++++++++++++ 2 files changed, 224 insertions(+), 206 deletions(-) create mode 100644 pype/configurations/lib.py diff --git a/pype/configurations/config.py b/pype/configurations/config.py index de310645ac..e19a27f33c 100644 --- a/pype/configurations/config.py +++ b/pype/configurations/config.py @@ -1,214 +1,24 @@ -import os -import json -import logging -import copy - -log = logging.getLogger(__name__) - -# Metadata keys for work with studio and project overrides -OVERRIDEN_KEY = "__overriden_keys__" -# NOTE key popping not implemented yet -POP_KEY = "__pop_key__" - -# Paths to studio and project overrides -STUDIO_OVERRIDES_PATH = os.environ["PYPE_CONFIG"] - -SYSTEM_CONFIGURATIONS_DIR = "system_configurations" -SYSTEM_CONFIGURATIONS_PATH = os.path.join( - STUDIO_OVERRIDES_PATH, SYSTEM_CONFIGURATIONS_DIR -) -PROJECT_CONFIGURATIONS_DIR = "project_configurations" -PROJECT_CONFIGURATIONS_PATH = os.path.join( - STUDIO_OVERRIDES_PATH, PROJECT_CONFIGURATIONS_DIR +from .lib import ( + apply_overrides, + default_configuration, + studio_system_configurations, + studio_project_configurations, + project_configurations_overrides ) -# Variable where cache of default configurations are stored -_DEFAULT_CONFIGURATIONS = None -# TODO remove this as is maybe deprecated -first_run = False +def system_configurations(): + default_values = default_configuration()["system_configurations"] + studio_values = studio_system_configurations() + return apply_overrides(default_values, studio_values) -def default_configuration(): - global _DEFAULT_CONFIGURATIONS - if _DEFAULT_CONFIGURATIONS is None: - current_dir = os.path.dirname(__file__) - defaults_path = os.path.join(current_dir, "defaults") - _DEFAULT_CONFIGURATIONS = load_jsons_from_dir(defaults_path) - return _DEFAULT_CONFIGURATIONS +def project_configurations(project_name): + default_values = default_configuration() + studio_values = studio_project_configurations() + studio_overrides = apply_overrides(default_values, studio_values) -def load_json(fpath): - # Load json data - with open(fpath, "r") as opened_file: - lines = opened_file.read().splitlines() + project_overrides = project_configurations_overrides(project_name) - # prepare json string - standard_json = "" - for line in lines: - # Remove all whitespace on both sides - line = line.strip() - - # Skip blank lines - if len(line) == 0: - continue - - standard_json += line - - # Check if has extra commas - extra_comma = False - if ",]" in standard_json or ",}" in standard_json: - extra_comma = True - standard_json = standard_json.replace(",]", "]") - standard_json = standard_json.replace(",}", "}") - - global first_run - if extra_comma and first_run: - log.error("Extra comma in json file: \"{}\"".format(fpath)) - - # return empty dict if file is empty - if standard_json == "": - if first_run: - log.error("Empty json file: \"{}\"".format(fpath)) - return {} - - # Try to parse string - try: - return json.loads(standard_json) - - except json.decoder.JSONDecodeError: - # Return empty dict if it is first time that decode error happened - if not first_run: - return {} - - # Repreduce the exact same exception but traceback contains better - # information about position of error in the loaded json - try: - with open(fpath, "r") as opened_file: - json.load(opened_file) - - except json.decoder.JSONDecodeError: - log.warning( - "File has invalid json format \"{}\"".format(fpath), - exc_info=True - ) - - return {} - - -def subkey_merge(_dict, value, keys): - key = keys.pop(0) - if not keys: - _dict[key] = value - return _dict - - if key not in _dict: - _dict[key] = {} - _dict[key] = subkey_merge(_dict[key], value, keys) - - return _dict - - -def load_jsons_from_dir(path, *args, **kwargs): - output = {} - - path = os.path.normpath(path) - if not os.path.exists(path): - # TODO warning - return output - - sub_keys = list(kwargs.pop("subkeys", args)) - for sub_key in tuple(sub_keys): - _path = os.path.join(path, sub_key) - if not os.path.exists(_path): - break - - path = _path - sub_keys.pop(0) - - base_len = len(path) + 1 - for base, _directories, filenames in os.walk(path): - base_items_str = base[base_len:] - if not base_items_str: - base_items = [] - else: - base_items = base_items_str.split(os.path.sep) - - for filename in filenames: - basename, ext = os.path.splitext(filename) - if ext == ".json": - full_path = os.path.join(base, filename) - value = load_json(full_path) - dict_keys = base_items + [basename] - output = subkey_merge(output, value, dict_keys) - - for sub_key in sub_keys: - output = output[sub_key] - return output - - -def system_configurations(*args, **kwargs): - return load_jsons_from_dir(SYSTEM_CONFIGURATIONS_PATH, *args, **kwargs) - - -def global_project_configurations(**kwargs): - return load_jsons_from_dir(PROJECT_CONFIGURATIONS_PATH, **kwargs) - - -def path_to_project_overrides(project_name): - project_configs_path = os.environ["PYPE_PROJECT_CONFIGS"] - dirpath = os.path.join(project_configs_path, project_name) - return os.path.join(dirpath, PROJECT_CONFIGURATIONS_DIR + ".json") - - -def project_configurations_overrides(project_name, **kwargs): - if not project_name: - return {} - - path_to_json = path_to_project_overrides(project_name) - if not os.path.exists(path_to_json): - return {} - return load_json(path_to_json) - - -def merge_overrides(global_dict, override_dict): - if OVERRIDEN_KEY in override_dict: - overriden_keys = set(override_dict.pop(OVERRIDEN_KEY)) - else: - overriden_keys = set() - - for key, value in override_dict.items(): - if value == POP_KEY: - global_dict.pop(key) - - elif ( - key in overriden_keys - or key not in global_dict - ): - global_dict[key] = value - - elif isinstance(value, dict) and isinstance(global_dict[key], dict): - global_dict[key] = merge_overrides(global_dict[key], value) - - else: - global_dict[key] = value - return global_dict - - -def apply_overrides(global_presets, project_overrides): - global_presets = copy.deepcopy(global_presets) - if not project_overrides: - return global_presets - return merge_overrides(global_presets, project_overrides) - - -def project_presets(project_name=None, **kwargs): - global_presets = global_project_configurations(**kwargs) - - if not project_name: - project_name = os.environ.get("AVALON_PROJECT") - project_overrides = project_configurations_overrides( - project_name, **kwargs - ) - - return apply_overrides(global_presets, project_overrides) + return apply_overrides(studio_overrides, project_overrides) diff --git a/pype/configurations/lib.py b/pype/configurations/lib.py new file mode 100644 index 0000000000..4cd7203626 --- /dev/null +++ b/pype/configurations/lib.py @@ -0,0 +1,208 @@ +import os +import json +import logging +import copy + +log = logging.getLogger(__name__) + +# Metadata keys for work with studio and project overrides +OVERRIDEN_KEY = "__overriden_keys__" +# NOTE key popping not implemented yet +POP_KEY = "__pop_key__" + +# Folder where studio overrides are stored +STUDIO_OVERRIDES_PATH = os.environ["PYPE_CONFIG"] + +# File where studio's system overrides are stored +SYSTEM_CONFIGURATIONS_PATH = os.path.join( + STUDIO_OVERRIDES_PATH, "system_configurations.json" +) + +# File where studio's default project overrides are stored +PROJECT_CONFIGURATIONS_FILENAME = "project_configurations.json" +PROJECT_CONFIGURATIONS_PATH = os.path.join( + STUDIO_OVERRIDES_PATH, PROJECT_CONFIGURATIONS_FILENAME +) + +# Folder where studio's per project overrides are stored +STUDIO_PROJECT_OVERRIDES_PATH = os.path.join( + STUDIO_OVERRIDES_PATH, "project_overrides" +) + +# Variable where cache of default configurations are stored +_DEFAULT_CONFIGURATIONS = None + + +def default_configuration(): + global _DEFAULT_CONFIGURATIONS + if _DEFAULT_CONFIGURATIONS is None: + current_dir = os.path.dirname(__file__) + defaults_path = os.path.join(current_dir, "defaults") + _DEFAULT_CONFIGURATIONS = load_jsons_from_dir(defaults_path) + return _DEFAULT_CONFIGURATIONS + + +def load_json(fpath): + # Load json data + with open(fpath, "r") as opened_file: + lines = opened_file.read().splitlines() + + # prepare json string + standard_json = "" + for line in lines: + # Remove all whitespace on both sides + line = line.strip() + + # Skip blank lines + if len(line) == 0: + continue + + standard_json += line + + # Check if has extra commas + extra_comma = False + if ",]" in standard_json or ",}" in standard_json: + extra_comma = True + standard_json = standard_json.replace(",]", "]") + standard_json = standard_json.replace(",}", "}") + + if extra_comma: + log.error("Extra comma in json file: \"{}\"".format(fpath)) + + # return empty dict if file is empty + if standard_json == "": + return {} + + # Try to parse string + try: + return json.loads(standard_json) + + except json.decoder.JSONDecodeError: + # Return empty dict if it is first time that decode error happened + return {} + + # Repreduce the exact same exception but traceback contains better + # information about position of error in the loaded json + try: + with open(fpath, "r") as opened_file: + json.load(opened_file) + + except json.decoder.JSONDecodeError: + log.warning( + "File has invalid json format \"{}\"".format(fpath), + exc_info=True + ) + + return {} + + +def subkey_merge(_dict, value, keys): + key = keys.pop(0) + if not keys: + _dict[key] = value + return _dict + + if key not in _dict: + _dict[key] = {} + _dict[key] = subkey_merge(_dict[key], value, keys) + + return _dict + + +def load_jsons_from_dir(path, *args, **kwargs): + output = {} + + path = os.path.normpath(path) + if not os.path.exists(path): + # TODO warning + return output + + sub_keys = list(kwargs.pop("subkeys", args)) + for sub_key in tuple(sub_keys): + _path = os.path.join(path, sub_key) + if not os.path.exists(_path): + break + + path = _path + sub_keys.pop(0) + + base_len = len(path) + 1 + for base, _directories, filenames in os.walk(path): + base_items_str = base[base_len:] + if not base_items_str: + base_items = [] + else: + base_items = base_items_str.split(os.path.sep) + + for filename in filenames: + basename, ext = os.path.splitext(filename) + if ext == ".json": + full_path = os.path.join(base, filename) + value = load_json(full_path) + dict_keys = base_items + [basename] + output = subkey_merge(output, value, dict_keys) + + for sub_key in sub_keys: + output = output[sub_key] + return output + + +def studio_system_configurations(): + if os.path.exists(SYSTEM_CONFIGURATIONS_PATH): + return load_json(SYSTEM_CONFIGURATIONS_PATH) + return {} + + +def studio_project_configurations(): + if os.path.exists(PROJECT_CONFIGURATIONS_PATH): + return load_json(PROJECT_CONFIGURATIONS_PATH) + return {} + + +def path_to_project_overrides(project_name): + return os.path.join( + STUDIO_PROJECT_OVERRIDES_PATH, + project_name, + PROJECT_CONFIGURATIONS_FILENAME + ) + + +def project_configurations_overrides(project_name): + if not project_name: + return {} + + path_to_json = path_to_project_overrides(project_name) + if not os.path.exists(path_to_json): + return {} + return load_json(path_to_json) + + +def merge_overrides(global_dict, override_dict): + if OVERRIDEN_KEY in override_dict: + overriden_keys = set(override_dict.pop(OVERRIDEN_KEY)) + else: + overriden_keys = set() + + for key, value in override_dict.items(): + if value == POP_KEY: + global_dict.pop(key) + + elif ( + key in overriden_keys + or key not in global_dict + ): + global_dict[key] = value + + elif isinstance(value, dict) and isinstance(global_dict[key], dict): + global_dict[key] = merge_overrides(global_dict[key], value) + + else: + global_dict[key] = value + return global_dict + + +def apply_overrides(global_presets, project_overrides): + global_presets = copy.deepcopy(global_presets) + if not project_overrides: + return global_presets + return merge_overrides(global_presets, project_overrides) From b6b439c369f164a9f4ed48bc232c283c0cf32564 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 9 Sep 2020 15:36:36 +0200 Subject: [PATCH 496/813] config_value method renamed to studio_value --- .../config_setting/widgets/anatomy_inputs.py | 16 +++++++------- .../config_setting/widgets/base.py | 6 ++--- .../config_setting/widgets/inputs.py | 22 +++++++++---------- .../config_setting/widgets/widgets.py | 2 +- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index 43146f7442..18a58b1dc6 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -165,15 +165,15 @@ class AnatomyWidget(QtWidgets.QWidget, ConfigObject): self.templates_widget.discard_changes() def overrides(self): - return self.config_value(), True + return self.studio_value(), True def item_value(self): output = {} - output.update(self.root_widget.config_value()) - output.update(self.templates_widget.config_value()) + output.update(self.root_widget.studio_value()) + output.update(self.templates_widget.studio_value()) return output - def config_value(self): + def studio_value(self): return {self.key: self.item_value()} @@ -479,7 +479,7 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): else: return self.singleroot_widget.item_value() - def config_value(self): + def studio_value(self): return {self.key: self.item_value()} @@ -587,13 +587,13 @@ class TemplatesWidget(QtWidgets.QWidget, ConfigObject): def overrides(self): if not self.child_overriden: return NOT_SET, False - return self.config_value(), True + return self.studio_value(), True def item_value(self): return self.value_input.item_value() - def config_value(self): - return self.value_input.config_value() + def studio_value(self): + return self.value_input.studio_value() TypeToKlass.types["anatomy"] = AnatomyWidget diff --git a/pype/tools/config_setting/config_setting/widgets/base.py b/pype/tools/config_setting/config_setting/widgets/base.py index 55ca6096de..981713d5ae 100644 --- a/pype/tools/config_setting/config_setting/widgets/base.py +++ b/pype/tools/config_setting/config_setting/widgets/base.py @@ -125,7 +125,7 @@ class SystemWidget(QtWidgets.QWidget): all_values = {} for item in self.input_fields: - all_values.update(item.config_value()) + all_values.update(item.studio_value()) for key in reversed(self.keys): _all_values = {key: all_values} @@ -499,7 +499,7 @@ class ProjectWidget(QtWidgets.QWidget): def _save_defaults(self): output = {} for item in self.input_fields: - output.update(item.config_value()) + output.update(item.studio_value()) for key in reversed(self.keys): _output = {key: output} @@ -507,7 +507,7 @@ class ProjectWidget(QtWidgets.QWidget): all_values = {} for item in self.input_fields: - all_values.update(item.config_value()) + all_values.update(item.studio_value()) for key in reversed(self.keys): _all_values = {key: all_values} diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index b0aaa5ed3d..d1b71b2848 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -89,7 +89,7 @@ class ConfigObject(AbstractConfigObject): """Setter for global parent item to apply changes for all inputs.""" self._parent.ignore_value_changes = value - def config_value(self): + def studio_value(self): """Output for saving changes or overrides.""" return {self.key: self.item_value()} @@ -217,7 +217,7 @@ class InputObject(ConfigObject): def overrides(self): if not self.is_overriden: return NOT_SET, False - return self.config_value(), self.is_group + return self.studio_value(), self.is_group def hierarchical_style_update(self): self.update_style() @@ -899,7 +899,7 @@ class ListItem(QtWidgets.QWidget, ConfigObject): def on_remove_clicked(self): self._parent.remove_row(self) - def config_value(self): + def studio_value(self): if self.value_input.isEnabled(): return self.value_input.item_value() return NOT_SET @@ -1106,7 +1106,7 @@ class ListWidget(QtWidgets.QWidget, InputObject): def item_value(self): output = [] for item in self.input_fields: - value = item.config_value() + value = item.studio_value() if value is not NOT_SET: output.append(value) return output @@ -1245,7 +1245,7 @@ class ModifiableDictItem(QtWidgets.QWidget, ConfigObject): def row(self): return self._parent.input_fields.index(self) - def config_value(self): + def studio_value(self): key = self.key_input.text() value = self.value_input.item_value() return {key: value} @@ -1405,7 +1405,7 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): def item_value(self): output = {} for item in self.input_fields: - output.update(item.config_value()) + output.update(item.studio_value()) return output def add_row(self, row=None, key=None, value=None, is_empty=False): @@ -1749,7 +1749,7 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): for input_field in self.input_fields: # TODO maybe merge instead of update should be used # NOTE merge is custom function which merges 2 dicts - output.update(input_field.config_value()) + output.update(input_field.studio_value()) return output def overrides(self): @@ -1859,7 +1859,7 @@ class DictInvisible(QtWidgets.QWidget, ConfigObject): for input_field in self.input_fields: # TODO maybe merge instead of update should be used # NOTE merge is custom function which merges 2 dicts - output.update(input_field.config_value()) + output.update(input_field.studio_value()) return output def _on_value_change(self, item=None): @@ -2236,7 +2236,7 @@ class PathWidget(QtWidgets.QWidget, ConfigObject): output = {} for input_field in self.input_fields: - output.update(input_field.config_value()) + output.update(input_field.studio_value()) return output def overrides(self): @@ -2402,10 +2402,10 @@ class DictFormWidget(QtWidgets.QWidget, ConfigObject): for input_field in self.input_fields: # TODO maybe merge instead of update should be used # NOTE merge is custom function which merges 2 dicts - output.update(input_field.config_value()) + output.update(input_field.studio_value()) return output - def config_value(self): + def studio_value(self): return self.item_value() def overrides(self): diff --git a/pype/tools/config_setting/config_setting/widgets/widgets.py b/pype/tools/config_setting/config_setting/widgets/widgets.py index 5547f66597..1e57bf73df 100644 --- a/pype/tools/config_setting/config_setting/widgets/widgets.py +++ b/pype/tools/config_setting/config_setting/widgets/widgets.py @@ -344,7 +344,7 @@ class AbstractConfigObject: "Method `item_value` not implemented!" ) - def config_value(self): + def studio_value(self): """Output for saving changes or overrides.""" return {self.key: self.item_value()} From 778f645057f22e076c9b4e6cad4320b724069cb6 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 9 Sep 2020 16:07:05 +0200 Subject: [PATCH 497/813] renamed method studio_value back to config_value --- .../config_setting/widgets/anatomy_inputs.py | 16 +++++++------- .../config_setting/widgets/base.py | 6 ++--- .../config_setting/widgets/inputs.py | 22 +++++++++---------- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index 18a58b1dc6..43146f7442 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -165,15 +165,15 @@ class AnatomyWidget(QtWidgets.QWidget, ConfigObject): self.templates_widget.discard_changes() def overrides(self): - return self.studio_value(), True + return self.config_value(), True def item_value(self): output = {} - output.update(self.root_widget.studio_value()) - output.update(self.templates_widget.studio_value()) + output.update(self.root_widget.config_value()) + output.update(self.templates_widget.config_value()) return output - def studio_value(self): + def config_value(self): return {self.key: self.item_value()} @@ -479,7 +479,7 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): else: return self.singleroot_widget.item_value() - def studio_value(self): + def config_value(self): return {self.key: self.item_value()} @@ -587,13 +587,13 @@ class TemplatesWidget(QtWidgets.QWidget, ConfigObject): def overrides(self): if not self.child_overriden: return NOT_SET, False - return self.studio_value(), True + return self.config_value(), True def item_value(self): return self.value_input.item_value() - def studio_value(self): - return self.value_input.studio_value() + def config_value(self): + return self.value_input.config_value() TypeToKlass.types["anatomy"] = AnatomyWidget diff --git a/pype/tools/config_setting/config_setting/widgets/base.py b/pype/tools/config_setting/config_setting/widgets/base.py index 981713d5ae..55ca6096de 100644 --- a/pype/tools/config_setting/config_setting/widgets/base.py +++ b/pype/tools/config_setting/config_setting/widgets/base.py @@ -125,7 +125,7 @@ class SystemWidget(QtWidgets.QWidget): all_values = {} for item in self.input_fields: - all_values.update(item.studio_value()) + all_values.update(item.config_value()) for key in reversed(self.keys): _all_values = {key: all_values} @@ -499,7 +499,7 @@ class ProjectWidget(QtWidgets.QWidget): def _save_defaults(self): output = {} for item in self.input_fields: - output.update(item.studio_value()) + output.update(item.config_value()) for key in reversed(self.keys): _output = {key: output} @@ -507,7 +507,7 @@ class ProjectWidget(QtWidgets.QWidget): all_values = {} for item in self.input_fields: - all_values.update(item.studio_value()) + all_values.update(item.config_value()) for key in reversed(self.keys): _all_values = {key: all_values} diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index d1b71b2848..b0aaa5ed3d 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -89,7 +89,7 @@ class ConfigObject(AbstractConfigObject): """Setter for global parent item to apply changes for all inputs.""" self._parent.ignore_value_changes = value - def studio_value(self): + def config_value(self): """Output for saving changes or overrides.""" return {self.key: self.item_value()} @@ -217,7 +217,7 @@ class InputObject(ConfigObject): def overrides(self): if not self.is_overriden: return NOT_SET, False - return self.studio_value(), self.is_group + return self.config_value(), self.is_group def hierarchical_style_update(self): self.update_style() @@ -899,7 +899,7 @@ class ListItem(QtWidgets.QWidget, ConfigObject): def on_remove_clicked(self): self._parent.remove_row(self) - def studio_value(self): + def config_value(self): if self.value_input.isEnabled(): return self.value_input.item_value() return NOT_SET @@ -1106,7 +1106,7 @@ class ListWidget(QtWidgets.QWidget, InputObject): def item_value(self): output = [] for item in self.input_fields: - value = item.studio_value() + value = item.config_value() if value is not NOT_SET: output.append(value) return output @@ -1245,7 +1245,7 @@ class ModifiableDictItem(QtWidgets.QWidget, ConfigObject): def row(self): return self._parent.input_fields.index(self) - def studio_value(self): + def config_value(self): key = self.key_input.text() value = self.value_input.item_value() return {key: value} @@ -1405,7 +1405,7 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): def item_value(self): output = {} for item in self.input_fields: - output.update(item.studio_value()) + output.update(item.config_value()) return output def add_row(self, row=None, key=None, value=None, is_empty=False): @@ -1749,7 +1749,7 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): for input_field in self.input_fields: # TODO maybe merge instead of update should be used # NOTE merge is custom function which merges 2 dicts - output.update(input_field.studio_value()) + output.update(input_field.config_value()) return output def overrides(self): @@ -1859,7 +1859,7 @@ class DictInvisible(QtWidgets.QWidget, ConfigObject): for input_field in self.input_fields: # TODO maybe merge instead of update should be used # NOTE merge is custom function which merges 2 dicts - output.update(input_field.studio_value()) + output.update(input_field.config_value()) return output def _on_value_change(self, item=None): @@ -2236,7 +2236,7 @@ class PathWidget(QtWidgets.QWidget, ConfigObject): output = {} for input_field in self.input_fields: - output.update(input_field.studio_value()) + output.update(input_field.config_value()) return output def overrides(self): @@ -2402,10 +2402,10 @@ class DictFormWidget(QtWidgets.QWidget, ConfigObject): for input_field in self.input_fields: # TODO maybe merge instead of update should be used # NOTE merge is custom function which merges 2 dicts - output.update(input_field.studio_value()) + output.update(input_field.config_value()) return output - def studio_value(self): + def config_value(self): return self.item_value() def overrides(self): From c483705a91e6cd9c4cf82634afdfc9428053022b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 9 Sep 2020 16:16:22 +0200 Subject: [PATCH 498/813] any_parent_is_group is abstract attribute --- .../config_setting/widgets/inputs.py | 18 +++++++++++------- .../config_setting/widgets/widgets.py | 6 ++++++ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index b0aaa5ed3d..9e712fd087 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -25,6 +25,7 @@ class ConfigObject(AbstractConfigObject): _is_group = False _is_nullable = False + _any_parent_is_group = None _log = None @property @@ -36,6 +37,10 @@ class ConfigObject(AbstractConfigObject): @property def has_studio_override(self): return self._has_studio_override + def any_parent_is_group(self): + if self._any_parent_is_group is None: + return super(ConfigObject, self).any_parent_is_group + return self._any_parent_is_group @property def is_modified(self): @@ -752,7 +757,7 @@ class RawJsonWidget(QtWidgets.QWidget, InputObject): if not any_parent_is_group: any_parent_is_group = parent.any_parent_is_group - self.any_parent_is_group = any_parent_is_group + self._any_parent_is_group = any_parent_is_group self._is_group = input_data.get("is_group", False) self._is_nullable = input_data.get("is_nullable", False) @@ -843,7 +848,6 @@ class ListItem(QtWidgets.QWidget, ConfigObject): def __init__(self, object_type, input_modifiers, config_parent, parent): self._parent = config_parent - super(ListItem, self).__init__(parent) layout = QtWidgets.QHBoxLayout(self) @@ -1280,7 +1284,7 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): if not any_parent_is_group: any_parent_is_group = parent.any_parent_is_group - self.any_parent_is_group = any_parent_is_group + self._any_parent_is_group = any_parent_is_group self._is_group = input_data.get("is_group", False) self._is_nullable = input_data.get("is_nullable", False) @@ -1500,7 +1504,7 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): any_parent_is_group = parent.is_group if not any_parent_is_group: any_parent_is_group = parent.any_parent_is_group - self.any_parent_is_group = any_parent_is_group + self._any_parent_is_group = any_parent_is_group self._is_group = input_data.get("is_group", False) self._is_nullable = input_data.get("is_nullable", False) @@ -1789,7 +1793,7 @@ class DictInvisible(QtWidgets.QWidget, ConfigObject): if not any_parent_is_group: any_parent_is_group = parent.any_parent_is_group - self.any_parent_is_group = any_parent_is_group + self._any_parent_is_group = any_parent_is_group self._is_group = input_data.get("is_group", False) self.setAttribute(QtCore.Qt.WA_TranslucentBackground) @@ -1997,7 +2001,7 @@ class PathWidget(QtWidgets.QWidget, ConfigObject): any_parent_is_group = parent.is_group if not any_parent_is_group: any_parent_is_group = parent.any_parent_is_group - self.any_parent_is_group = any_parent_is_group + self._any_parent_is_group = any_parent_is_group # This is partial input and dictionary input if not any_parent_is_group and not as_widget: @@ -2274,7 +2278,7 @@ class DictFormWidget(QtWidgets.QWidget, ConfigObject): if not any_parent_is_group: any_parent_is_group = parent.any_parent_is_group - self.any_parent_is_group = any_parent_is_group + self._any_parent_is_group = any_parent_is_group self._is_group = False diff --git a/pype/tools/config_setting/config_setting/widgets/widgets.py b/pype/tools/config_setting/config_setting/widgets/widgets.py index 1e57bf73df..d803624850 100644 --- a/pype/tools/config_setting/config_setting/widgets/widgets.py +++ b/pype/tools/config_setting/config_setting/widgets/widgets.py @@ -248,6 +248,12 @@ class AbstractConfigObject: "{} does not have implemented `is_overriden`".format(self) ) + @property + def any_parent_is_group(self): + raise NotImplementedError( + "{} does not have implemented `any_parent_is_group`".format(self) + ) + @property def was_overriden(self): """Initial state after applying overrides.""" From daebec1faec9fca76812ab8f9285849a25b66f19 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 9 Sep 2020 16:16:33 +0200 Subject: [PATCH 499/813] fixe override key import --- pype/tools/config_setting/config_setting/widgets/lib.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/lib.py b/pype/tools/config_setting/config_setting/widgets/lib.py index 69c3259b3b..6b3aa53c8f 100644 --- a/pype/tools/config_setting/config_setting/widgets/lib.py +++ b/pype/tools/config_setting/config_setting/widgets/lib.py @@ -1,9 +1,8 @@ import os import json import copy -from pype.api import config +from pype.configurations.lib import OVERRIDEN_KEY from queue import Queue -OVERRIDEN_KEY = config.OVERRIDEN_KEY # Singleton database of available inputs From ce90e8a1835cbef57ead0739bf980367b7b9d884 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 9 Sep 2020 16:16:44 +0200 Subject: [PATCH 500/813] has_studio_override fix --- pype/tools/config_setting/config_setting/widgets/inputs.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 9e712fd087..e7f3f71620 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -36,7 +36,8 @@ class ConfigObject(AbstractConfigObject): @property def has_studio_override(self): - return self._has_studio_override + return self._has_studio_override or self._parent.has_studio_override + def any_parent_is_group(self): if self._any_parent_is_group is None: return super(ConfigObject, self).any_parent_is_group @@ -290,7 +291,7 @@ class InputObject(ConfigObject): @property def child_has_studio_override(self): - return self.has_studio_override + return self._has_studio_override @property def child_modified(self): From 39cde9bd82e6d3852f7d92550a10e2884ba05e95 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 9 Sep 2020 16:34:46 +0200 Subject: [PATCH 501/813] update saving of studio overrides --- .../config_setting/widgets/base.py | 146 +++++------------- 1 file changed, 40 insertions(+), 106 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/base.py b/pype/tools/config_setting/config_setting/widgets/base.py index 55ca6096de..9abe05946e 100644 --- a/pype/tools/config_setting/config_setting/widgets/base.py +++ b/pype/tools/config_setting/config_setting/widgets/base.py @@ -1,7 +1,15 @@ import os import json from Qt import QtWidgets, QtCore, QtGui -from pype.api import config +from pype.configurations.lib import ( + SYSTEM_CONFIGURATIONS_PATH, + PROJECT_CONFIGURATIONS_PATH, + default_configuration, + studio_system_configurations, + project_configurations_overrides, + path_to_project_overrides, + studio_project_configurations +) from .widgets import UnsavedChangesDialog from . import lib from avalon import io @@ -123,60 +131,31 @@ class SystemWidget(QtWidgets.QWidget): first_invalid_item.setFocus(True) return - all_values = {} - for item in self.input_fields: - all_values.update(item.config_value()) + _data = {} + for input_field in self.input_fields: + value, is_group = input_field.studio_overrides() + if value is not lib.NOT_SET: + _data.update(value) - for key in reversed(self.keys): - _all_values = {key: all_values} - all_values = _all_values + values = _data["system"] - # Skip first key - all_values = all_values["system"] + dirpath = os.path.dirname(SYSTEM_CONFIGURATIONS_PATH) + if not os.path.exists(dirpath): + os.makedirs(dirpath) - # Load studio data with metadata - current_configurations = config.system_configurations() - - keys_to_file = lib.file_keys_from_schema(self.schema) - for key_sequence in keys_to_file: - # Skip first key - key_sequence = key_sequence[1:] - subpath = "/".join(key_sequence) + ".json" - origin_values = current_configurations - for key in key_sequence: - if not origin_values or key not in origin_values: - origin_values = {} - break - origin_values = origin_values[key] - - if not origin_values: - origin_values = {} - - new_values = all_values - for key in key_sequence: - new_values = new_values[key] - origin_values.update(new_values) - raise NotImplementedError("Output from global values has changed") - output_path = os.path.join( - config.STUDIO_PRESETS_PATH, subpath - ) - dirpath = os.path.dirname(output_path) - if not os.path.exists(dirpath): - os.makedirs(dirpath) - - print("Saving data to: ", output_path) - with open(output_path, "w") as file_stream: - json.dump(origin_values, file_stream, indent=4) + print("Saving data to:", SYSTEM_CONFIGURATIONS_PATH) + with open(SYSTEM_CONFIGURATIONS_PATH, "w") as file_stream: + json.dump(values, file_stream, indent=4) self._update_values() def _update_values(self): - default_values = config.default_configuration() + default_values = default_configuration() default_values = {"system": default_values["system_configurations"]} for input_field in self.input_fields: input_field.update_default_values(default_values) - system_values = {"system": config.system_configurations()} + system_values = {"system": studio_system_configurations()} for input_field in self.input_fields: input_field.update_studio_values(system_values) @@ -429,7 +408,7 @@ class ProjectWidget(QtWidgets.QWidget): _overrides = lib.NOT_SET self.is_overidable = False else: - _overrides = config.project_configurations_overrides(project_name) + _overrides = project_configurations_overrides(project_name) self.is_overidable = True overrides = {"project": lib.convert_overrides_to_gui_data(_overrides)} @@ -475,94 +454,49 @@ class ProjectWidget(QtWidgets.QWidget): value, is_group = item.overrides() if value is not lib.NOT_SET: _data.update(value) - if is_group: - raise Exception( - "Top item can't be overriden in Project widget." - ) data = _data.get("project") or {} output_data = lib.convert_gui_data_to_overrides(data) - overrides_json_path = config.path_to_project_overrides( + overrides_json_path = path_to_project_overrides( self.project_name ) dirpath = os.path.dirname(overrides_json_path) if not os.path.exists(dirpath): os.makedirs(dirpath) - print("Saving data to: ", overrides_json_path) + print("Saving data to:", overrides_json_path) with open(overrides_json_path, "w") as file_stream: json.dump(output_data, file_stream, indent=4) self._on_project_change() def _save_defaults(self): - output = {} - for item in self.input_fields: - output.update(item.config_value()) + _data = {} + for input_field in self.input_fields: + value, is_group = input_field.studio_overrides() + if value is not lib.NOT_SET: + _data.update(value) - for key in reversed(self.keys): - _output = {key: output} - output = _output + output = _data["project"] - all_values = {} - for item in self.input_fields: - all_values.update(item.config_value()) + dirpath = os.path.dirname(PROJECT_CONFIGURATIONS_PATH) + if not os.path.exists(dirpath): + os.makedirs(dirpath) - for key in reversed(self.keys): - _all_values = {key: all_values} - all_values = _all_values - - # Skip first key - all_values = all_values["project"] - - # Load studio data with metadata - current_configurations = config.global_project_configurations() - - keys_to_file = lib.file_keys_from_schema(self.schema) - for key_sequence in keys_to_file: - # Skip first key - key_sequence = key_sequence[1:] - subpath = "/".join(key_sequence) + ".json" - origin_values = current_configurations - for key in key_sequence: - if not origin_values or key not in origin_values: - origin_values = {} - break - origin_values = origin_values[key] - - if not origin_values: - origin_values = {} - - new_values = all_values - for key in key_sequence: - new_values = new_values[key] - if isinstance(new_values, dict): - origin_values.update(new_values) - else: - origin_values = new_values - - raise NotImplementedError("Output from global values has changed") - output_path = os.path.join( - config.PROJECT_PRESETS_PATH, subpath - ) - dirpath = os.path.dirname(output_path) - if not os.path.exists(dirpath): - os.makedirs(dirpath) - - print("Saving data to: ", output_path) - with open(output_path, "w") as file_stream: - json.dump(origin_values, file_stream, indent=4) + print("Saving data to:", PROJECT_CONFIGURATIONS_PATH) + with open(PROJECT_CONFIGURATIONS_PATH, "w") as file_stream: + json.dump(output, file_stream, indent=4) self._update_values() def _update_values(self): - default_values = config.default_configuration() + default_values = default_configuration() default_values = {"project": default_values["project_configurations"]} for input_field in self.input_fields: input_field.update_default_values(default_values) - studio_values = {"project": config.global_project_configurations()} + studio_values = {"project": studio_project_configurations()} for input_field in self.input_fields: input_field.update_studio_values(studio_values) From 9cda39766cb7e8361b5f1c9db41bb973ab9acac3 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 9 Sep 2020 16:35:01 +0200 Subject: [PATCH 502/813] inputs has studio_overrides methods now --- .../config_setting/widgets/inputs.py | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index e7f3f71620..6e229fb9ea 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -220,6 +220,11 @@ class InputObject(ConfigObject): self._is_modified = False + def studio_overrides(self): + if not self.has_studio_override: + return NOT_SET, False + return self.config_value(), self.is_group + def overrides(self): if not self.is_overriden: return NOT_SET, False @@ -1757,6 +1762,22 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): output.update(input_field.config_value()) return output + def studio_overrides(self): + if not self.has_studio_override 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.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 + def overrides(self): if not self.is_overriden and not self.child_overriden: return NOT_SET, False @@ -1953,6 +1974,22 @@ class DictInvisible(QtWidgets.QWidget, ConfigObject): ) self._was_overriden = bool(self._is_overriden) + def studio_overrides(self): + if not self.has_studio_override 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.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 + def overrides(self): if not self.is_overriden and not self.child_overriden: return NOT_SET, False @@ -2244,6 +2281,15 @@ class PathWidget(QtWidgets.QWidget, ConfigObject): output.update(input_field.config_value()) return output + def studio_overrides(self): + if not self.has_studio_override and not self.child_has_studio_override: + return NOT_SET, False + + value = self.item_value() + if not self.multiplatform: + value = {self.key: value} + return value, self.is_group + def overrides(self): if not self.is_overriden and not self.child_overriden: return NOT_SET, False @@ -2413,6 +2459,22 @@ class DictFormWidget(QtWidgets.QWidget, ConfigObject): def config_value(self): return self.item_value() + def studio_overrides(self): + if not self.has_studio_override 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.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 values, self.is_group + def overrides(self): if not self.is_overriden and not self.child_overriden: return NOT_SET, False From 9521ea3f08ee69b532c1991ac82d644404483b7c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 9 Sep 2020 16:40:40 +0200 Subject: [PATCH 503/813] removed attribute methods from base widgets --- .../config_setting/widgets/base.py | 38 ++++--------------- 1 file changed, 8 insertions(+), 30 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/base.py b/pype/tools/config_setting/config_setting/widgets/base.py index 9abe05946e..172450684b 100644 --- a/pype/tools/config_setting/config_setting/widgets/base.py +++ b/pype/tools/config_setting/config_setting/widgets/base.py @@ -17,9 +17,10 @@ from avalon import io class SystemWidget(QtWidgets.QWidget): is_overidable = False - _is_overriden = False - _is_group = False - _any_parent_is_group = False + has_studio_override = False + is_overriden = False + is_group = False + any_parent_is_group = False def __init__(self, parent=None): super(SystemWidget, self).__init__(parent) @@ -67,18 +68,6 @@ class SystemWidget(QtWidgets.QWidget): def any_parent_overriden(self): return False - @property - def is_overriden(self): - return self._is_overriden - - @property - def is_group(self): - return self._is_group - - @property - def any_parent_is_group(self): - return self._any_parent_is_group - @property def ignore_value_changes(self): return self._ignore_value_changes @@ -297,9 +286,10 @@ class ProjectListWidget(QtWidgets.QWidget): class ProjectWidget(QtWidgets.QWidget): - _is_overriden = False - _is_group = False - _any_parent_is_group = False + has_studio_override = False + is_overriden = False + is_group = False + any_parent_is_group = False def __init__(self, parent=None): super(ProjectWidget, self).__init__(parent) @@ -362,18 +352,6 @@ class ProjectWidget(QtWidgets.QWidget): def any_parent_overriden(self): return False - @property - def is_overriden(self): - return self._is_overriden - - @property - def is_group(self): - return self._is_group - - @property - def any_parent_is_group(self): - return self._any_parent_is_group - @property def ignore_value_changes(self): return self._ignore_value_changes From 9693a6a37b886e1ae3646f52903d493532542b85 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 9 Sep 2020 16:40:58 +0200 Subject: [PATCH 504/813] any parent is group is attribute --- pype/tools/config_setting/config_setting/widgets/inputs.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 6e229fb9ea..3a3d517cc1 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -38,6 +38,7 @@ class ConfigObject(AbstractConfigObject): def has_studio_override(self): return self._has_studio_override or self._parent.has_studio_override + @property def any_parent_is_group(self): if self._any_parent_is_group is None: return super(ConfigObject, self).any_parent_is_group From ffa233994e58994192e2715b1acbedea4f3c45ca Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Wed, 9 Sep 2020 16:57:19 +0200 Subject: [PATCH 505/813] bump version --- pype/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/version.py b/pype/version.py index 9e1a271244..95a6d3a792 100644 --- a/pype/version.py +++ b/pype/version.py @@ -1 +1 @@ -__version__ = "2.11.8" +__version__ = "2.12.0" From d51a5701590c08fcb928f784480d73da29468489 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Wed, 9 Sep 2020 17:27:16 +0200 Subject: [PATCH 506/813] configure changelog generator --- .github_changelog_generator | 7 + HISTORY.md | 435 ++++++++++++++++++++++++++++++++++++ changelog.md | 120 ---------- 3 files changed, 442 insertions(+), 120 deletions(-) create mode 100644 .github_changelog_generator create mode 100644 HISTORY.md delete mode 100644 changelog.md diff --git a/.github_changelog_generator b/.github_changelog_generator new file mode 100644 index 0000000000..93ab39f4d0 --- /dev/null +++ b/.github_changelog_generator @@ -0,0 +1,7 @@ +pr-wo-labels=False +exclude-labels=duplicate,question,invalid,wontfix,weekly-digest +author=False +unreleased=False +since-tag=2.11.0 +release-branch=master +enhancement-label=New / Enhancements diff --git a/HISTORY.md b/HISTORY.md new file mode 100644 index 0000000000..4b5463c924 --- /dev/null +++ b/HISTORY.md @@ -0,0 +1,435 @@ +# Pype changelog # +Welcome to pype changelog + + +## 2.11.0 ## + +_**release date:** 27 July 2020_ + +**new:** +- _(blender)_ namespace support [\#341](https://github.com/pypeclub/pype/pull/341) +- _(blender)_ start end frames [\#330](https://github.com/pypeclub/pype/pull/330) +- _(blender)_ camera asset [\#322](https://github.com/pypeclub/pype/pull/322) +- _(pype)_ toggle instances per family in pyblish GUI [\#320](https://github.com/pypeclub/pype/pull/320) +- _(pype)_ current release version is now shown in the tray menu [#379](https://github.com/pypeclub/pype/pull/379) + + +**improved:** +- _(resolve)_ tagging for publish [\#239](https://github.com/pypeclub/pype/issues/239) +- _(pype)_ Support publishing a subset of shots with standalone editorial [\#336](https://github.com/pypeclub/pype/pull/336) +- _(harmony)_ Basic support for palettes [\#324](https://github.com/pypeclub/pype/pull/324) +- _(photoshop)_ Flag outdated containers on startup and publish. [\#309](https://github.com/pypeclub/pype/pull/309) +- _(harmony)_ Flag Outdated containers [\#302](https://github.com/pypeclub/pype/pull/302) +- _(photoshop)_ Publish review [\#298](https://github.com/pypeclub/pype/pull/298) +- _(pype)_ Optional Last workfile launch [\#365](https://github.com/pypeclub/pype/pull/365) + + +**fixed:** +- _(premiere)_ workflow fixes [\#346](https://github.com/pypeclub/pype/pull/346) +- _(pype)_ pype-setup does not work with space in path [\#327](https://github.com/pypeclub/pype/issues/327) +- _(ftrack)_ Ftrack delete action cause circular error [\#206](https://github.com/pypeclub/pype/issues/206) +- _(nuke)_ Priority was forced to 50 [\#345](https://github.com/pypeclub/pype/pull/345) +- _(nuke)_ Fix ValidateNukeWriteKnobs [\#340](https://github.com/pypeclub/pype/pull/340) +- _(maya)_ If camera attributes are connected, we can ignore them. [\#339](https://github.com/pypeclub/pype/pull/339) +- _(pype)_ stop appending of tools environment to existing env [\#337](https://github.com/pypeclub/pype/pull/337) +- _(ftrack)_ Ftrack timeout needs to look at AVALON\_TIMEOUT [\#325](https://github.com/pypeclub/pype/pull/325) +- _(harmony)_ Only zip files are supported. [\#310](https://github.com/pypeclub/pype/pull/310) +- _(pype)_ hotfix/Fix event server mongo uri [\#305](https://github.com/pypeclub/pype/pull/305) +- _(photoshop)_ Subset was not named or validated correctly. [\#304](https://github.com/pypeclub/pype/pull/304) + + + + +## 2.10.0 ## + +_**release date:** 17 June 2020_ + +**new:** +- _(harmony)_ **Toon Boom Harmony** has been greatly extended to support rigging, scene build, animation and rendering workflows. [#270](https://github.com/pypeclub/pype/issues/270) [#271](https://github.com/pypeclub/pype/issues/271) [#190](https://github.com/pypeclub/pype/issues/190) [#191](https://github.com/pypeclub/pype/issues/191) [#172](https://github.com/pypeclub/pype/issues/172) [#168](https://github.com/pypeclub/pype/issues/168) +- _(pype)_ Added support for rudimentary **edl publishing** into individual shots. [#265](https://github.com/pypeclub/pype/issues/265) +- _(celaction)_ Simple **Celaction** integration has been added with support for workfiles and rendering. [#255](https://github.com/pypeclub/pype/issues/255) +- _(maya)_ Support for multiple job types when submitting to the farm. We can now render Maya or Standalone render jobs for Vray and Arnold (limited support for arnold) [#204](https://github.com/pypeclub/pype/issues/204) +- _(photoshop)_ Added initial support for Photoshop [#232](https://github.com/pypeclub/pype/issues/232) + +**improved:** +- _(blender)_ Updated support for rigs and added support Layout family [#233](https://github.com/pypeclub/pype/issues/233) [#226](https://github.com/pypeclub/pype/issues/226) +- _(premiere)_ It is now possible to choose different storage root for workfiles of different task types. [#255](https://github.com/pypeclub/pype/issues/255) +- _(maya)_ Support for unmerged AOVs in Redshift multipart EXRs [#197](https://github.com/pypeclub/pype/issues/197) +- _(pype)_ Pype repository has been refactored in preparation for 3.0 release [#169](https://github.com/pypeclub/pype/issues/169) +- _(deadline)_ All file dependencies are now passed to deadline from maya to prevent premature start of rendering if caches or textures haven't been coppied over yet. [#195](https://github.com/pypeclub/pype/issues/195) +- _(nuke)_ Script validation can now be made optional. [#194](https://github.com/pypeclub/pype/issues/194) +- _(pype)_ Publishing can now be stopped at any time. [#194](https://github.com/pypeclub/pype/issues/194) + +**fix:** +- _(pype)_ Pyblish-lite has been integrated into pype repository, plus various publishing GUI fixes. [#274](https://github.com/pypeclub/pype/issues/274) [#275](https://github.com/pypeclub/pype/issues/275) [#268](https://github.com/pypeclub/pype/issues/268) [#227](https://github.com/pypeclub/pype/issues/227) [#238](https://github.com/pypeclub/pype/issues/238) +- _(maya)_ Alembic extractor was getting wrong frame range type in certain scenarios [#254](https://github.com/pypeclub/pype/issues/254) +- _(maya)_ Attaching a render to subset in maya was not passing validation in certain scenarios [#256](https://github.com/pypeclub/pype/issues/256) +- _(ftrack)_ Various small fixes to ftrack sync [#263](https://github.com/pypeclub/pype/issues/263) [#259](https://github.com/pypeclub/pype/issues/259) +- _(maya)_ Look extraction is now able to skp invalid connections in shaders [#207](https://github.com/pypeclub/pype/issues/207) + + + + +## 2.9.0 ## + +_**release date:** 25 May 2020_ + +**new:** +- _(pype)_ Support for **Multiroot projects**. You can now store project data on multiple physical or virtual storages and target individual publishes to these locations. For instance render can be stored on a faster storage than the rest of the project. [#145](https://github.com/pypeclub/pype/issues/145), [#38](https://github.com/pypeclub/pype/issues/38) +- _(harmony)_ Basic implementation of **Toon Boom Harmony** has been added. [#142](https://github.com/pypeclub/pype/issues/142) +- _(pype)_ OSX support is in public beta now. There are issues to be expected, but the main implementation should be functional. [#141](https://github.com/pypeclub/pype/issues/141) + + +**improved:** + +- _(pype)_ **Review extractor** has been completely rebuilt. It now supports granular filtering so you can create **multiple outputs** for different tasks, families or hosts. [#103](https://github.com/pypeclub/pype/issues/103), [#166](https://github.com/pypeclub/pype/issues/166), [#165](https://github.com/pypeclub/pype/issues/165) +- _(pype)_ **Burnin** generation had been extended to **support same multi-output filtering** as review extractor [#103](https://github.com/pypeclub/pype/issues/103) +- _(pype)_ Publishing file templates can now be specified in config for each individual family [#114](https://github.com/pypeclub/pype/issues/114) +- _(pype)_ Studio specific plugins can now be appended to pype standard publishing plugins. [#112](https://github.com/pypeclub/pype/issues/112) +- _(nukestudio)_ Reviewable clips no longer need to be previously cut, exported and re-imported to timeline. **Pype can now dynamically cut reviewable quicktimes** from continuous offline footage during publishing. [#23](https://github.com/pypeclub/pype/issues/23) +- _(deadline)_ Deadline can now correctly differentiate between staging and production pype. [#154](https://github.com/pypeclub/pype/issues/154) +- _(deadline)_ `PYPE_PYTHON_EXE` env variable can now be used to direct publishing to explicit python installation. [#120](https://github.com/pypeclub/pype/issues/120) +- _(nuke)_ Nuke now check for new version of loaded data on file open. [#140](https://github.com/pypeclub/pype/issues/140) +- _(nuke)_ frame range and limit checkboxes are now exposed on write node. [#119](https://github.com/pypeclub/pype/issues/119) + + + +**fix:** + +- _(nukestudio)_ Project Location was using backslashes which was breaking nukestudio native exporting in certains configurations [#82](https://github.com/pypeclub/pype/issues/82) +- _(nukestudio)_ Duplicity in hierarchy tags was prone to throwing publishing error [#130](https://github.com/pypeclub/pype/issues/130), [#144](https://github.com/pypeclub/pype/issues/144) +- _(ftrack)_ multiple stability improvements [#157](https://github.com/pypeclub/pype/issues/157), [#159](https://github.com/pypeclub/pype/issues/159), [#128](https://github.com/pypeclub/pype/issues/128), [#118](https://github.com/pypeclub/pype/issues/118), [#127](https://github.com/pypeclub/pype/issues/127) +- _(deadline)_ multipart EXRs were stopping review publishing on the farm. They are still not supported for automatic review generation, but the publish will go through correctly without the quicktime. [#155](https://github.com/pypeclub/pype/issues/155) +- _(deadline)_ If deadline is non-responsive it will no longer freeze host when publishing [#149](https://github.com/pypeclub/pype/issues/149) +- _(deadline)_ Sometimes deadline was trying to launch render before all the source data was coppied over. [#137](https://github.com/pypeclub/pype/issues/137) _(harmony)_ Basic implementation of **Toon Boom Harmony** has been added. [#142](https://github.com/pypeclub/pype/issues/142) +- _(nuke)_ Filepath knob wasn't updated properly. [#131](https://github.com/pypeclub/pype/issues/131) +- _(maya)_ When extracting animation, the "Write Color Set" options on the instance were not respected. [#108](https://github.com/pypeclub/pype/issues/108) +- _(maya)_ Attribute overrides for AOV only worked for the legacy render layers. Now it works for new render setup as well [#132](https://github.com/pypeclub/pype/issues/132) +- _(maya)_ Stability and usability improvements in yeti workflow [#104](https://github.com/pypeclub/pype/issues/104) + + + + +## 2.8.0 ## + +_**release date:** 20 April 2020_ + +**new:** + +- _(pype)_ Option to generate slates from json templates. [PYPE-628] [#26](https://github.com/pypeclub/pype/issues/26) +- _(pype)_ It is now possible to automate loading of published subsets into any scene. Documentation will follow :). [PYPE-611] [#24](https://github.com/pypeclub/pype/issues/24) + +**fix:** + +- _(maya)_ Some Redshift render tokens could break publishing. [PYPE-778] [#33](https://github.com/pypeclub/pype/issues/33) +- _(maya)_ Publish was not preserving maya file extension. [#39](https://github.com/pypeclub/pype/issues/39) +- _(maya)_ Rig output validator was failing on nodes without shapes. [#40](https://github.com/pypeclub/pype/issues/40) +- _(maya)_ Yeti caches can now be properly versioned up in the scene inventory. [#40](https://github.com/pypeclub/pype/issues/40) +- _(nuke)_ Build first workfiles was not accepting jpeg sequences. [#34](https://github.com/pypeclub/pype/issues/34) +- _(deadline)_ Trying to generate ffmpeg review from multipart EXRs no longer crashes publishing. [PYPE-781] +- _(deadline)_ Render publishing is more stable in multiplatform environments. [PYPE-775] + + + + +## 2.7.0 ## + +_**release date:** 30 March 2020_ + +**new:** + +- _(maya)_ Artist can now choose to load multiple references of the same subset at once [PYPE-646, PYPS-81] +- _(nuke)_ Option to use named OCIO colorspaces for review colour baking. [PYPS-82] +- _(pype)_ Pype can now work with `master` versions for publishing and loading. These are non-versioned publishes that are overwritten with the latest version during publish. These are now supported in all the GUIs, but their publishing is deactivated by default. [PYPE-653] +- _(blender)_ Added support for basic blender workflow. We currently support `rig`, `model` and `animation` families. [PYPE-768] +- _(pype)_ Source timecode can now be used in burn-ins. [PYPE-777] +- _(pype)_ Review outputs profiles can now specify delivery resolution different than project setting [PYPE-759] +- _(nuke)_ Bookmark to current context is now added automatically to all nuke browser windows. [PYPE-712] + +**change:** + +- _(maya)_ It is now possible to publish camera without. baking. Keep in mind that unbaked cameras can't be guaranteed to work in other hosts. [PYPE-595] +- _(maya)_ All the renders from maya are now grouped in the loader by their Layer name. [PYPE-482] +- _(nuke/hiero)_ Any publishes from nuke and hiero can now be versioned independently of the workfile. [PYPE-728] + + +**fix:** + +- _(nuke)_ Mixed slashes caused issues in ocio config path. +- _(pype)_ Intent field in pyblish GUI was passing label instead of value to ftrack. [PYPE-733] +- _(nuke)_ Publishing of pre-renders was inconsistent. [PYPE-766] +- _(maya)_ Handles and frame ranges were inconsistent in various places during publishing. +- _(nuke)_ Nuke was crashing if it ran into certain missing knobs. For example DPX output missing `autocrop` [PYPE-774] +- _(deadline)_ Project overrides were not working properly with farm render publishing. +- _(hiero)_ Problems with single frame plates publishing. +- _(maya)_ Redshift RenderPass token were breaking render publishing. [PYPE-778] +- _(nuke)_ Build first workfile was not accepting jpeg sequences. +- _(maya)_ Multipart (Multilayer) EXRs were breaking review publishing due to FFMPEG incompatiblity [PYPE-781] + + + +## 2.6.0 ## + +_**release date:** 9 March 2020_ + +**change:** +- _(maya)_ render publishing has been simplified and made more robust. Render setup layers are now automatically added to publishing subsets and `render globals` family has been replaced with simple `render` [PYPE-570] +- _(avalon)_ change context and workfiles apps, have been merged into one, that allows both actions to be performed at the same time. [PYPE-747] +- _(pype)_ thumbnails are now automatically propagate to asset from the last published subset in the loader +- _(ftrack)_ publishing comment and intent are now being published to ftrack note as well as describtion. [PYPE-727] +- _(pype)_ when overriding existing version new old representations are now overriden, instead of the new ones just being appended. (to allow this behaviour, the version validator need to be disabled. [PYPE-690]) +- _(pype)_ burnin preset has been significantly simplified. It now doesn't require passing function to each field, but only need the actual text template. to use this, all the current burnin PRESETS MUST BE UPDATED for all the projects. +- _(ftrack)_ credentials are now stored on a per server basis, so it's possible to switch between ftrack servers without having to log in and out. [PYPE-723] + + +**new:** +- _(pype)_ production and development deployments now have different colour of the tray icon. Orange for Dev and Green for production [PYPE-718] +- _(maya)_ renders can now be attached to a publishable subset rather than creating their own subset. For example it is possible to create a reviewable `look` or `model` render and have it correctly attached as a representation of the subsets [PYPE-451] +- _(maya)_ after saving current scene into a new context (as a new shot for instance), all the scene publishing subsets data gets re-generated automatically to match the new context [PYPE-532] +- _(pype)_ we now support project specific publish, load and create plugins [PYPE-740] +- _(ftrack)_ new action that allow archiving/deleting old published versions. User can keep how many of the latest version to keep when the action is ran. [PYPE-748, PYPE-715] +- _(ftrack)_ it is now possible to monitor and restart ftrack event server using ftrack action. [PYPE-658] +- _(pype)_ validator that prevent accidental overwrites of previously published versions. [PYPE-680] +- _(avalon)_ avalon core updated to version 5.6.0 +- _(maya)_ added validator to make sure that relative paths are used when publishing arnold standins. +- _(nukestudio)_ it is now possible to extract and publish audio family from clip in nuke studio [PYPE-682] + +**fix**: +- _(maya)_ maya set framerange button was ignoring handles [PYPE-719] +- _(ftrack)_ sync to avalon was sometime crashing when ran on empty project +- _(nukestudio)_ publishing same shots after they've been previously archived/deleted would result in a crash. [PYPE-737] +- _(nuke)_ slate workflow was breaking in certain scenarios. [PYPE-730] +- _(pype)_ rendering publish workflow has been significantly improved to prevent error resulting from implicit render collection. [PYPE-665, PYPE-746] +- _(pype)_ launching application on a non-synced project resulted in obscure [PYPE-528] +- _(pype)_ missing keys in burnins no longer result in an error. [PYPE-706] +- _(ftrack)_ create folder structure action was sometimes failing for project managers due to wrong permissions. +- _(Nukestudio)_ using `source` in the start frame tag could result in wrong frame range calculation +- _(ftrack)_ sync to avalon action and event have been improved by catching more edge cases and provessing them properly. + + + +## 2.5.0 ## + +_**release date:** 11 Feb 2020_ + +**change:** +- _(pype)_ added many logs for easier debugging +- _(pype)_ review presets can now be separated between 2d and 3d renders [PYPE-693] +- _(pype)_ anatomy module has been greatly improved to allow for more dynamic pulblishing and faster debugging [PYPE-685] +- _(pype)_ avalon schemas have been moved from `pype-config` to `pype` repository, for simplification. [PYPE-670] +- _(ftrack)_ updated to latest ftrack API +- _(ftrack)_ publishing comments now appear in ftrack also as a note on version with customisable category [PYPE-645] +- _(ftrack)_ delete asset/subset action had been improved. It is now able to remove multiple entities and descendants of the selected entities [PYPE-361, PYPS-72] +- _(workfiles)_ added date field to workfiles app [PYPE-603] +- _(maya)_ old deprecated loader have been removed in favour of a single unified reference loader (old scenes will upgrade automatically to the new loader upon opening) [PYPE-633, PYPE-697] +- _(avalon)_ core updated to 5.5.15 [PYPE-671] +- _(nuke)_ library loader is now available in nuke [PYPE-698] + + +**new:** +- _(pype)_ added pype render wrapper to allow rendering on mixed platform farms. [PYPE-634] +- _(pype)_ added `pype launch` command. It let's admin run applications with dynamically built environment based on the given context. [PYPE-634] +- _(pype)_ added support for extracting review sequences with burnins [PYPE-657] +- _(publish)_ users can now set intent next to a comment when publishing. This will then be reflected on an attribute in ftrack. [PYPE-632] +- _(burnin)_ timecode can now be added to burnin +- _(burnin)_ datetime keys can now be added to burnin and anatomy [PYPE-651] +- _(burnin)_ anatomy templates can now be used in burnins. [PYPE=626] +- _(nuke)_ new validator for render resolution +- _(nuke)_ support for attach slate to nuke renders [PYPE-630] +- _(nuke)_ png sequences were added to loaders +- _(maya)_ added maya 2020 compatibility [PYPE-677] +- _(maya)_ ability to publish and load .ASS standin sequences [PYPS-54] +- _(pype)_ thumbnails can now be published and are visible in the loader. `AVALON_THUMBNAIL_ROOT` environment variable needs to be set for this to work [PYPE-573, PYPE-132] +- _(blender)_ base implementation of blender was added with publishing and loading of .blend files [PYPE-612] +- _(ftrack)_ new action for preparing deliveries [PYPE-639] + + +**fix**: +- _(burnin)_ more robust way of finding ffmpeg for burnins. +- _(pype)_ improved UNC paths remapping when sending to farm. +- _(pype)_ float frames sometimes made their way to representation context in database, breaking loaders [PYPE-668] +- _(pype)_ `pype install --force` was failing sometimes [PYPE-600] +- _(pype)_ padding in published files got calculated wrongly sometimes. It is now instead being always read from project anatomy. [PYPE-667] +- _(publish)_ comment publishing was failing in certain situations +- _(ftrack)_ multiple edge case scenario fixes in auto sync and sync-to-avalon action +- _(ftrack)_ sync to avalon now works on empty projects +- _(ftrack)_ thumbnail update event was failing when deleting entities [PYPE-561] +- _(nuke)_ loader applies proper colorspaces from Presets +- _(nuke)_ publishing handles didn't always work correctly [PYPE-686] +- _(maya)_ assembly publishing and loading wasn't working correctly + + + + + +## 2.4.0 ## + +_**release date:** 9 Dec 2019_ + +**change:** +- _(ftrack)_ version to status ftrack event can now be configured from Presets + - based on preset `presets/ftracc/ftrack_config.json["status_version_to_task"]` +- _(ftrack)_ sync to avalon event has been completely re-written. It now supports most of the project management situations on ftrack including moving, renaming and deleting entities, updating attributes and working with tasks. +- _(ftrack)_ sync to avalon action has been also re-writen. It is now much faster (up to 100 times depending on a project structure), has much better logging and reporting on encountered problems, and is able to handle much more complex situations. +- _(ftrack)_ sync to avalon trigger by checking `auto-sync` toggle on ftrack [PYPE-504] +- _(pype)_ various new features in the REST api +- _(pype)_ new visual identity used across pype +- _(pype)_ started moving all requirements to pip installation rather than vendorising them in pype repository. Due to a few yet unreleased packages, this means that pype can temporarily be only installed in the offline mode. + +**new:** +- _(nuke)_ support for publishing gizmos and loading them as viewer processes +- _(nuke)_ support for publishing nuke nodes from backdrops and loading them back +- _(pype)_ burnins can now work with start and end frames as keys + - use keys `{frame_start}`, `{frame_end}` and `{current_frame}` in burnin preset to use them. [PYPS-44,PYPS-73, PYPE-602] +- _(pype)_ option to filter logs by user and level in loggin GUI +- _(pype)_ image family added to standalone publisher [PYPE-574] +- _(pype)_ matchmove family added to standalone publisher [PYPE-574] +- _(nuke)_ validator for comparing arbitrary knobs with values from presets +- _(maya)_ option to force maya to copy textures in the new look publish rather than hardlinking them +- _(pype)_ comments from pyblish GUI are now being added to ftrack version +- _(maya)_ validator for checking outdated containers in the scene +- _(maya)_ option to publish and load arnold standin sequence [PYPE-579, PYPS-54] + +**fix**: +- _(pype)_ burnins were not respecting codec of the input video +- _(nuke)_ lot's of various nuke and nuke studio fixes across the board [PYPS-45] +- _(pype)_ workfiles app is not launching with the start of the app by default [PYPE-569] +- _(ftrack)_ ftrack integration during publishing was failing under certain situations [PYPS-66] +- _(pype)_ minor fixes in REST api +- _(ftrack)_ status change event was crashing when the target status was missing [PYPS-68] +- _(ftrack)_ actions will try to reconnect if they fail for some reason +- _(maya)_ problems with fps mapping when using float FPS values +- _(deadline)_ overall improvements to deadline publishing +- _(setup)_ environment variables are now remapped on the fly based on the platform pype is running on. This fixes many issues in mixed platform environments. + + + +## 2.3.6 # + +_**release date:** 27 Nov 2019_ + +**hotfix**: +- _(ftrack)_ was hiding important debug logo +- _(nuke)_ crashes during workfile publishing +- _(ftrack)_ event server crashes because of signal problems +- _(muster)_ problems with muster render submissions +- _(ftrack)_ thumbnail update event syntax errors + + +## 2.3.0 ## +_release date: 6 Oct 2019_ + +**new**: +- _(maya)_ support for yeti rigs and yeti caches +- _(maya)_ validator for comparing arbitrary attributes against ftrack +- _(pype)_ burnins can now show current date and time +- _(muster)_ pools can now be set in render globals in maya +- _(pype)_ Rest API has been implemented in beta stage +- _(nuke)_ LUT loader has been added +- _(pype)_ rudimentary user module has been added as preparation for user management +- _(pype)_ a simple logging GUI has been added to pype tray +- _(nuke)_ nuke can now bake input process into mov +- _(maya)_ imported models now have selection handle displayed by defaulting +- _(avalon)_ it's is now possible to load multiple assets at once using loader +- _(maya)_ added ability to automatically connect yeti rig to a mesh upon loading + +**changed**: +- _(ftrack)_ event server now runs two parallel processes and is able to keep queue of events to process. +- _(nuke)_ task name is now added to all rendered subsets +- _(pype)_ adding more families to standalone publisher +- _(pype)_ standalone publisher now uses pyblish-lite +- _(pype)_ standalone publisher can now create review quicktimes +- _(ftrack)_ queries to ftrack were sped up +- _(ftrack)_ multiple ftrack action have been deprecated +- _(avalon)_ avalon upstream has been updated to 5.5.0 +- _(nukestudio)_ published transforms can now be animated +- + +**fix**: +- _(maya)_ fps popup button didn't work in some cases +- _(maya)_ geometry instances and references in maya were losing shader assignments +- _(muster)_ muster rendering templates were not working correctly +- _(maya)_ arnold tx texture conversion wasn't respecting colorspace set by the artist +- _(pype)_ problems with avalon db sync +- _(maya)_ ftrack was rounding FPS making it inconsistent +- _(pype)_ wrong icon names in Creator +- _(maya)_ scene inventory wasn't showing anything if representation was removed from database after it's been loaded to the scene +- _(nukestudio)_ multiple bugs squashed +- _(loader)_ loader was taking long time to show all the loading action when first launcher in maya + +## 2.2.0 ## +_release date: 8 Sept 2019_ + +**new**: +- _(pype)_ add customisable workflow for creating quicktimes from renders or playblasts +- _(nuke)_ option to choose deadline chunk size on write nodes +- _(nukestudio)_ added option to publish soft effects (subTrackItems) from NukeStudio as subsets including LUT files. these can then be loaded in nuke or NukeStudio +- _(nuke)_ option to build nuke script from previously published latest versions of plate and render subsets. +- _(nuke)_ nuke writes now have deadline tab. +- _(ftrack)_ Prepare Project action can now be used for creating the base folder structure on disk and in ftrack, setting up all the initial project attributes and it automatically prepares `pype_project_config` folder for the given project. +- _(clockify)_ Added support for time tracking in clockify. This currently in addition to ftrack time logs, but does not completely replace them. +- _(pype)_ any attributes in Creator and Loader plugins can now be customised using pype preset system + +**changed**: +- nukestudio now uses workio API for workfiles +- _(maya)_ "FIX FPS" prompt in maya now appears in the middle of the screen +- _(muster)_ can now be configured with custom templates +- _(pype)_ global publishing plugins can now be configured using presets as well as host specific ones + + +**fix**: +- wrong version retrieval from path in certain scenarios +- nuke reset resolution wasn't working in certain scenarios + +## 2.1.0 ## +_release date: 6 Aug 2019_ + +A large cleanup release. Most of the change are under the hood. + +**new**: +- _(pype)_ add customisable workflow for creating quicktimes from renders or playblasts +- _(pype)_ Added configurable option to add burnins to any generated quicktimes +- _(ftrack)_ Action that identifies what machines pype is running on. +- _(system)_ unify subprocess calls +- _(maya)_ add audio to review quicktimes +- _(nuke)_ add crop before write node to prevent overscan problems in ffmpeg +- **Nuke Studio** publishing and workfiles support +- **Muster** render manager support +- _(nuke)_ Framerange, FPS and Resolution are set automatically at startup +- _(maya)_ Ability to load published sequences as image planes +- _(system)_ Ftrack event that sets asset folder permissions based on task assignees in ftrack. +- _(maya)_ Pyblish plugin that allow validation of maya attributes +- _(system)_ added better startup logging to tray debug, including basic connection information +- _(avalon)_ option to group published subsets to groups in the loader +- _(avalon)_ loader family filters are working now + +**changed**: +- change multiple key attributes to unify their behaviour across the pipeline + - `frameRate` to `fps` + - `startFrame` to `frameStart` + - `endFrame` to `frameEnd` + - `fstart` to `frameStart` + - `fend` to `frameEnd` + - `handle_start` to `handleStart` + - `handle_end` to `handleEnd` + - `resolution_width` to `resolutionWidth` + - `resolution_height` to `resolutionHeight` + - `pixel_aspect` to `pixelAspect` + +- _(nuke)_ write nodes are now created inside group with only some attributes editable by the artist +- rendered frames are now deleted from temporary location after their publishing is finished. +- _(ftrack)_ RV action can now be launched from any entity +- after publishing only refresh button is now available in pyblish UI +- added context instance pyblish-lite so that artist knows if context plugin fails +- _(avalon)_ allow opening selected files using enter key +- _(avalon)_ core updated to v5.2.9 with our forked changes on top + +**fix**: +- faster hierarchy retrieval from db +- _(nuke)_ A lot of stability enhancements +- _(nuke studio)_ A lot of stability enhancements +- _(nuke)_ now only renders a single write node on farm +- _(ftrack)_ pype would crash when launcher project level task +- work directory was sometimes not being created correctly +- major pype.lib cleanup. Removing of unused functions, merging those that were doing the same and general house cleaning. +- _(avalon)_ subsets in maya 2019 weren't behaving correctly in the outliner diff --git a/changelog.md b/changelog.md deleted file mode 100644 index bdee041615..0000000000 --- a/changelog.md +++ /dev/null @@ -1,120 +0,0 @@ -# Pype changelog # -Welcome to pype changelog - -## 2.3.0 ## -_release date: 6 Oct 2019_ - -**new**: -- _(maya)_ support for yeti rigs and yeti caches -- _(maya)_ validator for comparing arbitrary attributes against ftrack -- _(pype)_ burnins can now show current date and time -- _(muster)_ pools can now be set in render globals in maya -- _(pype)_ Rest API has been implemented in beta stage -- _(nuke)_ LUT loader has been added -- _(pype)_ rudimentary user module has been added as preparation for user management -- _(pype)_ a simple logging GUI has been added to pype tray -- _(nuke)_ nuke can now bake input process into mov -- _(maya)_ imported models now have selection handle displayed by defaulting -- _(avalon)_ it's is now possible to load multiple assets at once using loader -- _(maya)_ added ability to automatically connect yeti rig to a mesh upon loading - -**changed**: -- _(ftrack)_ event server now runs two parallel processes and is able to keep queue of events to process. -- _(nuke)_ task name is now added to all rendered subsets -- _(pype)_ adding more families to standalone publisher -- _(pype)_ standalone publisher now uses pyblish-lite -- _(pype)_ standalone publisher can now create review quicktimes -- _(ftrack)_ queries to ftrack were sped up -- _(ftrack)_ multiple ftrack action have been deprecated -- _(avalon)_ avalon upstream has been updated to 5.5.0 -- _(nukestudio)_ published transforms can now be animated -- - -**fix**: -- _(maya)_ fps popup button didn't work in some cases -- _(maya)_ geometry instances and references in maya were losing shader assignments -- _(muster)_ muster rendering templates were not working correctly -- _(maya)_ arnold tx texture conversion wasn't respecting colorspace set by the artist -- _(pype)_ problems with avalon db sync -- _(maya)_ ftrack was rounding FPS making it inconsistent -- _(pype)_ wrong icon names in Creator -- _(maya)_ scene inventory wasn't showing anything if representation was removed from database after it's been loaded to the scene -- _(nukestudio)_ multiple bugs squashed -- _(loader)_ loader was taking long time to show all the loading action when first launcher in maya - -## 2.2.0 ## -_release date: 8 Sept 2019_ - -**new**: -- _(pype)_ add customisable workflow for creating quicktimes from renders or playblasts -- _(nuke)_ option to choose deadline chunk size on write nodes -- _(nukestudio)_ added option to publish soft effects (subTrackItems) from NukeStudio as subsets including LUT files. these can then be loaded in nuke or NukeStudio -- _(nuke)_ option to build nuke script from previously published latest versions of plate and render subsets. -- _(nuke)_ nuke writes now have deadline tab. -- _(ftrack)_ Prepare Project action can now be used for creating the base folder structure on disk and in ftrack, setting up all the initial project attributes and it automatically prepares `pype_project_config` folder for the given project. -- _(clockify)_ Added support for time tracking in clockify. This currently in addition to ftrack time logs, but does not completely replace them. -- _(pype)_ any attributes in Creator and Loader plugins can now be customised using pype preset system - -**changed**: -- nukestudio now uses workio API for workfiles -- _(maya)_ "FIX FPS" prompt in maya now appears in the middle of the screen -- _(muster)_ can now be configured with custom templates -- _(pype)_ global publishing plugins can now be configured using presets as well as host specific ones - - -**fix**: -- wrong version retrieval from path in certain scenarios -- nuke reset resolution wasn't working in certain scenarios - -## 2.1.0 ## -_release date: 6 Aug 2019_ - -A large cleanup release. Most of the change are under the hood. - -**new**: -- _(pype)_ add customisable workflow for creating quicktimes from renders or playblasts -- _(pype)_ Added configurable option to add burnins to any generated quicktimes -- _(ftrack)_ Action that identifies what machines pype is running on. -- _(system)_ unify subprocess calls -- _(maya)_ add audio to review quicktimes -- _(nuke)_ add crop before write node to prevent overscan problems in ffmpeg -- **Nuke Studio** publishing and workfiles support -- **Muster** render manager support -- _(nuke)_ Framerange, FPS and Resolution are set automatically at startup -- _(maya)_ Ability to load published sequences as image planes -- _(system)_ Ftrack event that sets asset folder permissions based on task assignees in ftrack. -- _(maya)_ Pyblish plugin that allow validation of maya attributes -- _(system)_ added better startup logging to tray debug, including basic connection information -- _(avalon)_ option to group published subsets to groups in the loader -- _(avalon)_ loader family filters are working now - -**changed**: -- change multiple key attributes to unify their behaviour across the pipeline - - `frameRate` to `fps` - - `startFrame` to `frameStart` - - `endFrame` to `frameEnd` - - `fstart` to `frameStart` - - `fend` to `frameEnd` - - `handle_start` to `handleStart` - - `handle_end` to `handleEnd` - - `resolution_width` to `resolutionWidth` - - `resolution_height` to `resolutionHeight` - - `pixel_aspect` to `pixelAspect` - -- _(nuke)_ write nodes are now created inside group with only some attributes editable by the artist -- rendered frames are now deleted from temporary location after their publishing is finished. -- _(ftrack)_ RV action can now be launched from any entity -- after publishing only refresh button is now available in pyblish UI -- added context instance pyblish-lite so that artist knows if context plugin fails -- _(avalon)_ allow opening selected files using enter key -- _(avalon)_ core updated to v5.2.9 with our forked changes on top - -**fix**: -- faster hierarchy retrieval from db -- _(nuke)_ A lot of stability enhancements -- _(nuke studio)_ A lot of stability enhancements -- _(nuke)_ now only renders a single write node on farm -- _(ftrack)_ pype would crash when launcher project level task -- work directory was sometimes not being created correctly -- major pype.lib cleanup. Removing of unused functions, merging those that were doing the same and general house cleaning. -- _(avalon)_ subsets in maya 2019 weren't behaving correctly in the outliner From 93f7391e5a899a14adb1c8a0c964a26a9e565c23 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 9 Sep 2020 17:49:19 +0200 Subject: [PATCH 507/813] convert data from gui data t o config data --- pype/tools/config_setting/config_setting/widgets/base.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/base.py b/pype/tools/config_setting/config_setting/widgets/base.py index 172450684b..02116b6044 100644 --- a/pype/tools/config_setting/config_setting/widgets/base.py +++ b/pype/tools/config_setting/config_setting/widgets/base.py @@ -126,7 +126,7 @@ class SystemWidget(QtWidgets.QWidget): if value is not lib.NOT_SET: _data.update(value) - values = _data["system"] + values = lib.convert_gui_data_to_overrides(_data.get("system", {})) dirpath = os.path.dirname(SYSTEM_CONFIGURATIONS_PATH) if not os.path.exists(dirpath): @@ -456,7 +456,7 @@ class ProjectWidget(QtWidgets.QWidget): if value is not lib.NOT_SET: _data.update(value) - output = _data["project"] + output = lib.convert_gui_data_to_overrides(_data.get("project", {})) dirpath = os.path.dirname(PROJECT_CONFIGURATIONS_PATH) if not os.path.exists(dirpath): From 037a573f7ca7f774d3855bbf16250844dd517827 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 9 Sep 2020 17:51:22 +0200 Subject: [PATCH 508/813] a lot of changes to be able to get studio overrides --- .../config_setting/widgets/inputs.py | 223 ++++++++---------- 1 file changed, 93 insertions(+), 130 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 3a3d517cc1..cf42d47ab5 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -16,15 +16,20 @@ class ConfigObject(AbstractConfigObject): default_state = "" - _has_studio_override = False _as_widget = False - _is_overriden = False - _is_modified = False - _was_overriden = False - _is_invalid = False _is_group = False + # TODO not implemented yet _is_nullable = False + _has_studio_override = False + _had_studio_override = False + + _is_overriden = False + _was_overriden = False + + _is_modified = False + _is_invalid = False + _any_parent_is_group = None _log = None @@ -38,6 +43,10 @@ class ConfigObject(AbstractConfigObject): def has_studio_override(self): return self._has_studio_override or self._parent.has_studio_override + @property + def had_studio_override(self): + return self._had_studio_override + @property def any_parent_is_group(self): if self._any_parent_is_group is None: @@ -47,7 +56,11 @@ class ConfigObject(AbstractConfigObject): @property def is_modified(self): """Has object any changes that require saving.""" - return self._is_modified or (self.was_overriden != self.is_overriden) + return ( + self._is_modified + or (self.was_overriden != self.is_overriden) + or (self.has_studio_override != self.had_studio_override) + ) @property def is_overriden(self): @@ -200,8 +213,9 @@ class InputObject(ConfigObject): ) self.default_value = value - if not self.has_studio_override: - self.set_value(value) + self._has_studio_override = False + self._had_studio_override = False + self.set_value(value) def update_studio_values(self, parent_values): value = NOT_SET @@ -219,8 +233,31 @@ class InputObject(ConfigObject): self.set_value(self.default_value) self._has_studio_override = False + self._had_studio_override = bool(self._has_studio_override) self._is_modified = False + def _on_value_change(self, item=None): + if self.ignore_value_changes: + return + + if self.is_overidable: + self._is_overriden = True + else: + self._has_studio_override = True + + if self._is_invalid: + self._is_modified = True + elif self._is_overriden: + self._is_modified = self.item_value() != self.override_value + elif self._has_studio_override: + self._is_modified = self.item_value() != self.studio_value + else: + self._is_modified = self.item_value() != self.default_value + + self.update_style() + + self.value_changed.emit(self) + def studio_overrides(self): if not self.has_studio_override: return NOT_SET, False @@ -372,24 +409,6 @@ class BooleanWidget(QtWidgets.QWidget, InputObject): # value as `value` the `_on_value_change` is not triggered self.checkbox.setChecked(value) - def _on_value_change(self, item=None): - if self.ignore_value_changes: - return - - if self.is_overidable: - self._is_overriden = True - - if self._is_invalid: - self._is_modified = True - elif self._is_overriden: - self._is_modified = self.item_value() != self.override_value - else: - self._is_modified = self.item_value() != self.studio_value - - self.update_style() - - self.value_changed.emit(self) - def update_style(self): state = self.style_state( self.has_studio_override, @@ -464,24 +483,6 @@ class NumberWidget(QtWidgets.QWidget, InputObject): def set_value(self, value): self.input_field.setValue(value) - def _on_value_change(self, item=None): - if self.ignore_value_changes: - return - - if self.is_overidable: - self._is_overriden = True - - if self._is_invalid: - self._is_modified = True - elif self._is_overriden: - self._is_modified = self.item_value() != self.override_value - else: - self._is_modified = self.item_value() != self.studio_value - - self.update_style() - - self.value_changed.emit(self) - def update_style(self): state = self.style_state( self.has_studio_override, @@ -559,24 +560,6 @@ class TextWidget(QtWidgets.QWidget, InputObject): else: self.text_input.setText(value) - def _on_value_change(self, item=None): - if self.ignore_value_changes: - return - - if self.is_overidable: - self._is_overriden = True - - if self._is_invalid: - self._is_modified = True - elif self._is_overriden: - self._is_modified = self.item_value() != self.override_value - else: - self._is_modified = self.item_value() != self.studio_value - - self.update_style() - - self.value_changed.emit(self) - def update_style(self): state = self.style_state( self.has_studio_override, @@ -651,24 +634,6 @@ class PathInputWidget(QtWidgets.QWidget, InputObject): self.path_input.clear_end_path() super(PathInput, self).focusOutEvent(event) - def _on_value_change(self, item=None): - if self.ignore_value_changes: - return - - if self.is_overidable: - self._is_overriden = True - - if self._is_invalid: - self._is_modified = True - elif self.is_overriden: - self._is_modified = self.item_value() != self.override_value - else: - self._is_modified = self.item_value() != self.studio_value - - self.update_style() - - self.value_changed.emit(self) - def update_style(self): state = self.style_state( self.has_studio_override, @@ -804,24 +769,9 @@ class RawJsonWidget(QtWidgets.QWidget, InputObject): def set_value(self, value): self.text_input.set_value(value) - def _on_value_change(self, item=None): + def _on_value_change(self, *args, **kwargs): self._is_invalid = self.text_input.has_invalid_value() - if self.ignore_value_changes: - return - - if self.is_overidable: - self._is_overriden = True - - if self._is_invalid: - self._is_modified = True - elif self._is_overriden: - self._is_modified = self.item_value() != self.override_value - else: - self._is_modified = self.item_value() != self.studio_value - - self.update_style() - - self.value_changed.emit(self) + return super(RawJsonWidget, self)._on_value_change(*args, **kwargs) def update_style(self): state = self.style_state( @@ -1006,24 +956,6 @@ class ListWidget(QtWidgets.QWidget, InputObject): if self.count() == 0: self.add_row(is_empty=True) - def _on_value_change(self, item=None): - if self.ignore_value_changes: - return - - if self.is_overidable: - self._is_overriden = True - - if self._is_invalid: - self._is_modified = True - elif self._is_overriden: - self._is_modified = self.item_value() != self.override_value - else: - self._is_modified = self.item_value() != self.studio_value - - self.update_style() - - self.value_changed.emit(self) - def add_row(self, row=None, value=None, is_empty=False): # Create new item item_widget = ListItem( @@ -1351,6 +1283,9 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): self.add_row(is_empty=True) def _on_value_change(self, item=None): + if self.ignore_value_changes: + return + fields_by_keys = collections.defaultdict(list) for input_field in self.input_fields: key = input_field.key_value() @@ -1367,18 +1302,19 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): field.is_key_duplicated = True field.update_style() - if self.ignore_value_changes: - return - if self.is_overidable: self._is_overriden = True + else: + self._has_studio_override = True - if self.is_invalid: + if self._is_invalid: self._is_modified = True elif self._is_overriden: self._is_modified = self.item_value() != self.override_value - else: + elif self._has_studio_override: self._is_modified = self.item_value() != self.studio_value + else: + self._is_modified = self.item_value() != self.default_value self.update_style() @@ -1633,6 +1569,12 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): if parent_values is not NOT_SET: value = parent_values.get(self.key, NOT_SET) + if value is NOT_SET: + self._has_studio_override = False + else: + self._has_studio_override = True + self._had_studio_override = bool(self._has_studio_override) + for item in self.input_fields: item.update_studio_values(value) @@ -1669,6 +1611,8 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): if self.is_group: if self.is_overidable: self._is_overriden = True + else: + self._has_studio_override = True self.hierarchical_style_update() @@ -1702,7 +1646,7 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): self._child_state = child_state state = self.style_state( - child_has_studio_override, + self.had_studio_override, child_invalid, self.is_overriden, self.is_modified @@ -1724,7 +1668,10 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): @property def child_has_studio_override(self): for input_field in self.input_fields: - if input_field.child_has_studio_override: + if ( + input_field.has_studio_override + or input_field.child_has_studio_override + ): return True return False @@ -1770,7 +1717,7 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): values = {} groups = [] for input_field in self.input_fields: - value, is_group = input_field.overrides() + value, is_group = input_field.studio_overrides() if value is not NOT_SET: values.update(value) if is_group: @@ -1850,7 +1797,10 @@ class DictInvisible(QtWidgets.QWidget, ConfigObject): @property def child_has_studio_override(self): for input_field in self.input_fields: - if input_field.child_has_studio_override: + if ( + input_field.has_studio_override + or input_field.child_has_studio_override + ): return True return False @@ -1896,6 +1846,8 @@ class DictInvisible(QtWidgets.QWidget, ConfigObject): if self.is_group: if self.is_overidable: self._is_overriden = True + else: + self._has_studio_override = True self.hierarchical_style_update() self.value_changed.emit(self) @@ -1982,7 +1934,7 @@ class DictInvisible(QtWidgets.QWidget, ConfigObject): values = {} groups = [] for input_field in self.input_fields: - value, is_group = input_field.overrides() + value, is_group = input_field.studio_overrides() if value is not NOT_SET: values.update(value) if is_group: @@ -2174,20 +2126,24 @@ class PathWidget(QtWidgets.QWidget, ConfigObject): if self.is_overidable: self._is_overriden = True + else: + self._has_studio_override = True if self._is_invalid: self._is_modified = True elif self._is_overriden: self._is_modified = self.item_value() != self.override_value - else: + elif self._has_studio_override: self._is_modified = self.item_value() != self.studio_value + else: + self._is_modified = self.item_value() != self.default_value self.hierarchical_style_update() self.value_changed.emit(self) def update_style(self, is_overriden=None): - child_has_studio_override = self.has_studio_override + child_has_studio_override = self.child_has_studio_override child_modified = self.child_modified child_invalid = self.child_invalid child_state = self.style_state( @@ -2240,7 +2196,10 @@ class PathWidget(QtWidgets.QWidget, ConfigObject): @property def child_has_studio_override(self): for input_field in self.input_fields: - if input_field.child_has_studio_override: + if ( + input_field.has_studio_override + or input_field.child_has_studio_override + ): return True return False @@ -2407,6 +2366,7 @@ class DictFormWidget(QtWidgets.QWidget, ConfigObject): def _on_value_change(self, item=None): if self.ignore_value_changes: return + self.value_changed.emit(self) if self.any_parent_is_group: self.hierarchical_style_update() @@ -2414,7 +2374,10 @@ class DictFormWidget(QtWidgets.QWidget, ConfigObject): @property def child_has_studio_override(self): for input_field in self.input_fields: - if input_field.child_has_studio_override: + if ( + input_field.has_studio_override + or input_field.child_has_studio_override + ): return True return False @@ -2467,7 +2430,7 @@ class DictFormWidget(QtWidgets.QWidget, ConfigObject): values = {} groups = [] for input_field in self.input_fields: - value, is_group = input_field.overrides() + value, is_group = input_field.studio_overrides() if value is not NOT_SET: values.update(value) if is_group: From 6bdee49e2fd0d66e2b32b31eb56bcac3699d26f6 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 9 Sep 2020 17:52:52 +0200 Subject: [PATCH 509/813] underscored attributes are kept --- .../config_setting/widgets/base.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/base.py b/pype/tools/config_setting/config_setting/widgets/base.py index 02116b6044..cfbed884d9 100644 --- a/pype/tools/config_setting/config_setting/widgets/base.py +++ b/pype/tools/config_setting/config_setting/widgets/base.py @@ -17,10 +17,10 @@ from avalon import io class SystemWidget(QtWidgets.QWidget): is_overidable = False - has_studio_override = False - is_overriden = False - is_group = False - any_parent_is_group = False + has_studio_override = _has_studio_override = False + is_overriden = _is_overriden = False + is_group = _is_group = False + any_parent_is_group = _any_parent_is_group = False def __init__(self, parent=None): super(SystemWidget, self).__init__(parent) @@ -286,10 +286,10 @@ class ProjectListWidget(QtWidgets.QWidget): class ProjectWidget(QtWidgets.QWidget): - has_studio_override = False - is_overriden = False - is_group = False - any_parent_is_group = False + has_studio_override = _has_studio_override = False + is_overriden = _is_overriden = False + is_group = _is_group = False + any_parent_is_group = _any_parent_is_group = False def __init__(self, parent=None): super(ProjectWidget, self).__init__(parent) From b3f6c8296510e37f5940f945210496c64f6689b1 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 9 Sep 2020 18:00:41 +0200 Subject: [PATCH 510/813] ignore value changes during loading of studio overrides --- .../config_setting/widgets/base.py | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/base.py b/pype/tools/config_setting/config_setting/widgets/base.py index cfbed884d9..a143e584dd 100644 --- a/pype/tools/config_setting/config_setting/widgets/base.py +++ b/pype/tools/config_setting/config_setting/widgets/base.py @@ -139,8 +139,11 @@ class SystemWidget(QtWidgets.QWidget): self._update_values() def _update_values(self): - default_values = default_configuration() - default_values = {"system": default_values["system_configurations"]} + self.ignore_value_changes = True + + default_values = { + "system": default_configuration()["system_configurations"] + } for input_field in self.input_fields: input_field.update_default_values(default_values) @@ -148,8 +151,7 @@ class SystemWidget(QtWidgets.QWidget): for input_field in self.input_fields: input_field.update_studio_values(system_values) - for input_field in self.input_fields: - input_field.hierarchical_style_update() + self.ignore_value_changes = False def add_children_gui(self, child_configuration): item_type = child_configuration["type"] @@ -469,8 +471,11 @@ class ProjectWidget(QtWidgets.QWidget): self._update_values() def _update_values(self): - default_values = default_configuration() - default_values = {"project": default_values["project_configurations"]} + self.ignore_value_changes = True + + default_values = { + "project": default_configuration()["project_configurations"] + } for input_field in self.input_fields: input_field.update_default_values(default_values) @@ -478,5 +483,4 @@ class ProjectWidget(QtWidgets.QWidget): for input_field in self.input_fields: input_field.update_studio_values(studio_values) - for input_field in self.input_fields: - input_field.hierarchical_style_update() + self.ignore_value_changes = False From a4babc31da11cab5e9841c5187e03d409db99640 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 9 Sep 2020 18:00:59 +0200 Subject: [PATCH 511/813] few minor changes --- pype/tools/config_setting/config_setting/widgets/inputs.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index cf42d47ab5..ff4448992d 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -762,10 +762,10 @@ class RawJsonWidget(QtWidgets.QWidget, InputObject): self.text_input.textChanged.connect(self._on_value_change) def update_studio_values(self, parent_values): - super(RawJsonWidget, self).update_studio_values(parent_values) - self._is_invalid = self.text_input.has_invalid_value() + super(RawJsonWidget, self).update_studio_values(parent_values) + def set_value(self, value): self.text_input.set_value(value) @@ -1765,6 +1765,8 @@ class DictInvisible(QtWidgets.QWidget, ConfigObject): self._any_parent_is_group = any_parent_is_group self._is_group = input_data.get("is_group", False) + if self._is_group: + raise TypeError("DictInvisible can't be marked as group input.") self.setAttribute(QtCore.Qt.WA_TranslucentBackground) From 0506d513954a29950a517a194d8203c7da4da84b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 9 Sep 2020 18:38:54 +0200 Subject: [PATCH 512/813] fix dict studio override --- pype/tools/config_setting/config_setting/widgets/inputs.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index ff4448992d..4a71f0380c 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1569,10 +1569,10 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): if parent_values is not NOT_SET: value = parent_values.get(self.key, NOT_SET) - if value is NOT_SET: - self._has_studio_override = False - else: + self._has_studio_override = False + if self.is_group and value is not NOT_SET: self._has_studio_override = True + self._had_studio_override = bool(self._has_studio_override) for item in self.input_fields: From d372f9077eff211a34c8849a85ee422679da5554 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Thu, 10 Sep 2020 09:54:58 +0200 Subject: [PATCH 513/813] changelog update --- .github_changelog_generator | 2 +- CHANGELOG.md | 565 ++++++++++++++++++++++++++++++++++++ HISTORY.md | 3 - 3 files changed, 566 insertions(+), 4 deletions(-) create mode 100644 CHANGELOG.md diff --git a/.github_changelog_generator b/.github_changelog_generator index 93ab39f4d0..cd09ebcbfa 100644 --- a/.github_changelog_generator +++ b/.github_changelog_generator @@ -4,4 +4,4 @@ author=False unreleased=False since-tag=2.11.0 release-branch=master -enhancement-label=New / Enhancements +enhancement-label=**Enhancements:** diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000000..ba86b85eec --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,565 @@ +# Changelog + +## [2.12.0](https://github.com/pypeclub/pype/tree/2.12.0) (2020-09-09) + +[Full Changelog](https://github.com/pypeclub/pype/compare/2.11.8...2.12.0) + +**Enhancements:** + +- Less mongo connections [\#509](https://github.com/pypeclub/pype/pull/509) +- Nuke: adding image loader [\#499](https://github.com/pypeclub/pype/pull/499) +- Move launcher window to top if launcher action is clicked [\#450](https://github.com/pypeclub/pype/pull/450) +- Maya: better tile rendering support in Pype [\#446](https://github.com/pypeclub/pype/pull/446) +- Implementation of non QML launcher [\#443](https://github.com/pypeclub/pype/pull/443) +- Optional skip review on renders. [\#441](https://github.com/pypeclub/pype/pull/441) +- Ftrack: Option to push status from task to latest version [\#440](https://github.com/pypeclub/pype/pull/440) +- Properly containerize image plane loads. [\#434](https://github.com/pypeclub/pype/pull/434) +- Option to keep the review files. [\#426](https://github.com/pypeclub/pype/pull/426) +- Isolate view on instance members. [\#425](https://github.com/pypeclub/pype/pull/425) +- ftrack group is bcw compatible [\#418](https://github.com/pypeclub/pype/pull/418) +- Maya: Publishing of tile renderings on Deadline [\#398](https://github.com/pypeclub/pype/pull/398) +- Feature/little bit better logging gui [\#383](https://github.com/pypeclub/pype/pull/383) + +**Fixed bugs:** + +- Maya: Fix tile order for Draft Tile Assembler [\#511](https://github.com/pypeclub/pype/pull/511) +- Remove extra dash [\#501](https://github.com/pypeclub/pype/pull/501) +- Fix: strip dot from repre names in single frame renders [\#498](https://github.com/pypeclub/pype/pull/498) +- Better handling of destination during integrating [\#485](https://github.com/pypeclub/pype/pull/485) +- Fix: allow thumbnail creation for single frame renders [\#460](https://github.com/pypeclub/pype/pull/460) +- added missing argument to launch\_application in ftrack app handler [\#453](https://github.com/pypeclub/pype/pull/453) +- Burnins: Copy bit rate of input video to match quality. [\#448](https://github.com/pypeclub/pype/pull/448) +- Standalone publisher is now independent from tray [\#442](https://github.com/pypeclub/pype/pull/442) +- Bugfix/empty enumerator attributes [\#436](https://github.com/pypeclub/pype/pull/436) +- Fixed wrong order of "other" category collapssing in publisher [\#435](https://github.com/pypeclub/pype/pull/435) +- Multiple reviews where being overwritten to one. [\#424](https://github.com/pypeclub/pype/pull/424) +- Cleanup plugin fail on instances without staging dir [\#420](https://github.com/pypeclub/pype/pull/420) +- deprecated -intra parameter in ffmpeg to new `-g` [\#417](https://github.com/pypeclub/pype/pull/417) +- Delivery action can now work with entered path [\#397](https://github.com/pypeclub/pype/pull/397) + +**Merged pull requests:** + +- Review on instance.data [\#473](https://github.com/pypeclub/pype/pull/473) + +## [2.11.8](https://github.com/pypeclub/pype/tree/2.11.8) (2020-08-27) + +[Full Changelog](https://github.com/pypeclub/pype/compare/2.11.7...2.11.8) + +**Enhancements:** + +- DWAA support for Maya [\#382](https://github.com/pypeclub/pype/issues/382) +- Isolate View on Playblast [\#367](https://github.com/pypeclub/pype/issues/367) +- Maya: Tile rendering [\#297](https://github.com/pypeclub/pype/issues/297) +- single pype instance running [\#47](https://github.com/pypeclub/pype/issues/47) +- PYPE-649: projects don't guarantee backwards compatible environment [\#8](https://github.com/pypeclub/pype/issues/8) +- PYPE-663: separate venv for each deployed version [\#7](https://github.com/pypeclub/pype/issues/7) + +**Fixed bugs:** + +- pyblish pype - other group is collapsed before plugins are done [\#431](https://github.com/pypeclub/pype/issues/431) +- Alpha white edges in harmony on PNGs [\#412](https://github.com/pypeclub/pype/issues/412) +- harmony image loader picks wrong representations [\#404](https://github.com/pypeclub/pype/issues/404) +- Clockify crash when response contain symbol not allowed by UTF-8 [\#81](https://github.com/pypeclub/pype/issues/81) + +## [2.11.7](https://github.com/pypeclub/pype/tree/2.11.7) (2020-08-21) + +[Full Changelog](https://github.com/pypeclub/pype/compare/2.11.6...2.11.7) + +**Fixed bugs:** + +- Clean Up Baked Movie [\#369](https://github.com/pypeclub/pype/issues/369) +- celaction last workfile [\#459](https://github.com/pypeclub/pype/pull/459) + +## [2.11.6](https://github.com/pypeclub/pype/tree/2.11.6) (2020-08-18) + +[Full Changelog](https://github.com/pypeclub/pype/compare/2.11.5...2.11.6) + +**Enhancements:** + +- publisher app [\#56](https://github.com/pypeclub/pype/issues/56) + +## [2.11.5](https://github.com/pypeclub/pype/tree/2.11.5) (2020-08-13) + +[Full Changelog](https://github.com/pypeclub/pype/compare/2.11.4...2.11.5) + +**Enhancements:** + +- Switch from master to equivalent [\#220](https://github.com/pypeclub/pype/issues/220) +- Standalone publisher now only groups sequence if the extension is known [\#439](https://github.com/pypeclub/pype/pull/439) + +**Fixed bugs:** + +- Logs have been disable for editorial by default to speed up publishing [\#433](https://github.com/pypeclub/pype/pull/433) +- additional fixes for celaction [\#430](https://github.com/pypeclub/pype/pull/430) +- Harmony: invalid variable scope in validate scene settings [\#428](https://github.com/pypeclub/pype/pull/428) +- new representation name for audio was not accepted [\#427](https://github.com/pypeclub/pype/pull/427) + +## [2.11.4](https://github.com/pypeclub/pype/tree/2.11.4) (2020-08-10) + +[Full Changelog](https://github.com/pypeclub/pype/compare/2.11.3...2.11.4) + +**Enhancements:** + +- WebSocket server [\#135](https://github.com/pypeclub/pype/issues/135) +- standalonepublisher: editorial family features expansion \[master branch\] [\#411](https://github.com/pypeclub/pype/pull/411) + +## [2.11.3](https://github.com/pypeclub/pype/tree/2.11.3) (2020-08-04) + +[Full Changelog](https://github.com/pypeclub/pype/compare/2.11.2...2.11.3) + +**Fixed bugs:** + +- Harmony: publishing performance issues [\#408](https://github.com/pypeclub/pype/pull/408) + +## [2.11.2](https://github.com/pypeclub/pype/tree/2.11.2) (2020-07-31) + +[Full Changelog](https://github.com/pypeclub/pype/compare/2.11.1...2.11.2) + +**Fixed bugs:** + +- Ftrack to Avalon bug [\#406](https://github.com/pypeclub/pype/issues/406) + +## [2.11.1](https://github.com/pypeclub/pype/tree/2.11.1) (2020-07-29) + +[Full Changelog](https://github.com/pypeclub/pype/compare/2.11.0...2.11.1) + +**Merged pull requests:** + +- Celaction: metadata json folder fixes on path [\#393](https://github.com/pypeclub/pype/pull/393) +- CelAction - version up method taken fro pype.lib [\#391](https://github.com/pypeclub/pype/pull/391) + + +## 2.11.0 ## + +_**release date:** 27 July 2020_ + +**new:** +- _(blender)_ namespace support [\#341](https://github.com/pypeclub/pype/pull/341) +- _(blender)_ start end frames [\#330](https://github.com/pypeclub/pype/pull/330) +- _(blender)_ camera asset [\#322](https://github.com/pypeclub/pype/pull/322) +- _(pype)_ toggle instances per family in pyblish GUI [\#320](https://github.com/pypeclub/pype/pull/320) +- _(pype)_ current release version is now shown in the tray menu [#379](https://github.com/pypeclub/pype/pull/379) + + +**improved:** +- _(resolve)_ tagging for publish [\#239](https://github.com/pypeclub/pype/issues/239) +- _(pype)_ Support publishing a subset of shots with standalone editorial [\#336](https://github.com/pypeclub/pype/pull/336) +- _(harmony)_ Basic support for palettes [\#324](https://github.com/pypeclub/pype/pull/324) +- _(photoshop)_ Flag outdated containers on startup and publish. [\#309](https://github.com/pypeclub/pype/pull/309) +- _(harmony)_ Flag Outdated containers [\#302](https://github.com/pypeclub/pype/pull/302) +- _(photoshop)_ Publish review [\#298](https://github.com/pypeclub/pype/pull/298) +- _(pype)_ Optional Last workfile launch [\#365](https://github.com/pypeclub/pype/pull/365) + + +**fixed:** +- _(premiere)_ workflow fixes [\#346](https://github.com/pypeclub/pype/pull/346) +- _(pype)_ pype-setup does not work with space in path [\#327](https://github.com/pypeclub/pype/issues/327) +- _(ftrack)_ Ftrack delete action cause circular error [\#206](https://github.com/pypeclub/pype/issues/206) +- _(nuke)_ Priority was forced to 50 [\#345](https://github.com/pypeclub/pype/pull/345) +- _(nuke)_ Fix ValidateNukeWriteKnobs [\#340](https://github.com/pypeclub/pype/pull/340) +- _(maya)_ If camera attributes are connected, we can ignore them. [\#339](https://github.com/pypeclub/pype/pull/339) +- _(pype)_ stop appending of tools environment to existing env [\#337](https://github.com/pypeclub/pype/pull/337) +- _(ftrack)_ Ftrack timeout needs to look at AVALON\_TIMEOUT [\#325](https://github.com/pypeclub/pype/pull/325) +- _(harmony)_ Only zip files are supported. [\#310](https://github.com/pypeclub/pype/pull/310) +- _(pype)_ hotfix/Fix event server mongo uri [\#305](https://github.com/pypeclub/pype/pull/305) +- _(photoshop)_ Subset was not named or validated correctly. [\#304](https://github.com/pypeclub/pype/pull/304) + + + + +## 2.10.0 ## + +_**release date:** 17 June 2020_ + +**new:** +- _(harmony)_ **Toon Boom Harmony** has been greatly extended to support rigging, scene build, animation and rendering workflows. [#270](https://github.com/pypeclub/pype/issues/270) [#271](https://github.com/pypeclub/pype/issues/271) [#190](https://github.com/pypeclub/pype/issues/190) [#191](https://github.com/pypeclub/pype/issues/191) [#172](https://github.com/pypeclub/pype/issues/172) [#168](https://github.com/pypeclub/pype/issues/168) +- _(pype)_ Added support for rudimentary **edl publishing** into individual shots. [#265](https://github.com/pypeclub/pype/issues/265) +- _(celaction)_ Simple **Celaction** integration has been added with support for workfiles and rendering. [#255](https://github.com/pypeclub/pype/issues/255) +- _(maya)_ Support for multiple job types when submitting to the farm. We can now render Maya or Standalone render jobs for Vray and Arnold (limited support for arnold) [#204](https://github.com/pypeclub/pype/issues/204) +- _(photoshop)_ Added initial support for Photoshop [#232](https://github.com/pypeclub/pype/issues/232) + +**improved:** +- _(blender)_ Updated support for rigs and added support Layout family [#233](https://github.com/pypeclub/pype/issues/233) [#226](https://github.com/pypeclub/pype/issues/226) +- _(premiere)_ It is now possible to choose different storage root for workfiles of different task types. [#255](https://github.com/pypeclub/pype/issues/255) +- _(maya)_ Support for unmerged AOVs in Redshift multipart EXRs [#197](https://github.com/pypeclub/pype/issues/197) +- _(pype)_ Pype repository has been refactored in preparation for 3.0 release [#169](https://github.com/pypeclub/pype/issues/169) +- _(deadline)_ All file dependencies are now passed to deadline from maya to prevent premature start of rendering if caches or textures haven't been coppied over yet. [#195](https://github.com/pypeclub/pype/issues/195) +- _(nuke)_ Script validation can now be made optional. [#194](https://github.com/pypeclub/pype/issues/194) +- _(pype)_ Publishing can now be stopped at any time. [#194](https://github.com/pypeclub/pype/issues/194) + +**fix:** +- _(pype)_ Pyblish-lite has been integrated into pype repository, plus various publishing GUI fixes. [#274](https://github.com/pypeclub/pype/issues/274) [#275](https://github.com/pypeclub/pype/issues/275) [#268](https://github.com/pypeclub/pype/issues/268) [#227](https://github.com/pypeclub/pype/issues/227) [#238](https://github.com/pypeclub/pype/issues/238) +- _(maya)_ Alembic extractor was getting wrong frame range type in certain scenarios [#254](https://github.com/pypeclub/pype/issues/254) +- _(maya)_ Attaching a render to subset in maya was not passing validation in certain scenarios [#256](https://github.com/pypeclub/pype/issues/256) +- _(ftrack)_ Various small fixes to ftrack sync [#263](https://github.com/pypeclub/pype/issues/263) [#259](https://github.com/pypeclub/pype/issues/259) +- _(maya)_ Look extraction is now able to skp invalid connections in shaders [#207](https://github.com/pypeclub/pype/issues/207) + + + + +## 2.9.0 ## + +_**release date:** 25 May 2020_ + +**new:** +- _(pype)_ Support for **Multiroot projects**. You can now store project data on multiple physical or virtual storages and target individual publishes to these locations. For instance render can be stored on a faster storage than the rest of the project. [#145](https://github.com/pypeclub/pype/issues/145), [#38](https://github.com/pypeclub/pype/issues/38) +- _(harmony)_ Basic implementation of **Toon Boom Harmony** has been added. [#142](https://github.com/pypeclub/pype/issues/142) +- _(pype)_ OSX support is in public beta now. There are issues to be expected, but the main implementation should be functional. [#141](https://github.com/pypeclub/pype/issues/141) + + +**improved:** + +- _(pype)_ **Review extractor** has been completely rebuilt. It now supports granular filtering so you can create **multiple outputs** for different tasks, families or hosts. [#103](https://github.com/pypeclub/pype/issues/103), [#166](https://github.com/pypeclub/pype/issues/166), [#165](https://github.com/pypeclub/pype/issues/165) +- _(pype)_ **Burnin** generation had been extended to **support same multi-output filtering** as review extractor [#103](https://github.com/pypeclub/pype/issues/103) +- _(pype)_ Publishing file templates can now be specified in config for each individual family [#114](https://github.com/pypeclub/pype/issues/114) +- _(pype)_ Studio specific plugins can now be appended to pype standard publishing plugins. [#112](https://github.com/pypeclub/pype/issues/112) +- _(nukestudio)_ Reviewable clips no longer need to be previously cut, exported and re-imported to timeline. **Pype can now dynamically cut reviewable quicktimes** from continuous offline footage during publishing. [#23](https://github.com/pypeclub/pype/issues/23) +- _(deadline)_ Deadline can now correctly differentiate between staging and production pype. [#154](https://github.com/pypeclub/pype/issues/154) +- _(deadline)_ `PYPE_PYTHON_EXE` env variable can now be used to direct publishing to explicit python installation. [#120](https://github.com/pypeclub/pype/issues/120) +- _(nuke)_ Nuke now check for new version of loaded data on file open. [#140](https://github.com/pypeclub/pype/issues/140) +- _(nuke)_ frame range and limit checkboxes are now exposed on write node. [#119](https://github.com/pypeclub/pype/issues/119) + + + +**fix:** + +- _(nukestudio)_ Project Location was using backslashes which was breaking nukestudio native exporting in certains configurations [#82](https://github.com/pypeclub/pype/issues/82) +- _(nukestudio)_ Duplicity in hierarchy tags was prone to throwing publishing error [#130](https://github.com/pypeclub/pype/issues/130), [#144](https://github.com/pypeclub/pype/issues/144) +- _(ftrack)_ multiple stability improvements [#157](https://github.com/pypeclub/pype/issues/157), [#159](https://github.com/pypeclub/pype/issues/159), [#128](https://github.com/pypeclub/pype/issues/128), [#118](https://github.com/pypeclub/pype/issues/118), [#127](https://github.com/pypeclub/pype/issues/127) +- _(deadline)_ multipart EXRs were stopping review publishing on the farm. They are still not supported for automatic review generation, but the publish will go through correctly without the quicktime. [#155](https://github.com/pypeclub/pype/issues/155) +- _(deadline)_ If deadline is non-responsive it will no longer freeze host when publishing [#149](https://github.com/pypeclub/pype/issues/149) +- _(deadline)_ Sometimes deadline was trying to launch render before all the source data was coppied over. [#137](https://github.com/pypeclub/pype/issues/137) _(harmony)_ Basic implementation of **Toon Boom Harmony** has been added. [#142](https://github.com/pypeclub/pype/issues/142) +- _(nuke)_ Filepath knob wasn't updated properly. [#131](https://github.com/pypeclub/pype/issues/131) +- _(maya)_ When extracting animation, the "Write Color Set" options on the instance were not respected. [#108](https://github.com/pypeclub/pype/issues/108) +- _(maya)_ Attribute overrides for AOV only worked for the legacy render layers. Now it works for new render setup as well [#132](https://github.com/pypeclub/pype/issues/132) +- _(maya)_ Stability and usability improvements in yeti workflow [#104](https://github.com/pypeclub/pype/issues/104) + + + + +## 2.8.0 ## + +_**release date:** 20 April 2020_ + +**new:** + +- _(pype)_ Option to generate slates from json templates. [PYPE-628] [#26](https://github.com/pypeclub/pype/issues/26) +- _(pype)_ It is now possible to automate loading of published subsets into any scene. Documentation will follow :). [PYPE-611] [#24](https://github.com/pypeclub/pype/issues/24) + +**fix:** + +- _(maya)_ Some Redshift render tokens could break publishing. [PYPE-778] [#33](https://github.com/pypeclub/pype/issues/33) +- _(maya)_ Publish was not preserving maya file extension. [#39](https://github.com/pypeclub/pype/issues/39) +- _(maya)_ Rig output validator was failing on nodes without shapes. [#40](https://github.com/pypeclub/pype/issues/40) +- _(maya)_ Yeti caches can now be properly versioned up in the scene inventory. [#40](https://github.com/pypeclub/pype/issues/40) +- _(nuke)_ Build first workfiles was not accepting jpeg sequences. [#34](https://github.com/pypeclub/pype/issues/34) +- _(deadline)_ Trying to generate ffmpeg review from multipart EXRs no longer crashes publishing. [PYPE-781] +- _(deadline)_ Render publishing is more stable in multiplatform environments. [PYPE-775] + + + + +## 2.7.0 ## + +_**release date:** 30 March 2020_ + +**new:** + +- _(maya)_ Artist can now choose to load multiple references of the same subset at once [PYPE-646, PYPS-81] +- _(nuke)_ Option to use named OCIO colorspaces for review colour baking. [PYPS-82] +- _(pype)_ Pype can now work with `master` versions for publishing and loading. These are non-versioned publishes that are overwritten with the latest version during publish. These are now supported in all the GUIs, but their publishing is deactivated by default. [PYPE-653] +- _(blender)_ Added support for basic blender workflow. We currently support `rig`, `model` and `animation` families. [PYPE-768] +- _(pype)_ Source timecode can now be used in burn-ins. [PYPE-777] +- _(pype)_ Review outputs profiles can now specify delivery resolution different than project setting [PYPE-759] +- _(nuke)_ Bookmark to current context is now added automatically to all nuke browser windows. [PYPE-712] + +**change:** + +- _(maya)_ It is now possible to publish camera without. baking. Keep in mind that unbaked cameras can't be guaranteed to work in other hosts. [PYPE-595] +- _(maya)_ All the renders from maya are now grouped in the loader by their Layer name. [PYPE-482] +- _(nuke/hiero)_ Any publishes from nuke and hiero can now be versioned independently of the workfile. [PYPE-728] + + +**fix:** + +- _(nuke)_ Mixed slashes caused issues in ocio config path. +- _(pype)_ Intent field in pyblish GUI was passing label instead of value to ftrack. [PYPE-733] +- _(nuke)_ Publishing of pre-renders was inconsistent. [PYPE-766] +- _(maya)_ Handles and frame ranges were inconsistent in various places during publishing. +- _(nuke)_ Nuke was crashing if it ran into certain missing knobs. For example DPX output missing `autocrop` [PYPE-774] +- _(deadline)_ Project overrides were not working properly with farm render publishing. +- _(hiero)_ Problems with single frame plates publishing. +- _(maya)_ Redshift RenderPass token were breaking render publishing. [PYPE-778] +- _(nuke)_ Build first workfile was not accepting jpeg sequences. +- _(maya)_ Multipart (Multilayer) EXRs were breaking review publishing due to FFMPEG incompatiblity [PYPE-781] + + + +## 2.6.0 ## + +_**release date:** 9 March 2020_ + +**change:** +- _(maya)_ render publishing has been simplified and made more robust. Render setup layers are now automatically added to publishing subsets and `render globals` family has been replaced with simple `render` [PYPE-570] +- _(avalon)_ change context and workfiles apps, have been merged into one, that allows both actions to be performed at the same time. [PYPE-747] +- _(pype)_ thumbnails are now automatically propagate to asset from the last published subset in the loader +- _(ftrack)_ publishing comment and intent are now being published to ftrack note as well as describtion. [PYPE-727] +- _(pype)_ when overriding existing version new old representations are now overriden, instead of the new ones just being appended. (to allow this behaviour, the version validator need to be disabled. [PYPE-690]) +- _(pype)_ burnin preset has been significantly simplified. It now doesn't require passing function to each field, but only need the actual text template. to use this, all the current burnin PRESETS MUST BE UPDATED for all the projects. +- _(ftrack)_ credentials are now stored on a per server basis, so it's possible to switch between ftrack servers without having to log in and out. [PYPE-723] + + +**new:** +- _(pype)_ production and development deployments now have different colour of the tray icon. Orange for Dev and Green for production [PYPE-718] +- _(maya)_ renders can now be attached to a publishable subset rather than creating their own subset. For example it is possible to create a reviewable `look` or `model` render and have it correctly attached as a representation of the subsets [PYPE-451] +- _(maya)_ after saving current scene into a new context (as a new shot for instance), all the scene publishing subsets data gets re-generated automatically to match the new context [PYPE-532] +- _(pype)_ we now support project specific publish, load and create plugins [PYPE-740] +- _(ftrack)_ new action that allow archiving/deleting old published versions. User can keep how many of the latest version to keep when the action is ran. [PYPE-748, PYPE-715] +- _(ftrack)_ it is now possible to monitor and restart ftrack event server using ftrack action. [PYPE-658] +- _(pype)_ validator that prevent accidental overwrites of previously published versions. [PYPE-680] +- _(avalon)_ avalon core updated to version 5.6.0 +- _(maya)_ added validator to make sure that relative paths are used when publishing arnold standins. +- _(nukestudio)_ it is now possible to extract and publish audio family from clip in nuke studio [PYPE-682] + +**fix**: +- _(maya)_ maya set framerange button was ignoring handles [PYPE-719] +- _(ftrack)_ sync to avalon was sometime crashing when ran on empty project +- _(nukestudio)_ publishing same shots after they've been previously archived/deleted would result in a crash. [PYPE-737] +- _(nuke)_ slate workflow was breaking in certain scenarios. [PYPE-730] +- _(pype)_ rendering publish workflow has been significantly improved to prevent error resulting from implicit render collection. [PYPE-665, PYPE-746] +- _(pype)_ launching application on a non-synced project resulted in obscure [PYPE-528] +- _(pype)_ missing keys in burnins no longer result in an error. [PYPE-706] +- _(ftrack)_ create folder structure action was sometimes failing for project managers due to wrong permissions. +- _(Nukestudio)_ using `source` in the start frame tag could result in wrong frame range calculation +- _(ftrack)_ sync to avalon action and event have been improved by catching more edge cases and provessing them properly. + + + +## 2.5.0 ## + +_**release date:** 11 Feb 2020_ + +**change:** +- _(pype)_ added many logs for easier debugging +- _(pype)_ review presets can now be separated between 2d and 3d renders [PYPE-693] +- _(pype)_ anatomy module has been greatly improved to allow for more dynamic pulblishing and faster debugging [PYPE-685] +- _(pype)_ avalon schemas have been moved from `pype-config` to `pype` repository, for simplification. [PYPE-670] +- _(ftrack)_ updated to latest ftrack API +- _(ftrack)_ publishing comments now appear in ftrack also as a note on version with customisable category [PYPE-645] +- _(ftrack)_ delete asset/subset action had been improved. It is now able to remove multiple entities and descendants of the selected entities [PYPE-361, PYPS-72] +- _(workfiles)_ added date field to workfiles app [PYPE-603] +- _(maya)_ old deprecated loader have been removed in favour of a single unified reference loader (old scenes will upgrade automatically to the new loader upon opening) [PYPE-633, PYPE-697] +- _(avalon)_ core updated to 5.5.15 [PYPE-671] +- _(nuke)_ library loader is now available in nuke [PYPE-698] + + +**new:** +- _(pype)_ added pype render wrapper to allow rendering on mixed platform farms. [PYPE-634] +- _(pype)_ added `pype launch` command. It let's admin run applications with dynamically built environment based on the given context. [PYPE-634] +- _(pype)_ added support for extracting review sequences with burnins [PYPE-657] +- _(publish)_ users can now set intent next to a comment when publishing. This will then be reflected on an attribute in ftrack. [PYPE-632] +- _(burnin)_ timecode can now be added to burnin +- _(burnin)_ datetime keys can now be added to burnin and anatomy [PYPE-651] +- _(burnin)_ anatomy templates can now be used in burnins. [PYPE=626] +- _(nuke)_ new validator for render resolution +- _(nuke)_ support for attach slate to nuke renders [PYPE-630] +- _(nuke)_ png sequences were added to loaders +- _(maya)_ added maya 2020 compatibility [PYPE-677] +- _(maya)_ ability to publish and load .ASS standin sequences [PYPS-54] +- _(pype)_ thumbnails can now be published and are visible in the loader. `AVALON_THUMBNAIL_ROOT` environment variable needs to be set for this to work [PYPE-573, PYPE-132] +- _(blender)_ base implementation of blender was added with publishing and loading of .blend files [PYPE-612] +- _(ftrack)_ new action for preparing deliveries [PYPE-639] + + +**fix**: +- _(burnin)_ more robust way of finding ffmpeg for burnins. +- _(pype)_ improved UNC paths remapping when sending to farm. +- _(pype)_ float frames sometimes made their way to representation context in database, breaking loaders [PYPE-668] +- _(pype)_ `pype install --force` was failing sometimes [PYPE-600] +- _(pype)_ padding in published files got calculated wrongly sometimes. It is now instead being always read from project anatomy. [PYPE-667] +- _(publish)_ comment publishing was failing in certain situations +- _(ftrack)_ multiple edge case scenario fixes in auto sync and sync-to-avalon action +- _(ftrack)_ sync to avalon now works on empty projects +- _(ftrack)_ thumbnail update event was failing when deleting entities [PYPE-561] +- _(nuke)_ loader applies proper colorspaces from Presets +- _(nuke)_ publishing handles didn't always work correctly [PYPE-686] +- _(maya)_ assembly publishing and loading wasn't working correctly + + + + + +## 2.4.0 ## + +_**release date:** 9 Dec 2019_ + +**change:** +- _(ftrack)_ version to status ftrack event can now be configured from Presets + - based on preset `presets/ftracc/ftrack_config.json["status_version_to_task"]` +- _(ftrack)_ sync to avalon event has been completely re-written. It now supports most of the project management situations on ftrack including moving, renaming and deleting entities, updating attributes and working with tasks. +- _(ftrack)_ sync to avalon action has been also re-writen. It is now much faster (up to 100 times depending on a project structure), has much better logging and reporting on encountered problems, and is able to handle much more complex situations. +- _(ftrack)_ sync to avalon trigger by checking `auto-sync` toggle on ftrack [PYPE-504] +- _(pype)_ various new features in the REST api +- _(pype)_ new visual identity used across pype +- _(pype)_ started moving all requirements to pip installation rather than vendorising them in pype repository. Due to a few yet unreleased packages, this means that pype can temporarily be only installed in the offline mode. + +**new:** +- _(nuke)_ support for publishing gizmos and loading them as viewer processes +- _(nuke)_ support for publishing nuke nodes from backdrops and loading them back +- _(pype)_ burnins can now work with start and end frames as keys + - use keys `{frame_start}`, `{frame_end}` and `{current_frame}` in burnin preset to use them. [PYPS-44,PYPS-73, PYPE-602] +- _(pype)_ option to filter logs by user and level in loggin GUI +- _(pype)_ image family added to standalone publisher [PYPE-574] +- _(pype)_ matchmove family added to standalone publisher [PYPE-574] +- _(nuke)_ validator for comparing arbitrary knobs with values from presets +- _(maya)_ option to force maya to copy textures in the new look publish rather than hardlinking them +- _(pype)_ comments from pyblish GUI are now being added to ftrack version +- _(maya)_ validator for checking outdated containers in the scene +- _(maya)_ option to publish and load arnold standin sequence [PYPE-579, PYPS-54] + +**fix**: +- _(pype)_ burnins were not respecting codec of the input video +- _(nuke)_ lot's of various nuke and nuke studio fixes across the board [PYPS-45] +- _(pype)_ workfiles app is not launching with the start of the app by default [PYPE-569] +- _(ftrack)_ ftrack integration during publishing was failing under certain situations [PYPS-66] +- _(pype)_ minor fixes in REST api +- _(ftrack)_ status change event was crashing when the target status was missing [PYPS-68] +- _(ftrack)_ actions will try to reconnect if they fail for some reason +- _(maya)_ problems with fps mapping when using float FPS values +- _(deadline)_ overall improvements to deadline publishing +- _(setup)_ environment variables are now remapped on the fly based on the platform pype is running on. This fixes many issues in mixed platform environments. + + + +## 2.3.6 # + +_**release date:** 27 Nov 2019_ + +**hotfix**: +- _(ftrack)_ was hiding important debug logo +- _(nuke)_ crashes during workfile publishing +- _(ftrack)_ event server crashes because of signal problems +- _(muster)_ problems with muster render submissions +- _(ftrack)_ thumbnail update event syntax errors + + +## 2.3.0 ## +_release date: 6 Oct 2019_ + +**new**: +- _(maya)_ support for yeti rigs and yeti caches +- _(maya)_ validator for comparing arbitrary attributes against ftrack +- _(pype)_ burnins can now show current date and time +- _(muster)_ pools can now be set in render globals in maya +- _(pype)_ Rest API has been implemented in beta stage +- _(nuke)_ LUT loader has been added +- _(pype)_ rudimentary user module has been added as preparation for user management +- _(pype)_ a simple logging GUI has been added to pype tray +- _(nuke)_ nuke can now bake input process into mov +- _(maya)_ imported models now have selection handle displayed by defaulting +- _(avalon)_ it's is now possible to load multiple assets at once using loader +- _(maya)_ added ability to automatically connect yeti rig to a mesh upon loading + +**changed**: +- _(ftrack)_ event server now runs two parallel processes and is able to keep queue of events to process. +- _(nuke)_ task name is now added to all rendered subsets +- _(pype)_ adding more families to standalone publisher +- _(pype)_ standalone publisher now uses pyblish-lite +- _(pype)_ standalone publisher can now create review quicktimes +- _(ftrack)_ queries to ftrack were sped up +- _(ftrack)_ multiple ftrack action have been deprecated +- _(avalon)_ avalon upstream has been updated to 5.5.0 +- _(nukestudio)_ published transforms can now be animated +- + +**fix**: +- _(maya)_ fps popup button didn't work in some cases +- _(maya)_ geometry instances and references in maya were losing shader assignments +- _(muster)_ muster rendering templates were not working correctly +- _(maya)_ arnold tx texture conversion wasn't respecting colorspace set by the artist +- _(pype)_ problems with avalon db sync +- _(maya)_ ftrack was rounding FPS making it inconsistent +- _(pype)_ wrong icon names in Creator +- _(maya)_ scene inventory wasn't showing anything if representation was removed from database after it's been loaded to the scene +- _(nukestudio)_ multiple bugs squashed +- _(loader)_ loader was taking long time to show all the loading action when first launcher in maya + +## 2.2.0 ## +_release date: 8 Sept 2019_ + +**new**: +- _(pype)_ add customisable workflow for creating quicktimes from renders or playblasts +- _(nuke)_ option to choose deadline chunk size on write nodes +- _(nukestudio)_ added option to publish soft effects (subTrackItems) from NukeStudio as subsets including LUT files. these can then be loaded in nuke or NukeStudio +- _(nuke)_ option to build nuke script from previously published latest versions of plate and render subsets. +- _(nuke)_ nuke writes now have deadline tab. +- _(ftrack)_ Prepare Project action can now be used for creating the base folder structure on disk and in ftrack, setting up all the initial project attributes and it automatically prepares `pype_project_config` folder for the given project. +- _(clockify)_ Added support for time tracking in clockify. This currently in addition to ftrack time logs, but does not completely replace them. +- _(pype)_ any attributes in Creator and Loader plugins can now be customised using pype preset system + +**changed**: +- nukestudio now uses workio API for workfiles +- _(maya)_ "FIX FPS" prompt in maya now appears in the middle of the screen +- _(muster)_ can now be configured with custom templates +- _(pype)_ global publishing plugins can now be configured using presets as well as host specific ones + + +**fix**: +- wrong version retrieval from path in certain scenarios +- nuke reset resolution wasn't working in certain scenarios + +## 2.1.0 ## +_release date: 6 Aug 2019_ + +A large cleanup release. Most of the change are under the hood. + +**new**: +- _(pype)_ add customisable workflow for creating quicktimes from renders or playblasts +- _(pype)_ Added configurable option to add burnins to any generated quicktimes +- _(ftrack)_ Action that identifies what machines pype is running on. +- _(system)_ unify subprocess calls +- _(maya)_ add audio to review quicktimes +- _(nuke)_ add crop before write node to prevent overscan problems in ffmpeg +- **Nuke Studio** publishing and workfiles support +- **Muster** render manager support +- _(nuke)_ Framerange, FPS and Resolution are set automatically at startup +- _(maya)_ Ability to load published sequences as image planes +- _(system)_ Ftrack event that sets asset folder permissions based on task assignees in ftrack. +- _(maya)_ Pyblish plugin that allow validation of maya attributes +- _(system)_ added better startup logging to tray debug, including basic connection information +- _(avalon)_ option to group published subsets to groups in the loader +- _(avalon)_ loader family filters are working now + +**changed**: +- change multiple key attributes to unify their behaviour across the pipeline + - `frameRate` to `fps` + - `startFrame` to `frameStart` + - `endFrame` to `frameEnd` + - `fstart` to `frameStart` + - `fend` to `frameEnd` + - `handle_start` to `handleStart` + - `handle_end` to `handleEnd` + - `resolution_width` to `resolutionWidth` + - `resolution_height` to `resolutionHeight` + - `pixel_aspect` to `pixelAspect` + +- _(nuke)_ write nodes are now created inside group with only some attributes editable by the artist +- rendered frames are now deleted from temporary location after their publishing is finished. +- _(ftrack)_ RV action can now be launched from any entity +- after publishing only refresh button is now available in pyblish UI +- added context instance pyblish-lite so that artist knows if context plugin fails +- _(avalon)_ allow opening selected files using enter key +- _(avalon)_ core updated to v5.2.9 with our forked changes on top + +**fix**: +- faster hierarchy retrieval from db +- _(nuke)_ A lot of stability enhancements +- _(nuke studio)_ A lot of stability enhancements +- _(nuke)_ now only renders a single write node on farm +- _(ftrack)_ pype would crash when launcher project level task +- work directory was sometimes not being created correctly +- major pype.lib cleanup. Removing of unused functions, merging those that were doing the same and general house cleaning. +- _(avalon)_ subsets in maya 2019 weren't behaving correctly in the outliner + + +\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)* diff --git a/HISTORY.md b/HISTORY.md index 4b5463c924..d60bd7b0c7 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,6 +1,3 @@ -# Pype changelog # -Welcome to pype changelog - ## 2.11.0 ## From 431a52689ebdd91e7c1047a815da3a1c277b5f1a Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Sep 2020 10:25:34 +0200 Subject: [PATCH 514/813] discard changes fix --- .../config_setting/config_setting/widgets/inputs.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 4a71f0380c..afdcc207cf 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -306,14 +306,12 @@ class InputObject(ConfigObject): def discard_changes(self): self._is_overriden = self._was_overriden - if ( - self.is_overidable - and self._was_overriden - and self.override_value is not NOT_SET - ): - self.set_value(self.override_value) + self._has_studio_override = self._had_studio_override + if self.is_overidable: + if self._was_overriden and self.override_value is not NOT_SET: + self.set_value(self.override_value) else: - if self.has_studio_override: + if self._had_studio_override: self.set_value(self.studio_value) else: self.set_value(self.default_value) From 729b474f20332f3d5ebc7132b9b0675ade7c1662 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Sep 2020 10:39:15 +0200 Subject: [PATCH 515/813] restart states on value updates --- pype/tools/config_setting/config_setting/widgets/inputs.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index afdcc207cf..c9c40f251c 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -201,6 +201,7 @@ class ConfigObject(AbstractConfigObject): class InputObject(ConfigObject): def update_default_values(self, parent_values): + self._state = None value = NOT_SET if self._as_widget: value = parent_values @@ -218,6 +219,7 @@ class InputObject(ConfigObject): self.set_value(value) def update_studio_values(self, parent_values): + self._state = None value = NOT_SET if self._as_widget: value = parent_values From 50aea9f15061129cb0fcdeb3cb6d0db252b11229 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Sep 2020 10:40:01 +0200 Subject: [PATCH 516/813] apply_overrides works for widget inputs --- pype/tools/config_setting/config_setting/widgets/inputs.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index c9c40f251c..945ad0f255 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -285,7 +285,9 @@ class InputObject(ConfigObject): self._is_modified = False self._state = None - if parent_values is NOT_SET or self.key not in parent_values: + if self._as_widget: + override_value = parent_values + elif parent_values is NOT_SET or self.key not in parent_values: override_value = NOT_SET else: override_value = parent_values[self.key] From 6ed724d040bf85fa0dadbcc64699caa65fc124ec Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Sep 2020 10:56:08 +0200 Subject: [PATCH 517/813] state for disabled widgets is different --- .../config_setting/widgets/inputs.py | 107 +++++++++++------- 1 file changed, 65 insertions(+), 42 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 945ad0f255..eea33d3ef6 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -412,12 +412,15 @@ class BooleanWidget(QtWidgets.QWidget, InputObject): self.checkbox.setChecked(value) def update_style(self): - state = self.style_state( - self.has_studio_override, - self.is_invalid, - self.is_overriden, - self.is_modified - ) + if self._as_widget and not self.isEnabled(): + state = self.style_state(False, False, False, False) + else: + state = self.style_state( + self.has_studio_override, + self.is_invalid, + self.is_overriden, + self.is_modified + ) if self._state == state: return @@ -486,12 +489,15 @@ class NumberWidget(QtWidgets.QWidget, InputObject): self.input_field.setValue(value) def update_style(self): - state = self.style_state( - self.has_studio_override, - self.is_invalid, - self.is_overriden, - self.is_modified - ) + if self._as_widget and not self.isEnabled(): + state = self.style_state(False, False, False, False) + else: + state = self.style_state( + self.has_studio_override, + self.is_invalid, + self.is_overriden, + self.is_modified + ) if self._state == state: return @@ -563,12 +569,15 @@ class TextWidget(QtWidgets.QWidget, InputObject): self.text_input.setText(value) def update_style(self): - state = self.style_state( - self.has_studio_override, - self.is_invalid, - self.is_overriden, - self.is_modified - ) + if self._as_widget and not self.isEnabled(): + state = self.style_state(False, False, False, False) + else: + state = self.style_state( + self.has_studio_override, + self.is_invalid, + self.is_overriden, + self.is_modified + ) if self._state == state: return @@ -637,12 +646,16 @@ class PathInputWidget(QtWidgets.QWidget, InputObject): super(PathInput, self).focusOutEvent(event) def update_style(self): - state = self.style_state( - self.has_studio_override, - self.is_invalid, - self.is_overriden, - self.is_modified - ) + if self._as_widget and not self.isEnabled(): + state = self.style_state(False, False, False, False) + else: + state = self.style_state( + self.has_studio_override, + self.is_invalid, + self.is_overriden, + self.is_modified + ) + if self._state == state: return @@ -776,12 +789,16 @@ class RawJsonWidget(QtWidgets.QWidget, InputObject): return super(RawJsonWidget, self)._on_value_change(*args, **kwargs) def update_style(self): - state = self.style_state( - self.has_studio_override, - self.is_invalid, - self.is_overriden, - self.is_modified - ) + if self._as_widget and not self.isEnabled(): + state = self.style_state(False, False, False, False) + else: + state = self.style_state( + self.has_studio_override, + self.is_invalid, + self.is_overriden, + self.is_modified + ) + if self._state == state: return @@ -1036,12 +1053,15 @@ class ListWidget(QtWidgets.QWidget, InputObject): self.update_style() def update_style(self): - state = self.style_state( - self.has_studio_override, - self.is_invalid, - self.is_overriden, - self.is_modified - ) + if self._as_widget and not self.isEnabled(): + state = self.style_state(False, False, False, False) + else: + state = self.style_state( + self.has_studio_override, + self.is_invalid, + self.is_overriden, + self.is_modified + ) if self._state == state: return @@ -1328,12 +1348,15 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): self.update_style() def update_style(self): - state = self.style_state( - self.has_studio_override, - self.is_invalid, - self.is_overriden, - self.is_modified - ) + if self._as_widget and not self.isEnabled(): + state = self.style_state(False, False, False, False) + else: + state = self.style_state( + self.has_studio_override, + self.is_invalid, + self.is_overriden, + self.is_modified + ) if self._state == state: return From c55d2ee91f589522662e21b8e34c66efa91f5d20 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Sep 2020 10:56:38 +0200 Subject: [PATCH 518/813] list items has apply_overrides and update_studio_values --- .../config_setting/config_setting/widgets/inputs.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index eea33d3ef6..ae55679d73 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -902,6 +902,12 @@ class ListItem(QtWidgets.QWidget, ConfigObject): def mouseReleaseEvent(self, event): return QtWidgets.QWidget.mouseReleaseEvent(self, event) + def update_studio_values(self, value): + self.value_input.update_studio_values(value) + + def apply_overrides(self, value): + self.value_input.apply_overrides(value) + class ListWidget(QtWidgets.QWidget, InputObject): value_changed = QtCore.Signal(object) @@ -1002,7 +1008,11 @@ class ListWidget(QtWidgets.QWidget, InputObject): # Set text if entered text is not None # else (when add button clicked) trigger `_on_value_change` if value is not None: - item_widget.value_input.update_studio_values(value) + if self._is_overriden: + item_widget.apply_overrides(value) + else: + item_widget.update_studio_values(value) + self.hierarchical_style_update() else: self._on_value_change() self.updateGeometry() From 1164d0b9880716c231d05df679be21e7966d15e9 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Sep 2020 11:33:47 +0200 Subject: [PATCH 519/813] moved setting attributes much earlier --- pype/tools/config_setting/config_setting/widgets/inputs.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index ae55679d73..7096f70ecd 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -228,14 +228,15 @@ class InputObject(ConfigObject): self.studio_value = value if value is not NOT_SET: - self.set_value(value) self._has_studio_override = True + self._had_studio_override = True + self.set_value(value) else: - self.set_value(self.default_value) self._has_studio_override = False + self._had_studio_override = False + self.set_value(self.default_value) - self._had_studio_override = bool(self._has_studio_override) self._is_modified = False def _on_value_change(self, item=None): From c210b317042ae2dbab6e12b3fb027dde6c363330 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Sep 2020 11:34:05 +0200 Subject: [PATCH 520/813] is_modified cares if is_overridable or not --- .../config_setting/config_setting/widgets/inputs.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 7096f70ecd..8aebb6dd5f 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -56,11 +56,13 @@ class ConfigObject(AbstractConfigObject): @property def is_modified(self): """Has object any changes that require saving.""" - return ( - self._is_modified - or (self.was_overriden != self.is_overriden) - or (self.has_studio_override != self.had_studio_override) - ) + if self._is_modified: + return True + + if self.is_overidable: + return self.was_overriden != self.is_overriden + else: + return self.has_studio_override != self.had_studio_override @property def is_overriden(self): From adc086ba0f14ffc8f41c9404f70349e59874dfe2 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Sep 2020 11:34:29 +0200 Subject: [PATCH 521/813] method apply_overrides moved --- .../config_setting/widgets/inputs.py | 52 +++++++++---------- 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 8aebb6dd5f..a7e09abb6d 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -239,7 +239,32 @@ class InputObject(ConfigObject): self._had_studio_override = False self.set_value(self.default_value) + def apply_overrides(self, parent_values): self._is_modified = False + self._state = None + self._had_studio_override = bool(self._has_studio_override) + if self._as_widget: + override_value = parent_values + elif parent_values is NOT_SET or self.key not in parent_values: + override_value = NOT_SET + else: + override_value = parent_values[self.key] + + self.override_value = override_value + + if override_value is NOT_SET: + self._is_overriden = False + self._was_overriden = False + if self.has_studio_override: + value = self.studio_value + else: + value = self.default_value + else: + self._is_overriden = True + self._was_overriden = True + value = override_value + + self.set_value(value) def _on_value_change(self, item=None): if self.ignore_value_changes: @@ -284,33 +309,6 @@ class InputObject(ConfigObject): self._is_overriden = False self._is_modified = False - def apply_overrides(self, parent_values): - self._is_modified = False - self._state = None - - if self._as_widget: - override_value = parent_values - elif parent_values is NOT_SET or self.key not in parent_values: - override_value = NOT_SET - else: - override_value = parent_values[self.key] - - self.override_value = override_value - - if override_value is NOT_SET: - self._is_overriden = False - self._was_overriden = False - if self.has_studio_override: - value = self.studio_value - else: - value = self.default_value - else: - self._is_overriden = True - self._was_overriden = True - value = override_value - - self.set_value(value) - def discard_changes(self): self._is_overriden = self._was_overriden self._has_studio_override = self._had_studio_override From 2e9ef743536ed41be674e8cb2aaa7e4499d15947 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Sep 2020 11:36:43 +0200 Subject: [PATCH 522/813] as widget input does not care about overrides --- .../config_setting/widgets/inputs.py | 85 ++++++++++++++++--- 1 file changed, 71 insertions(+), 14 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index a7e09abb6d..81218855c9 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -413,8 +413,16 @@ class BooleanWidget(QtWidgets.QWidget, InputObject): self.checkbox.setChecked(value) def update_style(self): - if self._as_widget and not self.isEnabled(): - state = self.style_state(False, False, False, False) + if self._as_widget: + if not self.isEnabled(): + state = self.style_state(False, False, False, False) + else: + state = self.style_state( + False, + self._is_invalid, + False, + self._is_modified + ) else: state = self.style_state( self.has_studio_override, @@ -490,8 +498,16 @@ class NumberWidget(QtWidgets.QWidget, InputObject): self.input_field.setValue(value) def update_style(self): - if self._as_widget and not self.isEnabled(): - state = self.style_state(False, False, False, False) + if self._as_widget: + if not self.isEnabled(): + state = self.style_state(False, False, False, False) + else: + state = self.style_state( + False, + self._is_invalid, + False, + self._is_modified + ) else: state = self.style_state( self.has_studio_override, @@ -570,8 +586,16 @@ class TextWidget(QtWidgets.QWidget, InputObject): self.text_input.setText(value) def update_style(self): - if self._as_widget and not self.isEnabled(): - state = self.style_state(False, False, False, False) + if self._as_widget: + if not self.isEnabled(): + state = self.style_state(False, False, False, False) + else: + state = self.style_state( + False, + self._is_invalid, + False, + self._is_modified + ) else: state = self.style_state( self.has_studio_override, @@ -579,6 +603,7 @@ class TextWidget(QtWidgets.QWidget, InputObject): self.is_overriden, self.is_modified ) + if self._state == state: return @@ -647,8 +672,16 @@ class PathInputWidget(QtWidgets.QWidget, InputObject): super(PathInput, self).focusOutEvent(event) def update_style(self): - if self._as_widget and not self.isEnabled(): - state = self.style_state(False, False, False, False) + if self._as_widget: + if not self.isEnabled(): + state = self.style_state(False, False, False, False) + else: + state = self.style_state( + False, + self._is_invalid, + False, + self._is_modified + ) else: state = self.style_state( self.has_studio_override, @@ -790,8 +823,16 @@ class RawJsonWidget(QtWidgets.QWidget, InputObject): return super(RawJsonWidget, self)._on_value_change(*args, **kwargs) def update_style(self): - if self._as_widget and not self.isEnabled(): - state = self.style_state(False, False, False, False) + if self._as_widget: + if not self.isEnabled(): + state = self.style_state(False, False, False, False) + else: + state = self.style_state( + False, + self._is_invalid, + False, + self._is_modified + ) else: state = self.style_state( self.has_studio_override, @@ -1064,8 +1105,16 @@ class ListWidget(QtWidgets.QWidget, InputObject): self.update_style() def update_style(self): - if self._as_widget and not self.isEnabled(): - state = self.style_state(False, False, False, False) + if self._as_widget: + if not self.isEnabled(): + state = self.style_state(False, False, False, False) + else: + state = self.style_state( + False, + self._is_invalid, + False, + self._is_modified + ) else: state = self.style_state( self.has_studio_override, @@ -1359,8 +1408,16 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): self.update_style() def update_style(self): - if self._as_widget and not self.isEnabled(): - state = self.style_state(False, False, False, False) + if self._as_widget: + if not self.isEnabled(): + state = self.style_state(False, False, False, False) + else: + state = self.style_state( + False, + self._is_invalid, + False, + self._is_modified + ) else: state = self.style_state( self.has_studio_override, From 78dc9b1c3bedcc049d1904c0ca2b91ab5dbbfc08 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Sep 2020 11:37:34 +0200 Subject: [PATCH 523/813] list has updating default values --- .../config_setting/config_setting/widgets/inputs.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 81218855c9..3f59f751e5 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -204,6 +204,8 @@ class ConfigObject(AbstractConfigObject): class InputObject(ConfigObject): def update_default_values(self, parent_values): self._state = None + self._is_modified = False + value = NOT_SET if self._as_widget: value = parent_values @@ -222,6 +224,8 @@ class InputObject(ConfigObject): def update_studio_values(self, parent_values): self._state = None + self._is_modified = False + value = NOT_SET if self._as_widget: value = parent_values @@ -944,6 +948,9 @@ class ListItem(QtWidgets.QWidget, ConfigObject): def mouseReleaseEvent(self, event): return QtWidgets.QWidget.mouseReleaseEvent(self, event) + def update_default_values(self, value): + self.value_input.update_default_values(value) + def update_studio_values(self, value): self.value_input.update_studio_values(value) @@ -1050,7 +1057,9 @@ class ListWidget(QtWidgets.QWidget, InputObject): # Set text if entered text is not None # else (when add button clicked) trigger `_on_value_change` if value is not None: - if self._is_overriden: + if not self._has_studio_override: + item_widget.update_default_values(value) + elif self._is_overriden: item_widget.apply_overrides(value) else: item_widget.update_studio_values(value) From 442e5ccfd3099524f9c204d200905278e531202d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Sep 2020 11:41:43 +0200 Subject: [PATCH 524/813] fixed any_parent_is_group attribute in anatomy inputs --- .../config_setting/config_setting/widgets/anatomy_inputs.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index 43146f7442..525e1bcd88 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -46,7 +46,7 @@ class AnatomyWidget(QtWidgets.QWidget, ConfigObject): self._child_state = None self._state = None - self.any_parent_is_group = False + self._any_parent_is_group = False self.root_widget = RootsWidget(self) self.templates_widget = TemplatesWidget(self) @@ -190,7 +190,7 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): self._multiroot_state = None self._is_group = True - self.any_parent_is_group = False + self._any_parent_is_group = False self.global_is_multiroot = False self.was_multiroot = NOT_SET @@ -492,7 +492,7 @@ class TemplatesWidget(QtWidgets.QWidget, ConfigObject): self._state = None self._is_group = True - self.any_parent_is_group = False + self._any_parent_is_group = False self.key = "templates" body_widget = ExpandingWidget("Templates", self) From e8459167a8aaa4626e7d5c0e21ae8ad881584524 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Sep 2020 11:41:51 +0200 Subject: [PATCH 525/813] abstracted updating methods --- .../config_setting/widgets/widgets.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/widgets.py b/pype/tools/config_setting/config_setting/widgets/widgets.py index d803624850..d7631e6fea 100644 --- a/pype/tools/config_setting/config_setting/widgets/widgets.py +++ b/pype/tools/config_setting/config_setting/widgets/widgets.py @@ -234,6 +234,21 @@ class AbstractConfigObject: ) return super(AbstractConfigObject, self).__getattribute__(name) + def update_default_values(self, parent_values): + raise NotImplementedError( + "{} does not have implemented `update_default_values`".format(self) + ) + + def update_studio_values(self, parent_values): + raise NotImplementedError( + "{} does not have implemented `update_studio_values`".format(self) + ) + + def apply_overrides(self, parent_values): + raise NotImplementedError( + "{} does not have implemented `apply_overrides`".format(self) + ) + @property def is_modified(self): """Has object any changes that require saving.""" From 36fff023017c835927b5b0af256aa9a6b5d465cb Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Sep 2020 12:00:43 +0200 Subject: [PATCH 526/813] PathWIdget has update values methods --- .../config_setting/widgets/inputs.py | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 3f59f751e5..8e5e8c2a5d 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -2187,6 +2187,49 @@ class PathWidget(QtWidgets.QWidget, ConfigObject): self.setFocusProxy(self.input_fields[0]) self.content_layout.addWidget(proxy_widget) + def update_default_values(self, parent_values): + self._state = None + self._child_state = None + self._is_modified = False + + value = NOT_SET + if self._as_widget: + value = parent_values + elif parent_values is not NOT_SET: + value = parent_values.get(self.key, NOT_SET) + + if value is NOT_SET: + raise ValueError( + "Default value is not set. This is implementation BUG." + ) + + self.default_value = value + self._has_studio_override = False + self._had_studio_override = False + self.set_value(value) + + def update_studio_values(self, parent_values): + self._state = None + self._child_state = None + self._is_modified = False + + value = NOT_SET + if self._as_widget: + value = parent_values + elif parent_values is not NOT_SET: + value = parent_values.get(self.key, NOT_SET) + + self.studio_value = value + if value is not NOT_SET: + self._has_studio_override = True + self._had_studio_override = True + self.set_value(value) + + else: + self._has_studio_override = False + self._had_studio_override = False + self.set_value(self.default_value) + def apply_overrides(self, parent_values): self._is_modified = False self._state = None From 42dcad9655e03655e0724a2e36f6eb8ce203ef9c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Sep 2020 12:00:59 +0200 Subject: [PATCH 527/813] fixed update defaults in anatomy widget --- .../config_setting/widgets/anatomy_inputs.py | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index 525e1bcd88..4756ac25a4 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -75,7 +75,15 @@ class AnatomyWidget(QtWidgets.QWidget, ConfigObject): self.root_widget.value_changed.connect(self._on_value_change) - def update_default_values(self, value): + def update_default_values(self, parent_values): + self._state = None + self._child_state = None + + if isinstance(parent_values, dict): + value = parent_values.get(self.key, NOT_SET) + else: + value = NOT_SET + self.root_widget.update_default_values(value) self.templates_widget.update_default_values(value) @@ -280,11 +288,17 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): self.set_multiroot(is_multiroot) if is_multiroot: - self.singleroot_widget.update_studio_values(NOT_SET) - self.multiroot_widget.update_studio_values(value) + for _value in value.values(): + singleroot_value = _value + break + + multiroot_value = value else: - self.singleroot_widget.update_studio_values(value) - self.multiroot_widget.update_studio_values(NOT_SET) + singleroot_value = value + multiroot_value = {"": value} + + self.singleroot_widget.update_default_values(singleroot_value) + self.multiroot_widget.update_default_values(multiroot_value) def update_studio_values(self, parent_values): self._state = None From c31267906fc7fe5ac7427b5012e7a650936120fd Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Sep 2020 12:08:17 +0200 Subject: [PATCH 528/813] added studio overrides to anatomy inputs --- .../config_setting/widgets/anatomy_inputs.py | 42 +++++++++++++++++-- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index 4756ac25a4..141feee61d 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -123,10 +123,14 @@ class AnatomyWidget(QtWidgets.QWidget, ConfigObject): self.value_changed.emit(self) def update_style(self, is_overriden=None): + child_has_studio_override = self.child_has_studio_override child_modified = self.child_modified child_invalid = self.child_invalid child_state = self.style_state( - child_invalid, self.child_overriden, child_modified + child_has_studio_override, + child_invalid, + self.child_overriden, + child_modified ) if child_state: child_state = "child-{}".format(child_state) @@ -143,6 +147,13 @@ class AnatomyWidget(QtWidgets.QWidget, ConfigObject): self.templates_widget.hierarchical_style_update() self.update_style() + @property + def child_has_studio_override(self): + return ( + self.root_widget.child_has_studio_override + or self.templates_widget.child_has_studio_override + ) + @property def child_modified(self): return ( @@ -320,6 +331,13 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): self.was_multiroot = is_multiroot self.set_multiroot(is_multiroot) + if value is NOT_SET: + self._has_studio_override = False + self._had_studio_override = False + else: + self._has_studio_override = True + self._had_studio_override = True + if is_multiroot: self.singleroot_widget.update_studio_values(NOT_SET) self.multiroot_widget.update_studio_values(value) @@ -368,6 +386,7 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): def update_style(self): multiroot_state = self.style_state( + self.has_studio_override, False, self.is_overriden, self.was_multiroot != self.is_multiroot @@ -378,7 +397,10 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): self._multiroot_state = multiroot_state state = self.style_state( - self.is_invalid, self.is_overriden, self.is_modified + self.has_studio_override, + self.is_invalid, + self.is_overriden, + self.is_modified ) if self._state == state: return @@ -433,6 +455,13 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): self._on_value_change() + @property + def child_has_studio_override(self): + if self.is_multiroot: + return self.multiroot_widget.child_has_studio_override + else: + return self.singleroot_widget.child_has_studio_override + @property def child_modified(self): if self.is_multiroot: @@ -549,7 +578,10 @@ class TemplatesWidget(QtWidgets.QWidget, ConfigObject): def update_style(self): state = self.style_state( - self.child_invalid, self.child_overriden, self.child_modified + self.has_studio_override, + self.child_invalid, + self.child_overriden, + self.child_modified ) if self._state == state: return @@ -577,6 +609,10 @@ class TemplatesWidget(QtWidgets.QWidget, ConfigObject): def is_overriden(self): return self._is_overriden + @property + def child_has_studio_override(self): + return self.value_input.child_has_studio_override + @property def child_modified(self): return self.value_input.child_modified From 22f8084a8d393a93b4264e8292833f70bd7bd9b0 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 10 Sep 2020 12:53:41 +0200 Subject: [PATCH 529/813] Fix #237 - Reworked reference cleanup --- pype/plugins/maya/load/load_look.py | 121 +++++++++++++++++----------- 1 file changed, 73 insertions(+), 48 deletions(-) diff --git a/pype/plugins/maya/load/load_look.py b/pype/plugins/maya/load/load_look.py index d82978b1a1..cb5b6fa2e8 100644 --- a/pype/plugins/maya/load/load_look.py +++ b/pype/plugins/maya/load/load_look.py @@ -70,6 +70,9 @@ class LookLoader(pype.hosts.maya.plugin.ReferenceLoader): members = cmds.sets(node, query=True, nodesOnly=True) reference_node = self._get_reference_node(members) + shader_nodes = cmds.ls(members, type='shadingEngine') + orig_nodes = set(self._get_nodes_with_shader(shader_nodes)) + file_type = { "ma": "mayaAscii", "mb": "mayaBinary", @@ -80,35 +83,7 @@ class LookLoader(pype.hosts.maya.plugin.ReferenceLoader): assert os.path.exists(path), "%s does not exist." % path - try: - content = cmds.file(path, - loadReference=reference_node, - type=file_type, - returnNewNodes=True) - except RuntimeError as exc: - # When changing a reference to a file that has load errors the - # command will raise an error even if the file is still loaded - # correctly (e.g. when raising errors on Arnold attributes) - # When the file is loaded and has content, we consider it's fine. - if not cmds.referenceQuery(reference_node, isLoaded=True): - raise - - content = cmds.referenceQuery(reference_node, - nodes=True, - dagPath=True) - if not content: - raise - - self.log.warning("Ignoring file read error:\n%s", exc) - - # Fix PLN-40 for older containers created with Avalon that had the - # `.verticesOnlySet` set to True. - if cmds.getAttr("{}.verticesOnlySet".format(node)): - self.log.info("Setting %s.verticesOnlySet to False", node) - cmds.setAttr("{}.verticesOnlySet".format(node), False) - - # Add new nodes of the reference to the container - cmds.sets(content, forceElement=node) + self._load_reference(file_type, node, path, reference_node) # Remove any placeHolderList attribute entries from the set that # are remaining from nodes being removed from the referenced file. @@ -117,18 +92,9 @@ class LookLoader(pype.hosts.maya.plugin.ReferenceLoader): if invalid: cmds.sets(invalid, remove=node) - # Get container members + # get new applied shaders and nodes from new version shader_nodes = cmds.ls(members, type='shadingEngine') - - nodes_list = [] - for shader in shader_nodes: - connections = cmds.listConnections(cmds.listHistory(shader, f=1), - type='mesh') - if connections: - for connection in connections: - nodes_list.extend(cmds.listRelatives(connection, - shapes=True)) - nodes = set(nodes_list) + nodes = set(self._get_nodes_with_shader(shader_nodes)) json_representation = io.find_one({ "type": "representation", @@ -150,20 +116,16 @@ class LookLoader(pype.hosts.maya.plugin.ReferenceLoader): # highlight failed edits to user if failed_edits: - shader_data = relationships.get("relationships", {}) - for rel in shader_data.values(): - for member in rel["members"]: - nodes.add(member['name']) - # clean references - removes failed reference edits cmds.file(unloadReference=reference_node) cmds.file(cr=reference_node) # cleanReference - cmds.file(loadReference=reference_node) + # reload reference, now it shouldn't fail + self._load_reference(file_type, node, path, reference_node) - # reapply shading groups from json representation + # reapply shading groups from json representation on orig nodes pype.hosts.maya.lib.apply_shaders(relationships, shader_nodes, - nodes) + orig_nodes) msg = ["During reference update some edits failed.", "All successful edits were kept intact.\n", @@ -186,3 +148,66 @@ class LookLoader(pype.hosts.maya.plugin.ReferenceLoader): cmds.setAttr("{}.representation".format(node), str(representation["_id"]), type="string") + + def _get_nodes_with_shader(self, shader_nodes): + """ + Returns list of nodes belonging to specific shaders + Args: + shader_nodes: of Shader groups + Returns + node names + """ + import maya.cmds as cmds + # Get container members + + nodes_list = [] + for shader in shader_nodes: + connections = cmds.listConnections(cmds.listHistory(shader, f=1), + type='mesh') + if connections: + for connection in connections: + nodes_list.extend(cmds.listRelatives(connection, + shapes=True)) + return nodes_list + + def _load_reference(self, file_type, node, path, reference_node): + """ + Load reference from 'path' on 'reference_node'. Used when change + of look (version/update) is triggered. + Args: + file_type: extension of referenced file + node: + path: (string) location of referenced file + reference_node: (string) - name of node that should be applied + on + Returns: + None + """ + import maya.cmds as cmds + try: + content = cmds.file(path, + loadReference=reference_node, + type=file_type, + returnNewNodes=True) + except RuntimeError as exc: + # When changing a reference to a file that has load errors the + # command will raise an error even if the file is still loaded + # correctly (e.g. when raising errors on Arnold attributes) + # When the file is loaded and has content, we consider it's fine. + if not cmds.referenceQuery(reference_node, isLoaded=True): + raise + + content = cmds.referenceQuery(reference_node, + nodes=True, + dagPath=True) + if not content: + raise + + self.log.warning("Ignoring file read error:\n%s", exc) + # Fix PLN-40 for older containers created with Avalon that had the + # `.verticesOnlySet` set to True. + if cmds.getAttr("{}.verticesOnlySet".format(node)): + self.log.info("Setting %s.verticesOnlySet to False", node) + cmds.setAttr("{}.verticesOnlySet".format(node), False) + # Add new nodes of the reference to the container + cmds.sets(content, forceElement=node) From adb87fa9dd3fc4dcab07f0c2c1a055a2803a13a5 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 10 Sep 2020 13:13:52 +0200 Subject: [PATCH 530/813] Hound --- pype/widgets/message_window.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pype/widgets/message_window.py b/pype/widgets/message_window.py index f909c60710..969d6ccdd1 100644 --- a/pype/widgets/message_window.py +++ b/pype/widgets/message_window.py @@ -91,8 +91,7 @@ class ScrollMessageBox(QtWidgets.QDialog): messages: of messages cancelable: - True if Cancel button should be added """ - def __init__(self, icon, title, messages, cancelable=False, - *args, **kwargs): + def __init__(self, icon, title, messages, cancelable=False): super(ScrollMessageBox, self).__init__() self.setWindowTitle(title) self.icon = icon From c618cd0b4421a32412fee2806a93db72673cb1cc Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Sep 2020 16:01:23 +0200 Subject: [PATCH 531/813] modified pathwidget --- .../config_setting/widgets/inputs.py | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 8e5e8c2a5d..c32d45246b 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1197,7 +1197,7 @@ class ModifiableDictItem(QtWidgets.QWidget, ConfigObject): self.key_input.textChanged.connect(self._on_value_change) self.value_input.value_changed.connect(self._on_value_change) - self.origin_key = self.key_value() + self.origin_key = NOT_SET def key_value(self): return self.key_input.text() @@ -1214,6 +1214,11 @@ class ModifiableDictItem(QtWidgets.QWidget, ConfigObject): self.update_style() self.value_changed.emit(self) + def update_default_values(self, key, value): + self.origin_key = key + self.key_input.setText(key) + self.value_input.update_default_values(value) + def update_studio_values(self, key, value): self.origin_key = key self.key_input.setText(key) @@ -1488,7 +1493,9 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): # Set value if entered value is not None # else (when add button clicked) trigger `_on_value_change` if value is not None and key is not None: - if self._is_overriden: + if not self._has_studio_override: + item_widget.update_default_values(key, value) + elif self._is_overriden: item_widget.apply_overrides(key, value) else: item_widget.update_studio_values(key, value) @@ -2206,7 +2213,12 @@ class PathWidget(QtWidgets.QWidget, ConfigObject): self.default_value = value self._has_studio_override = False self._had_studio_override = False - self.set_value(value) + + if not self.multiplatform: + self.input_fields[0].update_studio_values(value) + else: + for input_field in self.input_fields: + input_field.update_studio_values(value) def update_studio_values(self, parent_values): self._state = None @@ -2223,12 +2235,16 @@ class PathWidget(QtWidgets.QWidget, ConfigObject): if value is not NOT_SET: self._has_studio_override = True self._had_studio_override = True - self.set_value(value) - else: self._has_studio_override = False self._had_studio_override = False - self.set_value(self.default_value) + value = self.default_value + + if not self.multiplatform: + self.input_fields[0].update_studio_values(value) + else: + for input_field in self.input_fields: + input_field.update_studio_values(value) def apply_overrides(self, parent_values): self._is_modified = False From 3e5b0cf82f0a547d0f2668d455e5357d02b4eafa Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Sep 2020 16:01:41 +0200 Subject: [PATCH 532/813] fixed saving in bases --- .../config_setting/widgets/base.py | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/base.py b/pype/tools/config_setting/config_setting/widgets/base.py index a143e584dd..a8bdd9b1a4 100644 --- a/pype/tools/config_setting/config_setting/widgets/base.py +++ b/pype/tools/config_setting/config_setting/widgets/base.py @@ -429,14 +429,15 @@ class ProjectWidget(QtWidgets.QWidget): self._save_overrides() def _save_overrides(self): - _data = {} + data = {} for item in self.input_fields: value, is_group = item.overrides() if value is not lib.NOT_SET: - _data.update(value) + data.update(value) - data = _data.get("project") or {} - output_data = lib.convert_gui_data_to_overrides(data) + output_data = lib.convert_gui_data_to_overrides( + data.get("project") or {} + ) overrides_json_path = path_to_project_overrides( self.project_name @@ -452,21 +453,27 @@ class ProjectWidget(QtWidgets.QWidget): self._on_project_change() def _save_defaults(self): - _data = {} + data = {} for input_field in self.input_fields: value, is_group = input_field.studio_overrides() if value is not lib.NOT_SET: - _data.update(value) + data.update(value) - output = lib.convert_gui_data_to_overrides(_data.get("project", {})) + output_data = lib.convert_gui_data_to_overrides( + data.get("project", {}) + ) dirpath = os.path.dirname(PROJECT_CONFIGURATIONS_PATH) if not os.path.exists(dirpath): os.makedirs(dirpath) print("Saving data to:", PROJECT_CONFIGURATIONS_PATH) - with open(PROJECT_CONFIGURATIONS_PATH, "w") as file_stream: - json.dump(output, file_stream, indent=4) + try: + with open(PROJECT_CONFIGURATIONS_PATH, "w") as file_stream: + json.dump(output_data, file_stream, indent=4) + except Exception as exc: + print(output_data) + raise self._update_values() From 25f3d91b6d6a60eeee3b0eaad0c3258a093b4ec0 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Sep 2020 16:01:53 +0200 Subject: [PATCH 533/813] fixed anatomy inputs filling --- .../config_setting/widgets/anatomy_inputs.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index 141feee61d..e5f0dd5990 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -1,7 +1,7 @@ from Qt import QtWidgets, QtCore from .widgets import ExpandingWidget from .inputs import ConfigObject, ModifiableDict, PathWidget, RawJsonWidget -from .lib import NOT_SET, TypeToKlass, CHILD_OFFSET +from .lib import NOT_SET, TypeToKlass, CHILD_OFFSET, METADATA_KEY class AnatomyWidget(QtWidgets.QWidget, ConfigObject): @@ -192,6 +192,17 @@ class AnatomyWidget(QtWidgets.QWidget, ConfigObject): output.update(self.templates_widget.config_value()) return output + def studio_overrides(self): + if ( + self.root_widget.child_has_studio_override + or self.templates_widget.child_has_studio_override + ): + groups = [self.root_widget.key, self.templates_widget.key] + value = self.config_value() + value[self.key][METADATA_KEY] = {"groups": groups} + return value, True + return NOT_SET, False + def config_value(self): return {self.key: self.item_value()} @@ -339,11 +350,9 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): self._had_studio_override = True if is_multiroot: - self.singleroot_widget.update_studio_values(NOT_SET) self.multiroot_widget.update_studio_values(value) else: self.singleroot_widget.update_studio_values(value) - self.multiroot_widget.update_studio_values(NOT_SET) def apply_overrides(self, parent_values): # Make sure this is set to False @@ -371,13 +380,11 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): if is_multiroot: self._is_overriden = parent_values is not NOT_SET self._was_overriden = bool(self._is_overriden) - self.singleroot_widget.apply_overrides(NOT_SET) self.multiroot_widget.apply_overrides(parent_values) else: self._is_overriden = value is not NOT_SET self._was_overriden = bool(self._is_overriden) self.singleroot_widget.apply_overrides(value) - self.multiroot_widget.apply_overrides(NOT_SET) def hierarchical_style_update(self): self.singleroot_widget.hierarchical_style_update() From f2372c27b9e873b0008a8d0484888ba73ff5f4b2 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Sep 2020 17:02:04 +0200 Subject: [PATCH 534/813] fixed roots input statuses --- .../config_setting/config_setting/widgets/anatomy_inputs.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index e5f0dd5990..bf2477bd7c 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -292,6 +292,7 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): def update_default_values(self, parent_values): self._state = None self._multiroot_state = None + self._is_modified = False if isinstance(parent_values, dict): value = parent_values.get(self.key, NOT_SET) @@ -309,6 +310,8 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): self.was_multiroot = is_multiroot self.set_multiroot(is_multiroot) + self._has_studio_override = False + self._had_studio_override = False if is_multiroot: for _value in value.values(): singleroot_value = _value @@ -325,6 +328,7 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): def update_studio_values(self, parent_values): self._state = None self._multiroot_state = None + self._is_modified = False if isinstance(parent_values, dict): value = parent_values.get(self.key, NOT_SET) @@ -597,7 +601,7 @@ class TemplatesWidget(QtWidgets.QWidget, ConfigObject): child_state = "child-{}".format(state) else: child_state = "" - print(child_state) + self.body_widget.side_line_widget.setProperty("state", child_state) self.body_widget.side_line_widget.style().polish( self.body_widget.side_line_widget From 5aa922026d338c487a13c0e12642e47741319511 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Sep 2020 17:18:39 +0200 Subject: [PATCH 535/813] templates widget has value_changed signal --- .../config_setting/widgets/anatomy_inputs.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index bf2477bd7c..404fe589d3 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -74,6 +74,7 @@ class AnatomyWidget(QtWidgets.QWidget, ConfigObject): self.label_widget = body_widget.label_widget self.root_widget.value_changed.connect(self._on_value_change) + self.templates_widget.value_changed.connect(self._on_value_change) def update_default_values(self, parent_values): self._state = None @@ -538,6 +539,8 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): class TemplatesWidget(QtWidgets.QWidget, ConfigObject): + value_changed = QtCore.Signal(object) + def __init__(self, parent): super(TemplatesWidget, self).__init__(parent) @@ -571,6 +574,13 @@ class TemplatesWidget(QtWidgets.QWidget, ConfigObject): layout.addWidget(body_widget) + self.value_input.value_changed.connect(self._on_value_change) + + def _on_value_change(self, item): + self.update_style() + + self.value_changed.emit(self) + def update_default_values(self, values): self._state = None self.value_input.update_default_values(values) @@ -620,6 +630,10 @@ class TemplatesWidget(QtWidgets.QWidget, ConfigObject): def is_overriden(self): return self._is_overriden + @property + def has_studio_override(self): + return self.value_input.has_studio_override + @property def child_has_studio_override(self): return self.value_input.child_has_studio_override From 5928990cb3159395bd7521a0335e256fd3d21a32 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Sep 2020 17:18:51 +0200 Subject: [PATCH 536/813] just small modification --- pype/tools/config_setting/config_setting/widgets/inputs.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index c32d45246b..05fd9e8aab 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -816,8 +816,7 @@ class RawJsonWidget(QtWidgets.QWidget, InputObject): def update_studio_values(self, parent_values): self._is_invalid = self.text_input.has_invalid_value() - - super(RawJsonWidget, self).update_studio_values(parent_values) + return super(RawJsonWidget, self).update_studio_values(parent_values) def set_value(self, value): self.text_input.set_value(value) From d020503f53e01983b4184ed2c5fc6b5418f50e29 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Sep 2020 17:53:41 +0200 Subject: [PATCH 537/813] ModifiableDict has fixed invalid state --- .../config_setting/config_setting/widgets/inputs.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 05fd9e8aab..5763738351 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1337,6 +1337,7 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): if as_widget: main_layout.addWidget(content_widget) + body_widget = None else: body_widget = ExpandingWidget(input_data["label"], self) main_layout.addWidget(body_widget) @@ -1353,6 +1354,7 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): if expanded: body_widget.toggle_content() + self.body_widget = body_widget self.content_widget = content_widget self.content_layout = content_layout @@ -1427,7 +1429,7 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): else: state = self.style_state( False, - self._is_invalid, + self.is_invalid, False, self._is_modified ) @@ -1446,8 +1448,11 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): else: child_state = "" - self.setProperty("state", child_state) - self.style().polish(self) + if self.body_widget: + self.body_widget.side_line_widget.setProperty("state", child_state) + self.body_widget.side_line_widget.style().polish( + self.body_widget.side_line_widget + ) if not self._as_widget: self.label_widget.setProperty("state", state) From 139c1b50cf417cd2ac313f9bb77f74422ff57f32 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Sep 2020 18:02:07 +0200 Subject: [PATCH 538/813] fixed style updates for root widget --- .../config_setting/config_setting/widgets/anatomy_inputs.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index 404fe589d3..efba588692 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -410,7 +410,7 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): state = self.style_state( self.has_studio_override, - self.is_invalid, + self.child_invalid, self.is_overriden, self.is_modified ) @@ -453,6 +453,8 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): or self.child_modified ) + self.update_style() + self.value_changed.emit(self) def set_multiroot(self, is_multiroot=None): From a2d3072fd6c5b52b6ea10e6f8484b7e6a1ff2643 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Thu, 10 Sep 2020 18:02:55 +0200 Subject: [PATCH 539/813] extract render was duplicating extract review --- .../publish/integrate_ftrack_instances.py | 10 +++- .../plugins/harmony/publish/extract_render.py | 59 +++---------------- 2 files changed, 17 insertions(+), 52 deletions(-) diff --git a/pype/plugins/ftrack/publish/integrate_ftrack_instances.py b/pype/plugins/ftrack/publish/integrate_ftrack_instances.py index 9ef82cbc4b..2646dc90cc 100644 --- a/pype/plugins/ftrack/publish/integrate_ftrack_instances.py +++ b/pype/plugins/ftrack/publish/integrate_ftrack_instances.py @@ -88,8 +88,14 @@ class IntegrateFtrackInstance(pyblish.api.InstancePlugin): instance.data["frameEnd"] - instance.data["frameStart"] ) - if not comp.get('fps'): - comp['fps'] = instance.context.data['fps'] + fps = comp.get('fps') + if fps is None: + fps = instance.data.get( + "fps", instance.context.data['fps'] + ) + + comp['fps'] = fps + location = self.get_ftrack_location( 'ftrack.server', ft_session ) diff --git a/pype/plugins/harmony/publish/extract_render.py b/pype/plugins/harmony/publish/extract_render.py index 45b52e0307..6486c13d70 100644 --- a/pype/plugins/harmony/publish/extract_render.py +++ b/pype/plugins/harmony/publish/extract_render.py @@ -4,6 +4,7 @@ import subprocess import pyblish.api from avalon import harmony +import pype.lib import clique @@ -43,6 +44,8 @@ class ExtractRender(pyblish.api.InstancePlugin): frame_start = result[4] frame_end = result[5] audio_path = result[6] + instance.data["audio"] = [{"filename": audio_path}] + instance.data["fps"] = frame_rate # Set output path to temp folder. path = tempfile.mkdtemp() @@ -87,17 +90,13 @@ class ExtractRender(pyblish.api.InstancePlugin): if len(list(col)) > 1: collection = col else: - # assert len(collections) == 1, ( - # "There should only be one image sequence in {}. Found: {}".format( - # path, len(collections) - # ) - # ) collection = collections[0] # Generate thumbnail. thumbnail_path = os.path.join(path, "thumbnail.png") + ffmpeg_path = pype.lib.get_ffmpeg_tool_path("ffmpeg") args = [ - "ffmpeg", "-y", + ffmpeg_path, "-y", "-i", os.path.join(path, list(collections[0])[0]), "-vf", "scale=300:-1", "-vframes", "1", @@ -117,57 +116,17 @@ class ExtractRender(pyblish.api.InstancePlugin): self.log.debug(output.decode("utf-8")) - # Generate mov. - mov_path = os.path.join(path, instance.data["name"] + ".mov") - if os.path.isfile(audio_path): - args = [ - "ffmpeg", "-y", - "-i", audio_path, - "-i", - os.path.join(path, collection.head + "%04d" + collection.tail), - mov_path - ] - else: - args = [ - "ffmpeg", "-y", - "-i", - os.path.join(path, collection.head + "%04d" + collection.tail), - mov_path - ] - - process = subprocess.Popen( - args, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - stdin=subprocess.PIPE - ) - - output = process.communicate()[0] - - if process.returncode != 0: - raise ValueError(output.decode("utf-8")) - - self.log.debug(output.decode("utf-8")) - # Generate representations. extension = collection.tail[1:] representation = { "name": extension, "ext": extension, "files": list(collection), - "stagingDir": path - } - movie = { - "name": "mov", - "ext": "mov", - "files": os.path.basename(mov_path), "stagingDir": path, - "frameStart": frame_start, - "frameEnd": frame_end, - "fps": frame_rate, - "preview": True, - "tags": ["review", "ftrackreview"] + "tags": ["review"], + "fps": frame_rate } + thumbnail = { "name": "thumbnail", "ext": "png", @@ -175,7 +134,7 @@ class ExtractRender(pyblish.api.InstancePlugin): "stagingDir": path, "tags": ["thumbnail"] } - instance.data["representations"] = [representation, movie, thumbnail] + instance.data["representations"] = [representation, thumbnail] # Required for extract_review plugin (L222 onwards). instance.data["frameStart"] = frame_start From 81b328977d4ef701671c5567b657c1c92ac51015 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Thu, 10 Sep 2020 20:54:11 +0200 Subject: [PATCH 540/813] allow burnin in harmony --- pype/plugins/global/publish/extract_burnin.py | 3 ++- pype/plugins/harmony/publish/extract_render.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pype/plugins/global/publish/extract_burnin.py b/pype/plugins/global/publish/extract_burnin.py index dedfd98979..4443cfe223 100644 --- a/pype/plugins/global/publish/extract_burnin.py +++ b/pype/plugins/global/publish/extract_burnin.py @@ -25,7 +25,8 @@ class ExtractBurnin(pype.api.Extractor): "shell", "nukestudio", "premiere", - "standalonepublisher" + "standalonepublisher", + "harmony" ] optional = True diff --git a/pype/plugins/harmony/publish/extract_render.py b/pype/plugins/harmony/publish/extract_render.py index 6486c13d70..70dceb9ca2 100644 --- a/pype/plugins/harmony/publish/extract_render.py +++ b/pype/plugins/harmony/publish/extract_render.py @@ -44,7 +44,8 @@ class ExtractRender(pyblish.api.InstancePlugin): frame_start = result[4] frame_end = result[5] audio_path = result[6] - instance.data["audio"] = [{"filename": audio_path}] + if audio_path: + instance.data["audio"] = [{"filename": audio_path}] instance.data["fps"] = frame_rate # Set output path to temp folder. From c8a6837d8927c0b251a5c3c63ecbf90d2d935c53 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 10 Sep 2020 21:30:25 +0200 Subject: [PATCH 541/813] removed activate_project and replaced with setting Session attribute --- pype/tools/standalonepublish/widgets/widget_asset.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/tools/standalonepublish/widgets/widget_asset.py b/pype/tools/standalonepublish/widgets/widget_asset.py index c468c9627b..6f041a535f 100644 --- a/pype/tools/standalonepublish/widgets/widget_asset.py +++ b/pype/tools/standalonepublish/widgets/widget_asset.py @@ -240,7 +240,7 @@ class AssetWidget(QtWidgets.QWidget): self.combo_projects.clear() if len(projects) > 0: self.combo_projects.addItems(projects) - self.dbcon.activate_project(projects[0]) + self.dbcon.Session["AVALON_PROJECT"] = projects[0] def on_project_change(self): projects = list() @@ -248,7 +248,7 @@ class AssetWidget(QtWidgets.QWidget): projects.append(project['name']) project_name = self.combo_projects.currentText() if project_name in projects: - self.dbcon.activate_project(project_name) + self.dbcon.Session["AVALON_PROJECT"] = project_name self.refresh() def _refresh_model(self): From b161dda7adea6a43b2e58f99a29823dddd7ea255 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Sep 2020 11:27:13 +0200 Subject: [PATCH 542/813] fix config loading --- pype/configurations/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/configurations/config.py b/pype/configurations/config.py index e19a27f33c..ed7104427f 100644 --- a/pype/configurations/config.py +++ b/pype/configurations/config.py @@ -14,7 +14,7 @@ def system_configurations(): def project_configurations(project_name): - default_values = default_configuration() + default_values = default_configuration()["project_configurations"] studio_values = studio_project_configurations() studio_overrides = apply_overrides(default_values, studio_values) From 2f87b60f2e2dd967a465682f9420a030564bcb99 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Sep 2020 11:27:44 +0200 Subject: [PATCH 543/813] add develop button to save as default --- pype/tools/config_setting/__main__.py | 3 ++- .../config_setting/widgets/base.py | 23 +++++++++++++++++-- .../config_setting/widgets/window.py | 6 ++--- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/pype/tools/config_setting/__main__.py b/pype/tools/config_setting/__main__.py index aa6f707443..0e4ab1c0aa 100644 --- a/pype/tools/config_setting/__main__.py +++ b/pype/tools/config_setting/__main__.py @@ -11,7 +11,8 @@ if __name__ == "__main__": app.setStyleSheet(stylesheet) app.setWindowIcon(QtGui.QIcon(config_setting.style.app_icon_path())) - widget = config_setting.MainWidget() + develop = "-dev" in sys.argv + widget = config_setting.MainWidget(develop) widget.show() sys.exit(app.exec_()) diff --git a/pype/tools/config_setting/config_setting/widgets/base.py b/pype/tools/config_setting/config_setting/widgets/base.py index a8bdd9b1a4..52d6b73c98 100644 --- a/pype/tools/config_setting/config_setting/widgets/base.py +++ b/pype/tools/config_setting/config_setting/widgets/base.py @@ -22,9 +22,10 @@ class SystemWidget(QtWidgets.QWidget): is_group = _is_group = False any_parent_is_group = _any_parent_is_group = False - def __init__(self, parent=None): + def __init__(self, develop, parent=None): super(SystemWidget, self).__init__(parent) + self.develop = develop self._ignore_value_changes = False self.input_fields = [] @@ -48,6 +49,11 @@ class SystemWidget(QtWidgets.QWidget): footer_widget = QtWidgets.QWidget() footer_layout = QtWidgets.QHBoxLayout(footer_widget) + if self.develop: + save_as_default_btn = QtWidgets.QPushButton("Save as Default") + footer_layout.addWidget(save_as_default_btn, 0) + save_as_default_btn.clicked.connect(self._save_as_defaults) + save_btn = QtWidgets.QPushButton("Save") spacer_widget = QtWidgets.QWidget() footer_layout.addWidget(spacer_widget, 1) @@ -138,6 +144,9 @@ class SystemWidget(QtWidgets.QWidget): self._update_values() + def _save_as_defaults(self): + print("_save_as_defaults") + def _update_values(self): self.ignore_value_changes = True @@ -293,9 +302,11 @@ class ProjectWidget(QtWidgets.QWidget): is_group = _is_group = False any_parent_is_group = _any_parent_is_group = False - def __init__(self, parent=None): + def __init__(self, develop, parent=None): super(ProjectWidget, self).__init__(parent) + self.develop = develop + self.is_overidable = False self._ignore_value_changes = False self.project_name = None @@ -320,6 +331,11 @@ class ProjectWidget(QtWidgets.QWidget): footer_widget = QtWidgets.QWidget() footer_layout = QtWidgets.QHBoxLayout(footer_widget) + if self.develop: + save_as_default_btn = QtWidgets.QPushButton("Save as Default") + footer_layout.addWidget(save_as_default_btn, 0) + save_as_default_btn.clicked.connect(self._save_as_defaults) + save_btn = QtWidgets.QPushButton("Save") spacer_widget = QtWidgets.QWidget() footer_layout.addWidget(spacer_widget, 1) @@ -398,6 +414,9 @@ class ProjectWidget(QtWidgets.QWidget): item.apply_overrides(overrides) self.ignore_value_changes = False + def _save_as_defaults(self): + print("_save_as_defaults") + def _save(self): has_invalid = False for item in self.input_fields: diff --git a/pype/tools/config_setting/config_setting/widgets/window.py b/pype/tools/config_setting/config_setting/widgets/window.py index 7ad46b1a06..f8da9a196e 100644 --- a/pype/tools/config_setting/config_setting/widgets/window.py +++ b/pype/tools/config_setting/config_setting/widgets/window.py @@ -6,15 +6,15 @@ class MainWidget(QtWidgets.QWidget): widget_width = 1000 widget_height = 600 - def __init__(self, parent=None): + def __init__(self, develop, parent=None): super(MainWidget, self).__init__(parent) self.resize(self.widget_width, self.widget_height) header_tab_widget = QtWidgets.QTabWidget(parent=self) - studio_widget = SystemWidget() - project_widget = ProjectWidget() + studio_widget = SystemWidget(develop) + project_widget = ProjectWidget(develop) header_tab_widget.addTab(studio_widget, "System") header_tab_widget.addTab(project_widget, "Project") From 6539dc325c2bd9ba43265d2adc3358dbc2b5d7f2 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Sep 2020 11:49:06 +0200 Subject: [PATCH 544/813] removed ftrack project config --- .../1_ftrack_projects_gui_schema.json | 39 ------------------- 1 file changed, 39 deletions(-) delete mode 100644 pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_ftrack_projects_gui_schema.json diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_ftrack_projects_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_ftrack_projects_gui_schema.json deleted file mode 100644 index e9937e64b8..0000000000 --- a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_ftrack_projects_gui_schema.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "key": "ftrack", - "type": "dict", - "expandable": true, - "label": "Ftrack", - "children": [ - { - "key": "status_update", - "type": "dict", - "expandable": true, - "label": "Status updates", - "is_group": true, - "children": [ - { - "key": "Ready", - "type": "text", - "label": "Ready" - }, { - "key": "Ready2", - "type": "text", - "label": "Ready2" - } - ] - }, { - "key": "status_version_to_task", - "type": "dict", - "expandable": true, - "label": "Version status to Task status", - "is_group": true, - "children": [ - { - "key": "in progress", - "type": "text", - "label": "In Progress" - } - ] - } - ] -} From e9b9198f4384d7c068959dcaea153652dc5bee01 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Sep 2020 11:52:46 +0200 Subject: [PATCH 545/813] added is_file attribute back --- .../projects_schema/0_project_gui_schema.json | 13 +++- .../projects_schema/1_plugins_gui_schema.json | 15 ++++- .../system_schema/0_system_gui_schema.json | 3 +- .../1_applications_gui_schema.json | 1 + .../system_schema/1_intents_gui_schema.json | 1 + .../system_schema/1_tools_gui_schema.json | 1 + .../system_schema/1_tray_items.json | 1 + .../config_setting/widgets/lib.py | 67 +++++++++++++++++++ 8 files changed, 98 insertions(+), 4 deletions(-) diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/0_project_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/0_project_gui_schema.json index 86504eefd2..52565640bc 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/0_project_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/0_project_gui_schema.json @@ -4,7 +4,18 @@ "children": [ { "type": "anatomy", - "key": "anatomy" + "key": "anatomy", + "children": [ + { + "type": "anatomy_roots", + "key": "roots", + "is_file": true + }, { + "type": "anatomy_templates", + "key": "templates", + "is_file": true + } + ] }, { "type": "schema", "children": [ diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json index 597f51d6fd..5dad665b2d 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json @@ -15,6 +15,7 @@ "expandable": true, "key": "publish", "label": "Publish plugins", + "is_file": true, "children": [ { "type": "dict", @@ -74,6 +75,7 @@ "expandable": true, "key": "publish", "label": "Publish plugins", + "is_file": true, "children": [ { "type": "dict", @@ -113,6 +115,7 @@ "expandable": true, "key": "publish", "label": "Publish plugins", + "is_file": true, "children": [ { "type": "dict", @@ -276,6 +279,7 @@ "expandable": true, "key": "publish", "label": "Publish plugins", + "is_file": true, "children": [ { "type": "dict", @@ -350,7 +354,8 @@ }, { "type": "raw-json", "key": "workfile_build", - "label": "Workfile Build logic" + "label": "Workfile Build logic", + "is_file": true } ] }, { @@ -364,6 +369,7 @@ "expandable": true, "key": "create", "label": "Create plugins", + "is_file": true, "children": [ { "type": "dict", @@ -398,6 +404,7 @@ "expandable": true, "key": "publish", "label": "Publish plugins", + "is_file": true, "children": [ { "type": "dict", @@ -510,7 +517,8 @@ }, { "type": "raw-json", "key": "workfile_build", - "label": "Workfile Build logic" + "label": "Workfile Build logic", + "is_file": true } ] }, { @@ -524,6 +532,7 @@ "expandable": true, "key": "publish", "label": "Publish plugins", + "is_file": true, "children": [ { "type": "dict", @@ -573,6 +582,7 @@ "expandable": true, "key": "create", "label": "Creator plugins", + "is_file": true, "children": [ { "type": "dict", @@ -611,6 +621,7 @@ "expandable": true, "key": "publish", "label": "Publish plugins", + "is_file": true, "children": [] } ] diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/0_system_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/0_system_gui_schema.json index 0b51267f4e..bdc0158511 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/0_system_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/0_system_gui_schema.json @@ -27,7 +27,8 @@ }, "is_group": true, "key": "templates_mapping", - "label": "Muster - Templates mapping" + "label": "Muster - Templates mapping", + "is_file": true }] } ] diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/1_applications_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/1_applications_gui_schema.json index af128b138f..5ee769eed8 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/1_applications_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/1_applications_gui_schema.json @@ -4,6 +4,7 @@ "label": "Applications", "expandable": true, "is_group": true, + "is_file": true, "children": [ { "type": "dict-form", diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/1_intents_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/1_intents_gui_schema.json index 11b45c8409..a4b5e16fa1 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/1_intents_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/1_intents_gui_schema.json @@ -4,6 +4,7 @@ "label": "Intent Setting", "expandable": true, "is_group": true, + "is_file": true, "children": [ { "type": "dict-modifiable", diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/1_tools_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/1_tools_gui_schema.json index a16f00547e..bf35326d1c 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/1_tools_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/1_tools_gui_schema.json @@ -4,6 +4,7 @@ "label": "Tools", "expandable": true, "is_group": true, + "is_file": true, "children": [ { "type": "dict-form", diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/1_tray_items.json b/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/1_tray_items.json index afa509254e..7507050900 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/1_tray_items.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/1_tray_items.json @@ -4,6 +4,7 @@ "label": "Modules", "expandable": true, "is_group": true, + "is_file": true, "children": [ { "key": "item_usage", diff --git a/pype/tools/config_setting/config_setting/widgets/lib.py b/pype/tools/config_setting/config_setting/widgets/lib.py index 6b3aa53c8f..daba87de15 100644 --- a/pype/tools/config_setting/config_setting/widgets/lib.py +++ b/pype/tools/config_setting/config_setting/widgets/lib.py @@ -80,6 +80,19 @@ def _fill_inner_schemas(schema_data, schema_collection): return schema_data +class SchemaMissingFileInfo(Exception): + def __init__(self, invalid): + full_path_keys = [] + for item in invalid: + full_path_keys.append("\"{}\"".format("/".join(item))) + + msg = ( + "Schema has missing definition of output file (\"is_file\" key)" + " for keys. [{}]" + ).format(", ".join(full_path_keys)) + super(SchemaMissingFileInfo, self).__init__(msg) + + class SchemeGroupHierarchyBug(Exception): def __init__(self, invalid): full_path_keys = [] @@ -108,6 +121,59 @@ class SchemaDuplicatedKeys(Exception): super(SchemaDuplicatedKeys, self).__init__(msg) +def file_keys_from_schema(schema_data): + output = [] + keys = [] + key = schema_data.get("key") + if key: + keys.append(key) + + for child in schema_data["children"]: + if child.get("is_file"): + _keys = copy.deepcopy(keys) + _keys.append(child["key"]) + output.append(_keys) + continue + + for result in file_keys_from_schema(child): + _keys = copy.deepcopy(keys) + _keys.extend(result) + output.append(_keys) + return output + + +def validate_all_has_ending_file(schema_data, is_top=True): + if schema_data.get("is_file"): + return None + + children = schema_data.get("children") + if not children: + return [[schema_data["key"]]] + + invalid = [] + keyless = "key" not in schema_data + for child in children: + result = validate_all_has_ending_file(child, False) + if result is None: + continue + + if keyless: + invalid.extend(result) + else: + for item in result: + new_invalid = [schema_data["key"]] + new_invalid.extend(item) + invalid.append(new_invalid) + + if not invalid: + return None + + if not is_top: + return invalid + + raise SchemaMissingFileInfo(invalid) + + def validate_is_group_is_unique_in_hierarchy( schema_data, any_parent_is_group=False, keys=None ): @@ -203,6 +269,7 @@ def validate_keys_are_unique(schema_data, keys=None): 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) From b8c15fd5144a22bf675bae4b074205e8b5ef53f0 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Sep 2020 12:01:16 +0200 Subject: [PATCH 546/813] added develop_mode attribute to inputs --- .../config_setting/config_setting/widgets/base.py | 12 ++++++------ .../config_setting/config_setting/widgets/inputs.py | 4 ++++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/base.py b/pype/tools/config_setting/config_setting/widgets/base.py index 52d6b73c98..58e53b8c58 100644 --- a/pype/tools/config_setting/config_setting/widgets/base.py +++ b/pype/tools/config_setting/config_setting/widgets/base.py @@ -22,10 +22,10 @@ class SystemWidget(QtWidgets.QWidget): is_group = _is_group = False any_parent_is_group = _any_parent_is_group = False - def __init__(self, develop, parent=None): + def __init__(self, develop_mode, parent=None): super(SystemWidget, self).__init__(parent) - self.develop = develop + self.develop_mode = develop_mode self._ignore_value_changes = False self.input_fields = [] @@ -49,7 +49,7 @@ class SystemWidget(QtWidgets.QWidget): footer_widget = QtWidgets.QWidget() footer_layout = QtWidgets.QHBoxLayout(footer_widget) - if self.develop: + if self.develop_mode: save_as_default_btn = QtWidgets.QPushButton("Save as Default") footer_layout.addWidget(save_as_default_btn, 0) save_as_default_btn.clicked.connect(self._save_as_defaults) @@ -302,10 +302,10 @@ class ProjectWidget(QtWidgets.QWidget): is_group = _is_group = False any_parent_is_group = _any_parent_is_group = False - def __init__(self, develop, parent=None): + def __init__(self, develop_mode, parent=None): super(ProjectWidget, self).__init__(parent) - self.develop = develop + self.develop_mode = develop_mode self.is_overidable = False self._ignore_value_changes = False @@ -331,7 +331,7 @@ class ProjectWidget(QtWidgets.QWidget): footer_widget = QtWidgets.QWidget() footer_layout = QtWidgets.QHBoxLayout(footer_widget) - if self.develop: + if self.develop_mode: save_as_default_btn = QtWidgets.QPushButton("Save as Default") footer_layout.addWidget(save_as_default_btn, 0) save_as_default_btn.clicked.connect(self._save_as_defaults) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 5763738351..5f56f39518 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -33,6 +33,10 @@ class ConfigObject(AbstractConfigObject): _any_parent_is_group = None _log = None + @property + def develop_mode(self): + return self._parent.develop_mode + @property def log(self): if self._log is None: From 68ba48bcf294c1a48958150b010e0d098444db03 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Sep 2020 13:29:15 +0200 Subject: [PATCH 547/813] in develop mode empty defaults won't crash the gui --- .../config_setting/widgets/inputs.py | 27 +++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 5f56f39518..ea774d90de 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -12,6 +12,7 @@ from .lib import NOT_SET, METADATA_KEY, TypeToKlass, CHILD_OFFSET class ConfigObject(AbstractConfigObject): + default_input_value = NOT_SET allow_actions = True default_state = "" @@ -217,11 +218,20 @@ class InputObject(ConfigObject): value = parent_values.get(self.key, NOT_SET) if value is NOT_SET: - raise ValueError( - "Default value is not set. This is implementation BUG." - ) + if self.develop_mode: + value = self.default_input_value + if value is NOT_SET: + raise NotImplementedError(( + "{} Does not have implemented" + " attribute `default_input_value`" + ).format(self)) - self.default_value = value + else: + raise ValueError( + "Default value is not set. This is implementation BUG." + ) + + self._default_value = value self._has_studio_override = False self._had_studio_override = False self.set_value(value) @@ -370,6 +380,7 @@ class InputObject(ConfigObject): class BooleanWidget(QtWidgets.QWidget, InputObject): + default_input_value = True value_changed = QtCore.Signal(object) def __init__( @@ -455,6 +466,7 @@ class BooleanWidget(QtWidgets.QWidget, InputObject): class NumberWidget(QtWidgets.QWidget, InputObject): + default_input_value = 0 value_changed = QtCore.Signal(object) input_modifiers = ("minimum", "maximum", "decimal") @@ -541,6 +553,7 @@ class NumberWidget(QtWidgets.QWidget, InputObject): class TextWidget(QtWidgets.QWidget, InputObject): + default_input_value = "" value_changed = QtCore.Signal(object) def __init__( @@ -633,6 +646,7 @@ class TextWidget(QtWidgets.QWidget, InputObject): class PathInputWidget(QtWidgets.QWidget, InputObject): + default_input_value = "" value_changed = QtCore.Signal(object) def __init__( @@ -768,6 +782,7 @@ class RawJsonInput(QtWidgets.QPlainTextEdit): class RawJsonWidget(QtWidgets.QWidget, InputObject): + default_input_value = "{}" value_changed = QtCore.Signal(object) def __init__( @@ -872,9 +887,9 @@ class ListItem(QtWidgets.QWidget, ConfigObject): value_changed = QtCore.Signal(object) def __init__(self, object_type, input_modifiers, config_parent, parent): - self._parent = config_parent super(ListItem, self).__init__(parent) + self._parent = config_parent layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(3) @@ -962,6 +977,7 @@ class ListItem(QtWidgets.QWidget, ConfigObject): class ListWidget(QtWidgets.QWidget, InputObject): + default_input_value = [] value_changed = QtCore.Signal(object) def __init__( @@ -1297,6 +1313,7 @@ class ModifiableDictItem(QtWidgets.QWidget, ConfigObject): class ModifiableDict(QtWidgets.QWidget, InputObject): + default_input_value = {} # Should be used only for dictionary with one datatype as value # TODO this is actually input field (do not care if is group or not) value_changed = QtCore.Signal(object) From 72221cfafd75ae167f907608faed624a497728d7 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Sep 2020 13:39:17 +0200 Subject: [PATCH 548/813] it is possible to know that defaults are not set --- .../config_setting/widgets/inputs.py | 218 +++++++----------- 1 file changed, 77 insertions(+), 141 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index ea774d90de..32befd6ef8 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -14,25 +14,58 @@ from .lib import NOT_SET, METADATA_KEY, TypeToKlass, CHILD_OFFSET class ConfigObject(AbstractConfigObject): default_input_value = NOT_SET allow_actions = True - default_state = "" - _as_widget = False - _is_group = False - # TODO not implemented yet - _is_nullable = False + def set_default_attributes(self): + # Default input attributes + self._has_studio_override = False + self._had_studio_override = False - _has_studio_override = False - _had_studio_override = False + self._is_overriden = False + self._was_overriden = False - _is_overriden = False - _was_overriden = False + self._is_modified = False + self._is_invalid = False - _is_modified = False - _is_invalid = False + self._is_nullable = False + self._as_widget = False + self._is_group = False - _any_parent_is_group = None - _log = None + self._any_parent_is_group = None + + # Parent input + self._parent = None + + # States of inputs + self._state = None + self._child_state = None + + # Attributes where values are stored + self.default_value = NOT_SET + self.studio_value = NOT_SET + self.override_value = NOT_SET + + # Log object + self._log = None + + # Only for develop mode + self.defaults_not_set = False + + def initial_attributes(self, input_data, parent, as_widget): + self.set_default_attributes() + + self._parent = parent + self._as_widget = as_widget + + self._is_group = input_data.get("is_group", False) + # TODO not implemented yet + self._is_nullable = input_data.get("is_nullable", False) + + any_parent_is_group = parent.is_group + if not any_parent_is_group: + any_parent_is_group = parent.any_parent_is_group + + self._any_parent_is_group = any_parent_is_group @property def develop_mode(self): @@ -61,7 +94,7 @@ class ConfigObject(AbstractConfigObject): @property def is_modified(self): """Has object any changes that require saving.""" - if self._is_modified: + if self._is_modified or self.defaults_not_set: return True if self.is_overidable: @@ -220,6 +253,7 @@ class InputObject(ConfigObject): if value is NOT_SET: if self.develop_mode: value = self.default_input_value + self.defaults_not_set = True if value is NOT_SET: raise NotImplementedError(( "{} Does not have implemented" @@ -231,7 +265,7 @@ class InputObject(ConfigObject): "Default value is not set. This is implementation BUG." ) - self._default_value = value + self.default_value = value self._has_studio_override = False self._had_studio_override = False self.set_value(value) @@ -391,22 +425,13 @@ class BooleanWidget(QtWidgets.QWidget, InputObject): parent_widget = parent super(BooleanWidget, self).__init__(parent_widget) - self._parent = parent - self._as_widget = as_widget - self._state = None - - self._is_group = input_data.get("is_group", False) - self._is_nullable = input_data.get("is_nullable", False) - - self.default_value = NOT_SET - self.studio_value = NOT_SET - self.override_value = NOT_SET + self.initial_attributes(input_data, parent, as_widget) layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(5) - if not as_widget: + if not self._as_widget: self.key = input_data["key"] if not label_widget: label = input_data["label"] @@ -478,16 +503,7 @@ class NumberWidget(QtWidgets.QWidget, InputObject): parent_widget = parent super(NumberWidget, self).__init__(parent_widget) - self._parent = parent - self._as_widget = as_widget - self._state = None - - self._is_group = input_data.get("is_group", False) - self._is_nullable = input_data.get("is_nullable", False) - - self.default_value = NOT_SET - self.studio_value = NOT_SET - self.override_value = NOT_SET + self.initial_attributes(input_data, parent, as_widget) layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) @@ -564,19 +580,10 @@ class TextWidget(QtWidgets.QWidget, InputObject): parent_widget = parent super(TextWidget, self).__init__(parent_widget) - self._parent = parent - self._as_widget = as_widget - self._state = None - - self._is_group = input_data.get("is_group", False) - self._is_nullable = input_data.get("is_nullable", False) + self.initial_attributes(input_data, parent, as_widget) self.multiline = input_data.get("multiline", False) - self.default_value = NOT_SET - self.studio_value = NOT_SET - self.override_value = NOT_SET - layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(5) @@ -657,16 +664,7 @@ class PathInputWidget(QtWidgets.QWidget, InputObject): parent_widget = parent super(PathInputWidget, self).__init__(parent_widget) - self._parent = parent - self._as_widget = as_widget - self._state = None - - self._is_group = input_data.get("is_group", False) - self._is_nullable = input_data.get("is_nullable", False) - - self.default_value = NOT_SET - self.studio_value = NOT_SET - self.override_value = NOT_SET + self.initial_attributes(input_data, parent, as_widget) layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) @@ -793,22 +791,7 @@ class RawJsonWidget(QtWidgets.QWidget, InputObject): parent_widget = parent super(RawJsonWidget, self).__init__(parent_widget) - self._parent = parent - self._as_widget = as_widget - self._state = None - - any_parent_is_group = parent.is_group - if not any_parent_is_group: - any_parent_is_group = parent.any_parent_is_group - - self._any_parent_is_group = any_parent_is_group - - self._is_group = input_data.get("is_group", False) - self._is_nullable = input_data.get("is_nullable", False) - - self.default_value = NOT_SET - self.studio_value = NOT_SET - self.override_value = NOT_SET + self.initial_attributes(input_data, parent, as_widget) layout = QtWidgets.QVBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) @@ -889,7 +872,10 @@ class ListItem(QtWidgets.QWidget, ConfigObject): def __init__(self, object_type, input_modifiers, config_parent, parent): super(ListItem, self).__init__(parent) + self.set_default_attributes() self._parent = config_parent + self._any_parent_is_group = True + layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(3) @@ -989,20 +975,11 @@ class ListWidget(QtWidgets.QWidget, InputObject): super(ListWidget, self).__init__(parent_widget) self.setObjectName("ListWidget") - self._parent = parent - self._state = None - self._as_widget = as_widget - - self._is_group = input_data.get("is_group", False) - self._is_nullable = input_data.get("is_nullable", False) + self.initial_attributes(input_data, parent, as_widget) self.object_type = input_data["object_type"] self.input_modifiers = input_data.get("input_modifiers") or {} - self.default_value = NOT_SET - self.studio_value = NOT_SET - self.override_value = NOT_SET - self.key = input_data["key"] self.input_fields = [] @@ -1327,22 +1304,7 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): super(ModifiableDict, self).__init__(parent_widget) self.setObjectName("ModifiableDict") - self._parent = parent - self._state = None - self._as_widget = as_widget - - self.default_value = NOT_SET - self.studio_value = NOT_SET - self.override_value = NOT_SET - - any_parent_is_group = parent.is_group - if not any_parent_is_group: - any_parent_is_group = parent.any_parent_is_group - - self._any_parent_is_group = any_parent_is_group - - self._is_group = input_data.get("is_group", False) - self._is_nullable = input_data.get("is_nullable", False) + self.initial_attributes(input_data, parent, as_widget) self.input_fields = [] @@ -1573,18 +1535,7 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): super(DictWidget, self).__init__(parent_widget) self.setObjectName("DictWidget") - self._state = None - self._child_state = None - - self._parent = parent - - any_parent_is_group = parent.is_group - if not any_parent_is_group: - any_parent_is_group = parent.any_parent_is_group - self._any_parent_is_group = any_parent_is_group - - self._is_group = input_data.get("is_group", False) - self._is_nullable = input_data.get("is_nullable", False) + self.initial_attributes(input_data, parent, as_widget) if input_data.get("highlight_content", False): content_state = "hightlighted" @@ -1891,14 +1842,8 @@ class DictInvisible(QtWidgets.QWidget, ConfigObject): super(DictInvisible, self).__init__(parent_widget) self.setObjectName("DictInvisible") - self._parent = parent + self.initial_attributes(input_data, parent, as_widget) - any_parent_is_group = parent.is_group - if not any_parent_is_group: - any_parent_is_group = parent.any_parent_is_group - - self._any_parent_is_group = any_parent_is_group - self._is_group = input_data.get("is_group", False) if self._is_group: raise TypeError("DictInvisible can't be marked as group input.") @@ -2098,7 +2043,6 @@ class DictInvisible(QtWidgets.QWidget, ConfigObject): class PathWidget(QtWidgets.QWidget, ConfigObject): value_changed = QtCore.Signal(object) - platforms = ("windows", "darwin", "linux") platform_labels_mapping = { "windows": "Windows", @@ -2120,30 +2064,17 @@ class PathWidget(QtWidgets.QWidget, ConfigObject): parent_widget = parent super(PathWidget, self).__init__(parent_widget) - self._parent = parent - self._state = None - self._child_state = None - self._as_widget = as_widget - - any_parent_is_group = parent.is_group - if not any_parent_is_group: - any_parent_is_group = parent.any_parent_is_group - self._any_parent_is_group = any_parent_is_group + self.initial_attributes(input_data, parent, as_widget) # This is partial input and dictionary input - if not any_parent_is_group and not as_widget: + if not self.any_parent_is_group and not self._as_widget: self._is_group = True else: self._is_group = False - self._is_nullable = input_data.get("is_nullable", False) self.multiplatform = input_data.get("multiplatform", False) self.multipath = input_data.get("multipath", False) - self.default_value = NOT_SET - self.studio_value = NOT_SET - self.override_value = NOT_SET - self.input_fields = [] if not self.multiplatform and not self.multipath: @@ -2153,7 +2084,7 @@ class PathWidget(QtWidgets.QWidget, ConfigObject): layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(5) - if not as_widget: + if not self._as_widget: self.key = input_data["key"] if not label_widget: label = input_data["label"] @@ -2171,6 +2102,16 @@ class PathWidget(QtWidgets.QWidget, ConfigObject): self.create_gui() + @property + def default_input_value(self): + if self.multiplatform: + return { + platform: "" + for platform in self.platforms + } + else: + return "" + def create_gui(self): if not self.multiplatform and not self.multipath: input_data = {"key": self.key} @@ -2467,14 +2408,9 @@ class DictFormWidget(QtWidgets.QWidget, ConfigObject): parent_widget = parent super(DictFormWidget, self).__init__(parent_widget) - self._parent = parent - - any_parent_is_group = parent.is_group - if not any_parent_is_group: - any_parent_is_group = parent.any_parent_is_group - - self._any_parent_is_group = any_parent_is_group + self.initial_attributes(input_data, parent, as_widget) + self._as_widget = False self._is_group = False self.input_fields = [] From 3cd46bb0f2e7045a406e25c19e23ca9cb00a6a5f Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Sep 2020 13:42:59 +0200 Subject: [PATCH 549/813] configurations lib has constant path to defaults --- pype/configurations/lib.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pype/configurations/lib.py b/pype/configurations/lib.py index 4cd7203626..b555ea0167 100644 --- a/pype/configurations/lib.py +++ b/pype/configurations/lib.py @@ -29,6 +29,9 @@ STUDIO_PROJECT_OVERRIDES_PATH = os.path.join( STUDIO_OVERRIDES_PATH, "project_overrides" ) +# Path to default configurations +DEFAULTS_DIR = os.path.join(os.path.dirname(__file__), "defaults") + # Variable where cache of default configurations are stored _DEFAULT_CONFIGURATIONS = None @@ -36,9 +39,7 @@ _DEFAULT_CONFIGURATIONS = None def default_configuration(): global _DEFAULT_CONFIGURATIONS if _DEFAULT_CONFIGURATIONS is None: - current_dir = os.path.dirname(__file__) - defaults_path = os.path.join(current_dir, "defaults") - _DEFAULT_CONFIGURATIONS = load_jsons_from_dir(defaults_path) + _DEFAULT_CONFIGURATIONS = load_jsons_from_dir(DEFAULTS_DIR) return _DEFAULT_CONFIGURATIONS From 714bd5794327785596964ee05d521a7e5b70c2d3 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Sep 2020 13:59:54 +0200 Subject: [PATCH 550/813] added specific keys as constants --- pype/configurations/lib.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pype/configurations/lib.py b/pype/configurations/lib.py index b555ea0167..c0b783e3fb 100644 --- a/pype/configurations/lib.py +++ b/pype/configurations/lib.py @@ -14,12 +14,14 @@ POP_KEY = "__pop_key__" STUDIO_OVERRIDES_PATH = os.environ["PYPE_CONFIG"] # File where studio's system overrides are stored +SYSTEM_CONFIGURATIONS_KEY = "system_configurations" SYSTEM_CONFIGURATIONS_PATH = os.path.join( - STUDIO_OVERRIDES_PATH, "system_configurations.json" + STUDIO_OVERRIDES_PATH, SYSTEM_CONFIGURATIONS_KEY + ".json" ) # File where studio's default project overrides are stored -PROJECT_CONFIGURATIONS_FILENAME = "project_configurations.json" +PROJECT_CONFIGURATIONS_KEY = "project_configurations" +PROJECT_CONFIGURATIONS_FILENAME = PROJECT_CONFIGURATIONS_KEY + ".json" PROJECT_CONFIGURATIONS_PATH = os.path.join( STUDIO_OVERRIDES_PATH, PROJECT_CONFIGURATIONS_FILENAME ) From ebfa87a3ac2cc57108a87cdec9b11da3f342928d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Sep 2020 14:05:12 +0200 Subject: [PATCH 551/813] removed standalone publisher as not complete yet --- .../projects_schema/1_plugins_gui_schema.json | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json index 5dad665b2d..df0de07a4d 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json @@ -610,21 +610,6 @@ ] } ] - }, { - "type": "dict", - "expandable": true, - "key": "standalonepublisher", - "label": "StandalonePublisher", - "children": [ - { - "type": "dict", - "expandable": true, - "key": "publish", - "label": "Publish plugins", - "is_file": true, - "children": [] - } - ] } ] } From ce198d1c4a3460174c16637eb8b6c04e04083351 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Sep 2020 14:09:26 +0200 Subject: [PATCH 552/813] added possibility to reset defaults --- pype/configurations/lib.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pype/configurations/lib.py b/pype/configurations/lib.py index c0b783e3fb..b8832ceacb 100644 --- a/pype/configurations/lib.py +++ b/pype/configurations/lib.py @@ -38,6 +38,11 @@ DEFAULTS_DIR = os.path.join(os.path.dirname(__file__), "defaults") _DEFAULT_CONFIGURATIONS = None +def reset_default_configurations(): + global _DEFAULT_CONFIGURATIONS + _DEFAULT_CONFIGURATIONS = None + + def default_configuration(): global _DEFAULT_CONFIGURATIONS if _DEFAULT_CONFIGURATIONS is None: From a4a4b5244f07b08099748217c86a4e15057d85e1 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Sep 2020 14:34:26 +0200 Subject: [PATCH 553/813] added ability to save as defaults --- .../global/applications.json | 31 +++--- .../projects_schema/1_plugins_gui_schema.json | 4 + .../config_setting/widgets/base.py | 94 ++++++++++++++++++- 3 files changed, 109 insertions(+), 20 deletions(-) diff --git a/pype/configurations/defaults/system_configurations/global/applications.json b/pype/configurations/defaults/system_configurations/global/applications.json index 8e27f11002..3a74a85468 100644 --- a/pype/configurations/defaults/system_configurations/global/applications.json +++ b/pype/configurations/defaults/system_configurations/global/applications.json @@ -6,34 +6,29 @@ "celaction_local": true, "celaction_remote": true, "harmony_17": true, - "houdini_16": true, - "houdini_17": true, - "houdini_18": true, - "maya_2016": true, "maya_2017": true, "maya_2018": true, "maya_2019": true, "maya_2020": true, - "nukestudio_10.0": true, - "nukestudio_11.0": true, - "nukestudio_11.2": true, - "nukestudio_11.3": true, - "nukestudio_12.0": true, - "nukex_10.0": true, - "nukex_11.0": true, - "nukex_11.2": true, - "nukex_11.3": true, - "nukex_12.0": true, "nuke_10.0": true, - "nuke_11.0": true, "nuke_11.2": true, "nuke_11.3": true, "nuke_12.0": true, - "photoshop_2020": true, + "nukex_10.0": true, + "nukex_11.2": true, + "nukex_11.3": true, + "nukex_12.0": true, + "nukestudio_10.0": true, + "nukestudio_11.2": true, + "nukestudio_11.3": true, + "nukestudio_12.0": true, + "houdini_16": true, + "houdini_16.5": false, + "houdini_17": true, + "houdini_18": true, "premiere_2019": true, "premiere_2020": true, "resolve_16": true, "storyboardpro_7": true, - "unreal_4.24": true, - "houdini_16.5": false + "unreal_4.24": true } \ No newline at end of file diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json index df0de07a4d..c279a6b04a 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json @@ -140,6 +140,10 @@ "is_group": true, "children": [ { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, { "type": "dict-invisible", "key": "ffmpeg_args", "children": [ diff --git a/pype/tools/config_setting/config_setting/widgets/base.py b/pype/tools/config_setting/config_setting/widgets/base.py index 58e53b8c58..d7078e4ab6 100644 --- a/pype/tools/config_setting/config_setting/widgets/base.py +++ b/pype/tools/config_setting/config_setting/widgets/base.py @@ -2,8 +2,12 @@ import os import json from Qt import QtWidgets, QtCore, QtGui from pype.configurations.lib import ( + SYSTEM_CONFIGURATIONS_KEY, SYSTEM_CONFIGURATIONS_PATH, + PROJECT_CONFIGURATIONS_KEY, PROJECT_CONFIGURATIONS_PATH, + DEFAULTS_DIR, + reset_default_configurations, default_configuration, studio_system_configurations, project_configurations_overrides, @@ -145,7 +149,50 @@ class SystemWidget(QtWidgets.QWidget): self._update_values() def _save_as_defaults(self): - print("_save_as_defaults") + output = {} + for item in self.input_fields: + output.update(item.config_value()) + + for key in reversed(self.keys): + _output = {key: output} + output = _output + + all_values = {} + for item in self.input_fields: + all_values.update(item.config_value()) + + for key in reversed(self.keys): + _all_values = {key: all_values} + all_values = _all_values + + # Skip first key + all_values = all_values["system"] + + prject_defaults_dir = os.path.join( + DEFAULTS_DIR, SYSTEM_CONFIGURATIONS_KEY + ) + keys_to_file = lib.file_keys_from_schema(self.schema) + for key_sequence in keys_to_file: + # Skip first key + key_sequence = key_sequence[1:] + subpath = "/".join(key_sequence) + ".json" + + new_values = all_values + for key in key_sequence: + new_values = new_values[key] + + output_path = os.path.join(prject_defaults_dir, subpath) + dirpath = os.path.dirname(output_path) + if not os.path.exists(dirpath): + os.makedirs(dirpath) + + print("Saving data to: ", subpath) + with open(output_path, "w") as file_stream: + json.dump(new_values, file_stream, indent=4) + + reset_default_configurations() + + self._update_values() def _update_values(self): self.ignore_value_changes = True @@ -415,7 +462,50 @@ class ProjectWidget(QtWidgets.QWidget): self.ignore_value_changes = False def _save_as_defaults(self): - print("_save_as_defaults") + output = {} + for item in self.input_fields: + output.update(item.config_value()) + + for key in reversed(self.keys): + _output = {key: output} + output = _output + + all_values = {} + for item in self.input_fields: + all_values.update(item.config_value()) + + for key in reversed(self.keys): + _all_values = {key: all_values} + all_values = _all_values + + # Skip first key + all_values = all_values["project"] + + prject_defaults_dir = os.path.join( + DEFAULTS_DIR, PROJECT_CONFIGURATIONS_KEY + ) + keys_to_file = lib.file_keys_from_schema(self.schema) + for key_sequence in keys_to_file: + # Skip first key + key_sequence = key_sequence[1:] + subpath = "/".join(key_sequence) + ".json" + + new_values = all_values + for key in key_sequence: + new_values = new_values[key] + + output_path = os.path.join(prject_defaults_dir, subpath) + dirpath = os.path.dirname(output_path) + if not os.path.exists(dirpath): + os.makedirs(dirpath) + + print("Saving data to: ", subpath) + with open(output_path, "w") as file_stream: + json.dump(new_values, file_stream, indent=4) + + reset_default_configurations() + + self._update_values() def _save(self): has_invalid = False From 438d456bb55b52158a51859ea347455aefa612a2 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Sep 2020 15:21:12 +0200 Subject: [PATCH 554/813] fix dictionary item initialization --- pype/tools/config_setting/config_setting/widgets/inputs.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 32befd6ef8..bc63e27f5a 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -873,6 +873,7 @@ class ListItem(QtWidgets.QWidget, ConfigObject): super(ListItem, self).__init__(parent) self.set_default_attributes() + self._parent = config_parent self._any_parent_is_group = True @@ -1149,6 +1150,7 @@ class ModifiableDictItem(QtWidgets.QWidget, ConfigObject): def __init__(self, object_type, input_modifiers, config_parent, parent): super(ModifiableDictItem, self).__init__(parent) + self.set_default_attributes() self._parent = config_parent self.is_single = False From ea8ffca0fadbc8122e2a1b55e4712c61bfb5a33b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Sep 2020 15:21:34 +0200 Subject: [PATCH 555/813] modified anatomy widgets to match new initialization --- .../config_setting/widgets/anatomy_inputs.py | 28 +++++++------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index efba588692..4ae7d8d290 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -40,14 +40,11 @@ class AnatomyWidget(QtWidgets.QWidget, ConfigObject): ) super(AnatomyWidget, self).__init__(parent) self.setObjectName("AnatomyWidget") - self._parent = parent + + self.initial_attributes(input_data, parent, as_widget) + self.key = "anatomy" - self._child_state = None - self._state = None - - self._any_parent_is_group = False - self.root_widget = RootsWidget(self) self.templates_widget = TemplatesWidget(self) @@ -214,15 +211,13 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): def __init__(self, parent): super(RootsWidget, self).__init__(parent) self.setObjectName("RootsWidget") - self._parent = parent + + input_data = {"is_group": True} + self.initial_attributes(input_data, parent, False) + self.key = "roots" - self._state = None self._multiroot_state = None - - self._is_group = True - self._any_parent_is_group = False - self.global_is_multiroot = False self.was_multiroot = NOT_SET @@ -546,12 +541,9 @@ class TemplatesWidget(QtWidgets.QWidget, ConfigObject): def __init__(self, parent): super(TemplatesWidget, self).__init__(parent) - self._parent = parent + input_data = {"is_group": True} + self.initial_attributes(input_data, parent, False) - self._state = None - - self._is_group = True - self._any_parent_is_group = False self.key = "templates" body_widget = ExpandingWidget("Templates", self) @@ -634,7 +626,7 @@ class TemplatesWidget(QtWidgets.QWidget, ConfigObject): @property def has_studio_override(self): - return self.value_input.has_studio_override + return self.value_input._has_studio_override @property def child_has_studio_override(self): From f346b80e4d29b25b9406709d26a8bcf74ebceb29 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Sep 2020 15:27:56 +0200 Subject: [PATCH 556/813] set as overriden kinda works on roots widget --- .../config_setting/config_setting/widgets/anatomy_inputs.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index 4ae7d8d290..2c4548df4e 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -173,6 +173,10 @@ class AnatomyWidget(QtWidgets.QWidget, ConfigObject): or self.templates_widget.child_invalid ) + def set_as_overriden(self): + self.root_widget.set_as_overriden() + self.templates_widget.set_as_overriden() + def remove_overrides(self): self.root_widget.remove_overrides() self.templates_widget.remove_overrides() @@ -521,7 +525,7 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): self._is_modified = self.child_modified def set_as_overriden(self): - self._is_overriden = self._was_overriden + self._is_overriden = True self.singleroot_widget.set_as_overriden() self.multiroot_widget.set_as_overriden() From 12fff9477a0d4038a3a54d7c6af144144ad675a5 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Sep 2020 15:31:01 +0200 Subject: [PATCH 557/813] multiroot label does not care about overrides --- .../config_setting/config_setting/widgets/anatomy_inputs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index 2c4548df4e..7dd89838d4 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -399,7 +399,7 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): multiroot_state = self.style_state( self.has_studio_override, False, - self.is_overriden, + False, self.was_multiroot != self.is_multiroot ) if multiroot_state != self._multiroot_state: From c76f9f604c96b82eb26c3fb2b1fd556431803f64 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Sep 2020 15:52:01 +0200 Subject: [PATCH 558/813] anatomy overrides can be unset --- .../config_setting/config_setting/widgets/anatomy_inputs.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index 7dd89838d4..a62b32664e 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -186,7 +186,9 @@ class AnatomyWidget(QtWidgets.QWidget, ConfigObject): self.templates_widget.discard_changes() def overrides(self): - return self.config_value(), True + if self.child_overriden: + return self.config_value(), True + return NOT_SET, False def item_value(self): output = {} From 47f1429214b73aa6db634e3660b7666471a87bb7 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Sep 2020 15:52:41 +0200 Subject: [PATCH 559/813] fixed different cases of set_overrides and removing overrides --- .../config_setting/widgets/anatomy_inputs.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index a62b32664e..306149f9b3 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -510,8 +510,10 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): self.set_multiroot(self.global_is_multiroot) - self.singleroot_widget.remove_overrides() - self.multiroot_widget.remove_overrides() + if self.is_multiroot: + self.multiroot_widget.remove_overrides() + else: + self.singleroot_widget.remove_overrides() def discard_changes(self): self._is_overriden = self._was_overriden @@ -521,8 +523,10 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): else: self.set_multiroot(self.global_is_multiroot) - self.singleroot_widget.discard_changes() - self.multiroot_widget.discard_changes() + if self.is_multiroot: + self.multiroot_widget.discard_changes() + else: + self.singleroot_widget.discard_changes() self._is_modified = self.child_modified From 3759920d3873158ccf0c7b5c1ab1844734b0b869 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Sep 2020 16:31:40 +0200 Subject: [PATCH 560/813] modified api to not break old way of loading presets --- pype/api.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pype/api.py b/pype/api.py index ee0d1ef4a2..c5cd28d4de 100644 --- a/pype/api.py +++ b/pype/api.py @@ -1,8 +1,12 @@ -from .configurations import config +from .configurations.config import ( + system_configurations, + project_configurations +) from pypeapp import ( Logger, Anatomy, project_overrides_dir_path, + config, execute ) @@ -49,6 +53,9 @@ from .lib import ( from .lib import _subprocess as subprocess __all__ = [ + "system_configurations", + "project_configurations", + "Logger", "Anatomy", "project_overrides_dir_path", From 201d0aca14a58c419d13fcf298719ccedfb72721 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Sep 2020 17:17:54 +0200 Subject: [PATCH 561/813] moved anatomy to separate folder --- .../colorspace.json | 0 .../anatomy => project_anatomy}/dataflow.json | 0 .../anatomy => project_anatomy}/roots.json | 0 .../templates.json | 0 .../project_configurations/anatomy/README.md | 0 .../anatomy/colorspace/aces103-cg.json | 46 --------------- .../anatomy/dataflow/aces-exr.json | 58 ------------------- .../anatomy/default.yaml | 29 ---------- 8 files changed, 133 deletions(-) rename pype/configurations/defaults/{project_configurations/anatomy => project_anatomy}/colorspace.json (100%) rename pype/configurations/defaults/{project_configurations/anatomy => project_anatomy}/dataflow.json (100%) rename pype/configurations/defaults/{project_configurations/anatomy => project_anatomy}/roots.json (100%) rename pype/configurations/defaults/{project_configurations/anatomy => project_anatomy}/templates.json (100%) delete mode 100644 pype/configurations/defaults/project_configurations/anatomy/README.md delete mode 100644 pype/configurations/defaults/project_configurations/anatomy/colorspace/aces103-cg.json delete mode 100644 pype/configurations/defaults/project_configurations/anatomy/dataflow/aces-exr.json delete mode 100644 pype/configurations/defaults/project_configurations/anatomy/default.yaml diff --git a/pype/configurations/defaults/project_configurations/anatomy/colorspace.json b/pype/configurations/defaults/project_anatomy/colorspace.json similarity index 100% rename from pype/configurations/defaults/project_configurations/anatomy/colorspace.json rename to pype/configurations/defaults/project_anatomy/colorspace.json diff --git a/pype/configurations/defaults/project_configurations/anatomy/dataflow.json b/pype/configurations/defaults/project_anatomy/dataflow.json similarity index 100% rename from pype/configurations/defaults/project_configurations/anatomy/dataflow.json rename to pype/configurations/defaults/project_anatomy/dataflow.json diff --git a/pype/configurations/defaults/project_configurations/anatomy/roots.json b/pype/configurations/defaults/project_anatomy/roots.json similarity index 100% rename from pype/configurations/defaults/project_configurations/anatomy/roots.json rename to pype/configurations/defaults/project_anatomy/roots.json diff --git a/pype/configurations/defaults/project_configurations/anatomy/templates.json b/pype/configurations/defaults/project_anatomy/templates.json similarity index 100% rename from pype/configurations/defaults/project_configurations/anatomy/templates.json rename to pype/configurations/defaults/project_anatomy/templates.json diff --git a/pype/configurations/defaults/project_configurations/anatomy/README.md b/pype/configurations/defaults/project_configurations/anatomy/README.md deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/pype/configurations/defaults/project_configurations/anatomy/colorspace/aces103-cg.json b/pype/configurations/defaults/project_configurations/anatomy/colorspace/aces103-cg.json deleted file mode 100644 index dd3fca4c2d..0000000000 --- a/pype/configurations/defaults/project_configurations/anatomy/colorspace/aces103-cg.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "resolve": { - - }, - "nukestudio": { - - }, - "nuke": { - "root": { - "colorManagement": "OCIO", - "OCIO_config": "aces_1.0.3", - "workingSpaceLUT": "ACES - ACEScg", - "defaultViewerLUT": "OCIO LUTs", - "monitorLut": "ACES/sRGB", - "int8Lut": "Utility - sRGB - Texture", - "int16Lut": "Utility - sRGB - Texture", - "logLut": "Input - ADX - ADX10", - "floatLut": "ACES - ACES2065-1" - }, - "viewer": { - "viewerProcess": "sRGB (ACES)" - }, - "write": { - "render": { - "colorspace": "ACES - ACES2065-1" - }, - "prerender": { - "colorspace": "ACES - ACES2065-1" - }, - "still": { - "colorspace": "Utility - Curve - sRGB" - } - }, - "read": { - "[^-a-zA-Z0-9](beauty)[^-a-zA-Z0-9]": "lin_srgb", - "[^-a-zA-Z0-9](P|N|Z|crypto)[^-a-zA-Z0-9]": "raw", - "[^-a-zA-Z0-9](plateRef)[^-a-zA-Z0-9]": "crv_srgb" - } - }, - "maya": { - - }, - "houdini": { - - } -} diff --git a/pype/configurations/defaults/project_configurations/anatomy/dataflow/aces-exr.json b/pype/configurations/defaults/project_configurations/anatomy/dataflow/aces-exr.json deleted file mode 100644 index 75846c0bd6..0000000000 --- a/pype/configurations/defaults/project_configurations/anatomy/dataflow/aces-exr.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "nuke": { - "nodes": { - "connected": true, - "modifymetadata": { - "_id": "connect_metadata", - "_previous": "ENDING", - "metadata.set.pype_studio_name": "{PYPE_STUDIO_NAME}", - "metadata.set.avalon_project_name": "{AVALON_PROJECT}", - "metadata.set.avalon_project_code": "{PYPE_STUDIO_CODE}", - "metadata.set.avalon_asset_name": "{AVALON_ASSET}" - }, - "crop": { - "_id": "connect_crop", - "_previous": "connect_metadata", - "box": [ - "{metadata.crop.x}", - "{metadata.crop.y}", - "{metadata.crop.right}", - "{metadata.crop.top}" - ] - }, - "write": { - "render": { - "_id": "output_write", - "_previous": "connect_crop", - "file_type": "exr", - "datatype": "16 bit half", - "compression": "Zip (1 scanline)", - "create_directories": true, - "autocrop": true, - "tile_color": "0xff0000ff", - "channels": "rgb" - }, - "prerender": { - "_id": "output_write", - "_previous": "connect_crop", - "file_type": "exr", - "datatype": "16 bit half", - "compression": "Zip (1 scanline)", - "create_directories": true, - "autocrop": false, - "tile_color": "0xc9892aff", - "channels": "rgba" - }, - "still": { - "_previous": "connect_crop", - "channels": "rgba", - "file_type": "tiff", - "datatype": "16 bit", - "compression": "LZW", - "create_directories": true, - "tile_color": "0x4145afff" - } - } - } - } -} diff --git a/pype/configurations/defaults/project_configurations/anatomy/default.yaml b/pype/configurations/defaults/project_configurations/anatomy/default.yaml deleted file mode 100644 index 381aa05d4a..0000000000 --- a/pype/configurations/defaults/project_configurations/anatomy/default.yaml +++ /dev/null @@ -1,29 +0,0 @@ - -version_padding: 3 -version: "v{version:0>{@version_padding}}" -frame_padding: 4 -frame: "{frame:0>{@frame_padding}}" - -work: - folder: "{root}/{project[name]}/{hierarchy}/{asset}/work/{task}" - file: "{project[code]}_{asset}_{task}_{@version}<_{comment}>.{ext}" - path: "{@folder}/{@file}" - -render: - folder: "{root}/{project[name]}/{hierarchy}/{asset}/publish/render/{subset}/{@version}" - file: "{project[code]}_{asset}_{subset}_{@version}<_{output}><.{@frame}>.{representation}" - path: "{@folder}/{@file}" - -texture: - path: "{root}/{project[name]}/{hierarchy}/{asset}/publish/{family}/{subset}" - -publish: - folder: "{root}/{project[name]}/{hierarchy}/{asset}/publish/{family}/{subset}/{@version}" - file: "{project[code]}_{asset}_{subset}_{@version}<_{output}><.{@frame}>.{representation}" - path: "{@folder}/{@file}" - thumbnail: "{thumbnail_root}/{project[name]}/{_id}_{thumbnail_type}{ext}" - -master: - folder: "{root}/{project[name]}/{hierarchy}/{asset}/publish/{family}/{subset}/master" - file: "{project[code]}_{asset}_{subset}_master<_{output}><.{frame}>.{representation}" - path: "{@folder}/{@file}" From 5971468b93a22627ed08c28313642bfd7c88323d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Sep 2020 17:21:42 +0200 Subject: [PATCH 562/813] anatomy data are more schema based --- .../config_setting/widgets/anatomy_inputs.py | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index 306149f9b3..ed73ad6468 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -43,10 +43,19 @@ class AnatomyWidget(QtWidgets.QWidget, ConfigObject): self.initial_attributes(input_data, parent, as_widget) - self.key = "anatomy" + self.key = input_data["key"] - self.root_widget = RootsWidget(self) - self.templates_widget = TemplatesWidget(self) + children_data = input_data["children"] + roots_input_data = {} + templates_input_data = {} + for child in children_data: + if child["type"] == "anatomy_roots": + roots_input_data = child + elif child["type"] == "anatomy_templates": + templates_input_data = child + + self.root_widget = RootsWidget(roots_input_data, self) + self.templates_widget = TemplatesWidget(templates_input_data, self) self.setAttribute(QtCore.Qt.WA_StyledBackground) @@ -214,14 +223,14 @@ class AnatomyWidget(QtWidgets.QWidget, ConfigObject): class RootsWidget(QtWidgets.QWidget, ConfigObject): value_changed = QtCore.Signal(object) - def __init__(self, parent): + def __init__(self, input_data, parent): super(RootsWidget, self).__init__(parent) self.setObjectName("RootsWidget") input_data = {"is_group": True} self.initial_attributes(input_data, parent, False) - self.key = "roots" + self.key = input_data["key"] self._multiroot_state = None self.global_is_multiroot = False @@ -548,13 +557,13 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): class TemplatesWidget(QtWidgets.QWidget, ConfigObject): value_changed = QtCore.Signal(object) - def __init__(self, parent): + def __init__(self, input_data, parent): super(TemplatesWidget, self).__init__(parent) input_data = {"is_group": True} self.initial_attributes(input_data, parent, False) - self.key = "templates" + self.key = input_data["key"] body_widget = ExpandingWidget("Templates", self) content_widget = QtWidgets.QWidget(body_widget) From 4bd3c8716bfdd5e4e7d55521d719a1569bc973f1 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Sep 2020 17:21:53 +0200 Subject: [PATCH 563/813] wrapped project data more inside --- .../projects_schema/0_project_gui_schema.json | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/0_project_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/0_project_gui_schema.json index 52565640bc..3d7cac68fd 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/0_project_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/0_project_gui_schema.json @@ -4,7 +4,7 @@ "children": [ { "type": "anatomy", - "key": "anatomy", + "key": "project_anatomy", "children": [ { "type": "anatomy_roots", @@ -17,9 +17,15 @@ } ] }, { - "type": "schema", + "type": "dict-invisible", + "key": "project_configurations", "children": [ - "1_plugins_gui_schema" + { + "type": "schema", + "children": [ + "1_plugins_gui_schema" + ] + } ] } ] From e359256b6ebcc100be3e8a83ebce3356a342abf4 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Sep 2020 17:22:39 +0200 Subject: [PATCH 564/813] configurations has also functions for getting anatomy --- pype/configurations/lib.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/pype/configurations/lib.py b/pype/configurations/lib.py index b8832ceacb..abd848f95c 100644 --- a/pype/configurations/lib.py +++ b/pype/configurations/lib.py @@ -26,6 +26,12 @@ PROJECT_CONFIGURATIONS_PATH = os.path.join( STUDIO_OVERRIDES_PATH, PROJECT_CONFIGURATIONS_FILENAME ) +PROJECT_ANATOMY_KEY = "project_anatomy" +PROJECT_ANATOMY_FILENAME = PROJECT_ANATOMY_KEY + ".json" +PROJECT_ANATOMY_PATH = os.path.join( + STUDIO_OVERRIDES_PATH, PROJECT_ANATOMY_FILENAME +) + # Folder where studio's per project overrides are stored STUDIO_PROJECT_OVERRIDES_PATH = os.path.join( STUDIO_OVERRIDES_PATH, "project_overrides" @@ -167,6 +173,12 @@ def studio_project_configurations(): return {} +def studio_project_anatomy(): + if os.path.exists(PROJECT_ANATOMY_PATH): + return load_json(PROJECT_ANATOMY_PATH) + return {} + + def path_to_project_overrides(project_name): return os.path.join( STUDIO_PROJECT_OVERRIDES_PATH, @@ -175,6 +187,14 @@ def path_to_project_overrides(project_name): ) +def path_to_project_anatomy(project_name): + return os.path.join( + STUDIO_PROJECT_OVERRIDES_PATH, + project_name, + PROJECT_ANATOMY_FILENAME + ) + + def project_configurations_overrides(project_name): if not project_name: return {} @@ -185,6 +205,16 @@ def project_configurations_overrides(project_name): return load_json(path_to_json) +def project_anatomy_overrides(project_name): + if not project_name: + return {} + + path_to_json = path_to_project_anatomy(project_name) + if not os.path.exists(path_to_json): + return {} + return load_json(path_to_json) + + def merge_overrides(global_dict, override_dict): if OVERRIDEN_KEY in override_dict: overriden_keys = set(override_dict.pop(OVERRIDEN_KEY)) From f5736a7e9ab1e7b7ea537df526c63aea6509f503 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Sep 2020 17:23:02 +0200 Subject: [PATCH 565/813] anatomy loading/saving is in project widget separated --- .../config_setting/widgets/base.py | 100 +++++++++++++----- 1 file changed, 75 insertions(+), 25 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/base.py b/pype/tools/config_setting/config_setting/widgets/base.py index d7078e4ab6..6d25357fc9 100644 --- a/pype/tools/config_setting/config_setting/widgets/base.py +++ b/pype/tools/config_setting/config_setting/widgets/base.py @@ -6,13 +6,23 @@ from pype.configurations.lib import ( SYSTEM_CONFIGURATIONS_PATH, PROJECT_CONFIGURATIONS_KEY, PROJECT_CONFIGURATIONS_PATH, + PROJECT_ANATOMY_KEY, + PROJECT_ANATOMY_PATH, + DEFAULTS_DIR, + reset_default_configurations, default_configuration, + studio_system_configurations, + studio_project_configurations, + studio_project_anatomy, + project_configurations_overrides, + project_anatomy_overrides, + path_to_project_overrides, - studio_project_configurations + path_to_project_anatomy ) from .widgets import UnsavedChangesDialog from . import lib @@ -448,13 +458,22 @@ class ProjectWidget(QtWidgets.QWidget): def _on_project_change(self): project_name = self.project_list_widget.project_name() if project_name is None: - _overrides = lib.NOT_SET + _project_overrides = lib.NOT_SET + _project_anatomy = lib.NOT_SET self.is_overidable = False else: - _overrides = project_configurations_overrides(project_name) + _project_overrides = project_configurations_overrides(project_name) + _project_anatomy = project_anatomy_overrides(project_name) self.is_overidable = True - overrides = {"project": lib.convert_overrides_to_gui_data(_overrides)} + overrides = {"project": { + PROJECT_CONFIGURATIONS_KEY: lib.convert_overrides_to_gui_data( + _project_overrides + ), + PROJECT_ANATOMY_KEY: lib.convert_overrides_to_gui_data( + _project_anatomy + ) + }} self.project_name = project_name self.ignore_value_changes = True for item in self.input_fields: @@ -481,9 +500,6 @@ class ProjectWidget(QtWidgets.QWidget): # Skip first key all_values = all_values["project"] - prject_defaults_dir = os.path.join( - DEFAULTS_DIR, PROJECT_CONFIGURATIONS_KEY - ) keys_to_file = lib.file_keys_from_schema(self.schema) for key_sequence in keys_to_file: # Skip first key @@ -494,7 +510,7 @@ class ProjectWidget(QtWidgets.QWidget): for key in key_sequence: new_values = new_values[key] - output_path = os.path.join(prject_defaults_dir, subpath) + output_path = os.path.join(DEFAULTS_DIR, subpath) dirpath = os.path.dirname(output_path) if not os.path.exists(dirpath): os.makedirs(dirpath) @@ -533,7 +549,7 @@ class ProjectWidget(QtWidgets.QWidget): return if self.project_name is None: - self._save_defaults() + self._save_studio_overrides() else: self._save_overrides() @@ -548,20 +564,40 @@ class ProjectWidget(QtWidgets.QWidget): data.get("project") or {} ) - overrides_json_path = path_to_project_overrides( + # Saving overrides data + project_overrides_data = output_data.get( + PROJECT_CONFIGURATIONS_KEY, {} + ) + project_overrides_json_path = path_to_project_overrides( self.project_name ) - dirpath = os.path.dirname(overrides_json_path) + dirpath = os.path.dirname(project_overrides_json_path) if not os.path.exists(dirpath): os.makedirs(dirpath) - print("Saving data to:", overrides_json_path) - with open(overrides_json_path, "w") as file_stream: - json.dump(output_data, file_stream, indent=4) + print("Saving data to:", project_overrides_json_path) + with open(project_overrides_json_path, "w") as file_stream: + json.dump(project_overrides_data, file_stream, indent=4) + # Saving anatomy data + project_anatomy_data = output_data.get( + PROJECT_ANATOMY_KEY, {} + ) + project_anatomy_json_path = path_to_project_anatomy( + self.project_name + ) + dirpath = os.path.dirname(project_anatomy_json_path) + if not os.path.exists(dirpath): + os.makedirs(dirpath) + + print("Saving data to:", project_anatomy_json_path) + with open(project_anatomy_json_path, "w") as file_stream: + json.dump(project_anatomy_data, file_stream, indent=4) + + # Refill values with overrides self._on_project_change() - def _save_defaults(self): + def _save_studio_overrides(self): data = {} for input_field in self.input_fields: value, is_group = input_field.studio_overrides() @@ -572,30 +608,44 @@ class ProjectWidget(QtWidgets.QWidget): data.get("project", {}) ) + # Project overrides data + project_overrides_data = output_data.get( + PROJECT_CONFIGURATIONS_KEY, {} + ) dirpath = os.path.dirname(PROJECT_CONFIGURATIONS_PATH) if not os.path.exists(dirpath): os.makedirs(dirpath) print("Saving data to:", PROJECT_CONFIGURATIONS_PATH) - try: - with open(PROJECT_CONFIGURATIONS_PATH, "w") as file_stream: - json.dump(output_data, file_stream, indent=4) - except Exception as exc: - print(output_data) - raise + with open(PROJECT_CONFIGURATIONS_PATH, "w") as file_stream: + json.dump(project_overrides_data, file_stream, indent=4) + # Project Anatomy data + project_anatomy_data = output_data.get( + PROJECT_ANATOMY_KEY, {} + ) + dirpath = os.path.dirname(PROJECT_ANATOMY_PATH) + if not os.path.exists(dirpath): + os.makedirs(dirpath) + + print("Saving data to:", PROJECT_ANATOMY_PATH) + with open(PROJECT_ANATOMY_PATH, "w") as file_stream: + json.dump(project_anatomy_data, file_stream, indent=4) + + # Update saved values self._update_values() def _update_values(self): self.ignore_value_changes = True - default_values = { - "project": default_configuration()["project_configurations"] - } + default_values = {"project": default_configuration()} for input_field in self.input_fields: input_field.update_default_values(default_values) - studio_values = {"project": studio_project_configurations()} + studio_values = {"project": { + PROJECT_CONFIGURATIONS_KEY: studio_project_configurations(), + PROJECT_ANATOMY_KEY: studio_project_anatomy() + }} for input_field in self.input_fields: input_field.update_studio_values(studio_values) From 5eb25c7ae3ebdfe7fe66729c2f27a840f6a00dcd Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Sep 2020 17:23:28 +0200 Subject: [PATCH 566/813] overrides are not saved to path from PYPE_CONFIG but to PYPE_PROJECT_CONFIGS --- pype/configurations/lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/configurations/lib.py b/pype/configurations/lib.py index abd848f95c..9ef782ca6a 100644 --- a/pype/configurations/lib.py +++ b/pype/configurations/lib.py @@ -11,7 +11,7 @@ OVERRIDEN_KEY = "__overriden_keys__" POP_KEY = "__pop_key__" # Folder where studio overrides are stored -STUDIO_OVERRIDES_PATH = os.environ["PYPE_CONFIG"] +STUDIO_OVERRIDES_PATH = os.environ["PYPE_PROJECT_CONFIGS"] # File where studio's system overrides are stored SYSTEM_CONFIGURATIONS_KEY = "system_configurations" From 4d05ebaa71d4cc6a2f0194e610e94e01e07f12fc Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Sep 2020 17:26:29 +0200 Subject: [PATCH 567/813] fixed roots and templates input data --- .../config_setting/config_setting/widgets/anatomy_inputs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index ed73ad6468..b034bc1763 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -227,7 +227,7 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): super(RootsWidget, self).__init__(parent) self.setObjectName("RootsWidget") - input_data = {"is_group": True} + input_data["is_group"] = True self.initial_attributes(input_data, parent, False) self.key = input_data["key"] @@ -560,7 +560,7 @@ class TemplatesWidget(QtWidgets.QWidget, ConfigObject): def __init__(self, input_data, parent): super(TemplatesWidget, self).__init__(parent) - input_data = {"is_group": True} + input_data["is_group"] = True self.initial_attributes(input_data, parent, False) self.key = input_data["key"] From 8a1c164f51efa34dfb5e7a964effdfbea09fcce4 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Sep 2020 18:07:53 +0200 Subject: [PATCH 568/813] changed how the margins of main window are --- pype/tools/config_setting/config_setting/widgets/base.py | 4 ++-- pype/tools/config_setting/config_setting/widgets/window.py | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/base.py b/pype/tools/config_setting/config_setting/widgets/base.py index 6d25357fc9..a0a470fc97 100644 --- a/pype/tools/config_setting/config_setting/widgets/base.py +++ b/pype/tools/config_setting/config_setting/widgets/base.py @@ -74,7 +74,7 @@ class SystemWidget(QtWidgets.QWidget): footer_layout.addWidget(save_btn, 0) layout = QtWidgets.QVBoxLayout(self) - layout.setContentsMargins(5, 0, 5, 0) + layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) self.setLayout(layout) @@ -400,7 +400,7 @@ class ProjectWidget(QtWidgets.QWidget): configurations_widget = QtWidgets.QWidget() configurations_layout = QtWidgets.QVBoxLayout(configurations_widget) - configurations_layout.setContentsMargins(5, 0, 5, 0) + configurations_layout.setContentsMargins(0, 0, 0, 0) configurations_layout.setSpacing(0) configurations_layout.addWidget(scroll_widget, 1) diff --git a/pype/tools/config_setting/config_setting/widgets/window.py b/pype/tools/config_setting/config_setting/widgets/window.py index f8da9a196e..5c7a35fa52 100644 --- a/pype/tools/config_setting/config_setting/widgets/window.py +++ b/pype/tools/config_setting/config_setting/widgets/window.py @@ -8,18 +8,19 @@ class MainWidget(QtWidgets.QWidget): def __init__(self, develop, parent=None): super(MainWidget, self).__init__(parent) + self.setObjectName("MainWidget") self.resize(self.widget_width, self.widget_height) header_tab_widget = QtWidgets.QTabWidget(parent=self) - studio_widget = SystemWidget(develop) - project_widget = ProjectWidget(develop) + studio_widget = SystemWidget(develop, header_tab_widget) + project_widget = ProjectWidget(develop, header_tab_widget) header_tab_widget.addTab(studio_widget, "System") header_tab_widget.addTab(project_widget, "Project") layout = QtWidgets.QVBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) + layout.setContentsMargins(5, 5, 5, 5) layout.setSpacing(0) layout.addWidget(header_tab_widget) From 375188a5587ca6b36719df56af104fefb356855d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Sep 2020 18:08:06 +0200 Subject: [PATCH 569/813] modified tab style --- .../config_setting/style/style.css | 49 ++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/pype/tools/config_setting/config_setting/style/style.css b/pype/tools/config_setting/config_setting/style/style.css index 73c1854bee..5b6821a85d 100644 --- a/pype/tools/config_setting/config_setting/style/style.css +++ b/pype/tools/config_setting/config_setting/style/style.css @@ -90,7 +90,7 @@ QPushButton[btn-type="expand-toggle"] { } #GroupWidget { - border: 1px solid #aaaaaa; + border-bottom: 1px solid #1d272f; } #ProjectListWidget QListView { @@ -148,6 +148,53 @@ QPushButton[btn-type="expand-toggle"] { #SideLineWidget[state="child-overriden-modified"] {border-color: #106aa2;} #SideLineWidget[state="child-overriden-modified"]:hover {border-color: #137cbd;} +#MainWidget { + background: #141a1f; +} + +QTabWidget::pane { + border-top-style: none; +} + +QTabBar { + background: transparent; +} + +QTabBar::tab { + border-top-left-radius: 4px; + border-top-right-radius: 4px; + padding: 5px; +} + +QTabBar::tab:selected { + background: #293742; + border-color: #9B9B9B; + border-bottom-color: #C2C7CB; +} + +QTabBar::tab:!selected { + margin-top: 2px; + background: #1d272f; +} + +QTabBar::tab:!selected:hover { + background: #3b4f5e; +} + + + +QTabBar::tab:first:selected { + margin-left: 0; +} + +QTabBar::tab:last:selected { + margin-right: 0; +} + +QTabBar::tab:only-one { + margin: 0; +} + QScrollBar:horizontal { height: 15px; margin: 3px 15px 3px 15px; From 529570c906ec6f7465206039e5bf7d47310fb74c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Sep 2020 18:44:57 +0200 Subject: [PATCH 570/813] small changes --- .../defaults/project_anatomy/templates.json | 7 +------ .../project_configurations/plugins/global/publish.json | 3 +-- pype/tools/config_setting/config_setting/style/style.css | 8 ++++---- pype/tools/config_setting/config_setting/widgets/base.py | 2 +- 4 files changed, 7 insertions(+), 13 deletions(-) diff --git a/pype/configurations/defaults/project_anatomy/templates.json b/pype/configurations/defaults/project_anatomy/templates.json index 23e9f308f2..0fff0265b3 100644 --- a/pype/configurations/defaults/project_anatomy/templates.json +++ b/pype/configurations/defaults/project_anatomy/templates.json @@ -3,33 +3,28 @@ "version": "v{version:0>{@version_padding}}", "frame_padding": 4, "frame": "{frame:0>{@frame_padding}}", - "work": { "folder": "{root}/{project[name]}/{hierarchy}/{asset}/work/{task}", "file": "{project[code]}_{asset}_{task}_{@version}<_{comment}>.{ext}", "path": "{@folder}/{@file}" }, - "render": { "folder": "{root}/{project[name]}/{hierarchy}/{asset}/publish/render/{subset}/{@version}", "file": "{project[code]}_{asset}_{subset}_{@version}<_{output}><.{@frame}>.{representation}", "path": "{@folder}/{@file}" }, - "texture": { "path": "{root}/{project[name]}/{hierarchy}/{asset}/publish/{family}/{subset}" }, - "publish": { "folder": "{root}/{project[name]}/{hierarchy}/{asset}/publish/{family}/{subset}/{@version}", "file": "{project[code]}_{asset}_{subset}_{@version}<_{output}><.{@frame}>.{representation}", "path": "{@folder}/{@file}", "thumbnail": "{thumbnail_root}/{project[name]}/{_id}_{thumbnail_type}{ext}" }, - "master": { "folder": "{root}/{project[name]}/{hierarchy}/{asset}/publish/{family}/{subset}/master", "file": "{project[code]}_{asset}_{subset}_master<_{output}><.{frame}>.{representation}", "path": "{@folder}/{@file}" } -} +} \ No newline at end of file diff --git a/pype/configurations/defaults/project_configurations/plugins/global/publish.json b/pype/configurations/defaults/project_configurations/plugins/global/publish.json index d531f1aa47..b946ac4b32 100644 --- a/pype/configurations/defaults/project_configurations/plugins/global/publish.json +++ b/pype/configurations/defaults/project_configurations/plugins/global/publish.json @@ -75,7 +75,6 @@ ] }, "IntegrateAssetNew": { - "enabled": true, "template_name_profiles": { "publish": { "families": [], @@ -96,4 +95,4 @@ "deadline_pool": "", "deadline_group": "" } -} +} \ No newline at end of file diff --git a/pype/tools/config_setting/config_setting/style/style.css b/pype/tools/config_setting/config_setting/style/style.css index 5b6821a85d..f6dd354935 100644 --- a/pype/tools/config_setting/config_setting/style/style.css +++ b/pype/tools/config_setting/config_setting/style/style.css @@ -44,9 +44,9 @@ QToolButton { QLabel { background: transparent; - color: #808080; + color: #7390a5; } -QLabel:hover {color: #999999;} +QLabel:hover {color: #839caf;} QLabel[state="studio"] {color: #bfccd6;} QLabel[state="studio"]:hover {color: #ffffff;} @@ -122,7 +122,7 @@ QPushButton[btn-type="expand-toggle"] { #SideLineWidget { background-color: #31424e; border-style: solid; - border-color: #808080; + border-color: #3b4f5e; border-left-width: 3px; border-bottom-width: 0px; border-right-width: 0px; @@ -130,7 +130,7 @@ QPushButton[btn-type="expand-toggle"] { } #SideLineWidget:hover { - border-color: #999999; + border-color: #58768d; } #SideLineWidget[state="child-studio"] {border-color: #455c6e;} diff --git a/pype/tools/config_setting/config_setting/widgets/base.py b/pype/tools/config_setting/config_setting/widgets/base.py index a0a470fc97..16192aadf3 100644 --- a/pype/tools/config_setting/config_setting/widgets/base.py +++ b/pype/tools/config_setting/config_setting/widgets/base.py @@ -250,7 +250,7 @@ class ProjectListWidget(QtWidgets.QWidget): self.setObjectName("ProjectListWidget") label_widget = QtWidgets.QLabel("Projects") - + label_widget.setProperty("state", "studio") project_list = ProjectListView(self) project_list.setModel(QtGui.QStandardItemModel()) From e9737d9b3a644a1958c66e18dec34101bb647b7d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Sep 2020 19:01:27 +0200 Subject: [PATCH 571/813] fix default_values for pathwidget --- pype/tools/config_setting/config_setting/widgets/inputs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index bc63e27f5a..4a9dd28df1 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -2183,10 +2183,10 @@ class PathWidget(QtWidgets.QWidget, ConfigObject): self._had_studio_override = False if not self.multiplatform: - self.input_fields[0].update_studio_values(value) + self.input_fields[0].update_default_values(value) else: for input_field in self.input_fields: - input_field.update_studio_values(value) + input_field.update_default_values(value) def update_studio_values(self, parent_values): self._state = None From 80c091c7fd36dafcc439b15c6e9774322d0418a4 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Sep 2020 19:01:51 +0200 Subject: [PATCH 572/813] project overrides are not stored in subfolder --- pype/configurations/lib.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/pype/configurations/lib.py b/pype/configurations/lib.py index 9ef782ca6a..7e24d25483 100644 --- a/pype/configurations/lib.py +++ b/pype/configurations/lib.py @@ -32,11 +32,6 @@ PROJECT_ANATOMY_PATH = os.path.join( STUDIO_OVERRIDES_PATH, PROJECT_ANATOMY_FILENAME ) -# Folder where studio's per project overrides are stored -STUDIO_PROJECT_OVERRIDES_PATH = os.path.join( - STUDIO_OVERRIDES_PATH, "project_overrides" -) - # Path to default configurations DEFAULTS_DIR = os.path.join(os.path.dirname(__file__), "defaults") @@ -181,7 +176,7 @@ def studio_project_anatomy(): def path_to_project_overrides(project_name): return os.path.join( - STUDIO_PROJECT_OVERRIDES_PATH, + STUDIO_OVERRIDES_PATH, project_name, PROJECT_CONFIGURATIONS_FILENAME ) @@ -189,7 +184,7 @@ def path_to_project_overrides(project_name): def path_to_project_anatomy(project_name): return os.path.join( - STUDIO_PROJECT_OVERRIDES_PATH, + STUDIO_OVERRIDES_PATH, project_name, PROJECT_ANATOMY_FILENAME ) From 098c1bed80f30bcc0dc797b4a43f7e42fa99ff1c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Sep 2020 19:06:59 +0200 Subject: [PATCH 573/813] fix roots fillings --- .../config_setting/config_setting/widgets/anatomy_inputs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index b034bc1763..573f48409d 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -393,9 +393,9 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): self.set_multiroot(is_multiroot) if is_multiroot: - self._is_overriden = parent_values is not NOT_SET + self._is_overriden = value is not NOT_SET self._was_overriden = bool(self._is_overriden) - self.multiroot_widget.apply_overrides(parent_values) + self.multiroot_widget.apply_overrides(value) else: self._is_overriden = value is not NOT_SET self._was_overriden = bool(self._is_overriden) From 026358a2ea3b866bda7060a068667fd3711d5f10 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Sep 2020 19:07:19 +0200 Subject: [PATCH 574/813] change order of filling values logic --- pype/tools/config_setting/config_setting/widgets/inputs.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 4a9dd28df1..1280b39904 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -1054,10 +1054,10 @@ class ListWidget(QtWidgets.QWidget, InputObject): # Set text if entered text is not None # else (when add button clicked) trigger `_on_value_change` if value is not None: - if not self._has_studio_override: - item_widget.update_default_values(value) - elif self._is_overriden: + if self._is_overriden: item_widget.apply_overrides(value) + elif not self._has_studio_override: + item_widget.update_default_values(value) else: item_widget.update_studio_values(value) self.hierarchical_style_update() From 35bee377dd6b8c19ee31c62d4ca56510239e08e0 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Sep 2020 19:07:39 +0200 Subject: [PATCH 575/813] change logic of roots changes for default, studio and overrides --- .../config_setting/widgets/anatomy_inputs.py | 42 ++++++++++++------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index 573f48409d..ec6da20c72 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -233,7 +233,8 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): self.key = input_data["key"] self._multiroot_state = None - self.global_is_multiroot = False + self.default_is_multiroot = False + self.studio_is_multiroot = False self.was_multiroot = NOT_SET checkbox_widget = QtWidgets.QWidget(self) @@ -317,7 +318,7 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): is_multiroot = True break - self.global_is_multiroot = is_multiroot + self.default_is_multiroot = is_multiroot self.was_multiroot = is_multiroot self.set_multiroot(is_multiroot) @@ -346,24 +347,25 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): else: value = NOT_SET - is_multiroot = False - if isinstance(value, dict): - for _value in value.values(): - if isinstance(_value, dict): - is_multiroot = True - break - - self.global_is_multiroot = is_multiroot - self.was_multiroot = is_multiroot - self.set_multiroot(is_multiroot) - if value is NOT_SET: + is_multiroot = self.default_is_multiroot + self.studio_is_multiroot = NOT_SET self._has_studio_override = False self._had_studio_override = False else: + is_multiroot = False + if isinstance(value, dict): + for _value in value.values(): + if isinstance(_value, dict): + is_multiroot = True + break + self.studio_is_multiroot = is_multiroot self._has_studio_override = True self._had_studio_override = True + self.was_multiroot = is_multiroot + self.set_multiroot(is_multiroot) + if is_multiroot: self.multiroot_widget.update_studio_values(value) else: @@ -380,7 +382,9 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): value = parent_values.get(self.key, value) if value is NOT_SET: - is_multiroot = self.global_is_multiroot + is_multiroot = self.studio_is_multiroot + if is_multiroot is NOT_SET: + is_multiroot = self.default_is_multiroot else: is_multiroot = False if isinstance(value, dict): @@ -517,7 +521,10 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): self._is_overriden = False self._is_modified = False - self.set_multiroot(self.global_is_multiroot) + if self.studio_is_multiroot is NOT_SET: + self.set_multiroot(self.default_is_multiroot) + else: + self.set_multiroot(self.studio_is_multiroot) if self.is_multiroot: self.multiroot_widget.remove_overrides() @@ -530,7 +537,10 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): if self._is_overriden: self.set_multiroot(self.was_multiroot) else: - self.set_multiroot(self.global_is_multiroot) + if self.studio_is_multiroot is NOT_SET: + self.set_multiroot(self.default_is_multiroot) + else: + self.set_multiroot(self.studio_is_multiroot) if self.is_multiroot: self.multiroot_widget.discard_changes() From 3351264c7c1028eac98e665f4747687ff6617e32 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Sep 2020 19:21:28 +0200 Subject: [PATCH 576/813] a little bit better changing from multi to single root --- .../config_setting/widgets/anatomy_inputs.py | 32 +++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index ec6da20c72..ed104128a0 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -447,7 +447,7 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): self._state = state def _on_multiroot_checkbox(self): - self.set_multiroot(self.is_multiroot) + self.set_multiroot() def _on_value_change(self, item=None): if self.ignore_value_changes: @@ -471,9 +471,37 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): self.value_changed.emit(self) + def _from_single_to_multi(self): + single_value = self.singleroot_widget.item_value() + mutli_value = self.multiroot_widget.item_value() + first_key = None + for key in mutli_value.keys(): + first_key = key + break + + if first_key is None: + first_key = "" + + mutli_value[first_key] = single_value + + self.multiroot_widget.set_value(mutli_value) + + def _from_multi_to_single(self): + mutli_value = self.multiroot_widget.item_value() + first_key = None + for value in mutli_value.values(): + single_value = value + break + + self.singleroot_widget.set_value(single_value) + def set_multiroot(self, is_multiroot=None): if is_multiroot is None: - is_multiroot = not self.is_multiroot + is_multiroot = self.is_multiroot + if is_multiroot: + self._from_single_to_multi() + else: + self._from_multi_to_single() if is_multiroot != self.is_multiroot: self.multiroot_checkbox.setChecked(is_multiroot) From 93c391a9d4f084b2db79fccb9c13d3f7d331f904 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Sep 2020 19:27:01 +0200 Subject: [PATCH 577/813] changed labels of actions --- .../config_setting/config_setting/widgets/anatomy_inputs.py | 1 - pype/tools/config_setting/config_setting/widgets/inputs.py | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index ed104128a0..75ee12ee02 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -488,7 +488,6 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): def _from_multi_to_single(self): mutli_value = self.multiroot_widget.item_value() - first_key = None for value in mutli_value.values(): single_value = value break diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 1280b39904..c7f817a48e 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -201,7 +201,7 @@ class ConfigObject(AbstractConfigObject): and not self.is_overriden and not self.any_parent_is_group ): - action = QtWidgets.QAction("Set as overriden") + action = QtWidgets.QAction("Set project override") actions_mapping[action] = self._set_as_overriden menu.addAction(action) @@ -210,7 +210,7 @@ class ConfigObject(AbstractConfigObject): and (self.is_overriden or self.child_overriden) ): # TODO better label - action = QtWidgets.QAction("Remove override") + action = QtWidgets.QAction("Remove project override") actions_mapping[action] = self._remove_overrides menu.addAction(action) From 17362076a5b56f1e065b81a2029dec33503ab4ef Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Sep 2020 19:37:21 +0200 Subject: [PATCH 578/813] Added possibility to reset to pype's default values --- .../config_setting/widgets/anatomy_inputs.py | 15 +++++ .../config_setting/widgets/inputs.py | 64 +++++++++++++++---- .../config_setting/widgets/widgets.py | 12 ++++ 3 files changed, 79 insertions(+), 12 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py index 75ee12ee02..09544a2455 100644 --- a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py @@ -190,6 +190,10 @@ class AnatomyWidget(QtWidgets.QWidget, ConfigObject): self.root_widget.remove_overrides() self.templates_widget.remove_overrides() + def reset_to_pype_default(self): + self.root_widget.reset_to_pype_default() + self.templates_widget.reset_to_pype_default() + def discard_changes(self): self.root_widget.discard_changes() self.templates_widget.discard_changes() @@ -558,6 +562,14 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): else: self.singleroot_widget.remove_overrides() + def reset_to_pype_default(self): + self.set_multiroot(self.default_is_multiroot) + if self.is_multiroot: + self.multiroot_widget.reset_to_pype_default() + else: + self.singleroot_widget.reset_to_pype_default() + self._has_studio_override = False + def discard_changes(self): self._is_overriden = self._was_overriden self._is_modified = False @@ -703,6 +715,9 @@ class TemplatesWidget(QtWidgets.QWidget, ConfigObject): def remove_overrides(self): self.value_input.remove_overrides() + def reset_to_pype_default(self): + self.value_input.reset_to_pype_default() + def discard_changes(self): self.value_input.discard_changes() diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index c7f817a48e..fe2e8032b1 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -176,6 +176,11 @@ class ConfigObject(AbstractConfigObject): self.discard_changes() self.ignore_value_changes = False + def _reset_to_pype_default(self): + self.ignore_value_changes = True + self.reset_to_pype_default() + self.ignore_value_changes = False + def _remove_overrides(self): self.ignore_value_changes = True self.remove_overrides() @@ -205,6 +210,17 @@ class ConfigObject(AbstractConfigObject): actions_mapping[action] = self._set_as_overriden menu.addAction(action) + if ( + not self.is_overidable + and ( + self.has_studio_override + or self.child_has_studio_override + ) + ): + action = QtWidgets.QAction("Reset to pype default") + actions_mapping[action] = self._reset_to_pype_default + menu.addAction(action) + if ( not self.any_parent_overriden() and (self.is_overriden or self.child_overriden) @@ -361,6 +377,10 @@ class InputObject(ConfigObject): self._is_overriden = False self._is_modified = False + def reset_to_pype_default(self): + self.set_value(self.default_value) + self._has_studio_override = False + def discard_changes(self): self._is_overriden = self._was_overriden self._has_studio_override = self._had_studio_override @@ -1618,15 +1638,20 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): def remove_overrides(self): self._is_overriden = False self._is_modified = False - for item in self.input_fields: - item.remove_overrides() + for input_field in self.input_fields: + input_field.remove_overrides() + + def reset_to_pype_default(self): + for input_field in self.input_fields: + input_field.reset_to_pype_default() + self._has_studio_override = False def discard_changes(self): self._is_overriden = self._was_overriden self._is_modified = False - for item in self.input_fields: - item.discard_changes() + for input_field in self.input_fields: + input_field.discard_changes() self._is_modified = self.child_modified @@ -1943,15 +1968,20 @@ class DictInvisible(QtWidgets.QWidget, ConfigObject): def remove_overrides(self): self._is_overriden = False self._is_modified = False - for item in self.input_fields: - item.remove_overrides() + for input_field in self.input_fields: + input_field.remove_overrides() + + def reset_to_pype_default(self): + for input_field in self.input_fields: + input_field.reset_to_pype_default() + self._has_studio_override = False def discard_changes(self): self._is_modified = False self._is_overriden = self._was_overriden - for item in self.input_fields: - item.discard_changes() + for input_field in self.input_fields: + input_field.discard_changes() self._is_modified = self.child_modified @@ -2309,8 +2339,13 @@ class PathWidget(QtWidgets.QWidget, ConfigObject): def remove_overrides(self): self._is_overriden = False self._is_modified = False - for item in self.input_fields: - item.remove_overrides() + for input_field in self.input_fields: + input_field.remove_overrides() + + def reset_to_pype_default(self): + for input_field in self.input_fields: + input_field.reset_to_pype_default() + self._has_studio_override = False def discard_changes(self): self._is_modified = False @@ -2467,8 +2502,13 @@ class DictFormWidget(QtWidgets.QWidget, ConfigObject): def remove_overrides(self): self._is_overriden = False self._is_modified = False - for item in self.input_fields: - item.remove_overrides() + for input_field in self.input_fields: + input_field.remove_overrides() + + def reset_to_pype_default(self): + for input_field in self.input_fields: + input_field.reset_to_pype_default() + self._has_studio_override = False def set_as_overriden(self): if self.is_overriden: diff --git a/pype/tools/config_setting/config_setting/widgets/widgets.py b/pype/tools/config_setting/config_setting/widgets/widgets.py index d7631e6fea..e8286fd919 100644 --- a/pype/tools/config_setting/config_setting/widgets/widgets.py +++ b/pype/tools/config_setting/config_setting/widgets/widgets.py @@ -400,6 +400,18 @@ class AbstractConfigObject: ) ) + def _reset_to_pype_default(self): + self.ignore_value_changes = True + self.reset_to_pype_default() + self.ignore_value_changes = False + + def reset_to_pype_default(self): + raise NotImplementedError( + "{} Method `reset_to_pype_default` not implemented!".format( + repr(self) + ) + ) + def _remove_overrides(self): self.ignore_value_changes = True self.remove_overrides() From 4996c5617041f872d487c67c38700886d3e5ad2a Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Sep 2020 19:38:32 +0200 Subject: [PATCH 579/813] removed duplicated methods --- .../config_setting/widgets/inputs.py | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index fe2e8032b1..55b0c999d0 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -171,25 +171,6 @@ class ConfigObject(AbstractConfigObject): return "-".join(items) or cls.default_state - def _discard_changes(self): - self.ignore_value_changes = True - self.discard_changes() - self.ignore_value_changes = False - - def _reset_to_pype_default(self): - self.ignore_value_changes = True - self.reset_to_pype_default() - self.ignore_value_changes = False - - def _remove_overrides(self): - self.ignore_value_changes = True - self.remove_overrides() - self.ignore_value_changes = False - - def _set_as_overriden(self): - self.ignore_value_changes = True - self.set_as_overriden() - self.ignore_value_changes = False def mouseReleaseEvent(self, event): if self.allow_actions and event.button() == QtCore.Qt.RightButton: From 57ac12b5140def9432d16de21fc82b28a048b3fd Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 11 Sep 2020 19:43:44 +0200 Subject: [PATCH 580/813] added set studio defaults action --- .../config_setting/widgets/inputs.py | 43 ++++++++++++++++++- .../config_setting/widgets/widgets.py | 12 ++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 55b0c999d0..fba799e5c0 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -171,7 +171,6 @@ class ConfigObject(AbstractConfigObject): return "-".join(items) or cls.default_state - def mouseReleaseEvent(self, event): if self.allow_actions and event.button() == QtCore.Qt.RightButton: menu = QtWidgets.QMenu() @@ -202,6 +201,17 @@ class ConfigObject(AbstractConfigObject): actions_mapping[action] = self._reset_to_pype_default menu.addAction(action) + if ( + not self.is_overidable + and ( + (self.is_group and not self._had_studio_override) + or self.any_parent_is_group + ) + ): + action = QtWidgets.QAction("Set sudio default") + actions_mapping[action] = self._set_studio_default + menu.addAction(action) + if ( not self.any_parent_overriden() and (self.is_overriden or self.child_overriden) @@ -362,6 +372,9 @@ class InputObject(ConfigObject): self.set_value(self.default_value) self._has_studio_override = False + def set_studio_default(self): + self._has_studio_override = True + def discard_changes(self): self._is_overriden = self._was_overriden self._has_studio_override = self._had_studio_override @@ -1627,6 +1640,13 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): input_field.reset_to_pype_default() self._has_studio_override = False + def set_studio_default(self): + for input_field in self.input_fields: + input_field.set_studio_default() + + if self.is_group: + self._has_studio_override = True + def discard_changes(self): self._is_overriden = self._was_overriden self._is_modified = False @@ -1957,6 +1977,13 @@ class DictInvisible(QtWidgets.QWidget, ConfigObject): input_field.reset_to_pype_default() self._has_studio_override = False + def set_studio_default(self): + for input_field in self.input_fields: + input_field.set_studio_default() + + if self.is_group: + self._has_studio_override = True + def discard_changes(self): self._is_modified = False self._is_overriden = self._was_overriden @@ -2328,6 +2355,13 @@ class PathWidget(QtWidgets.QWidget, ConfigObject): input_field.reset_to_pype_default() self._has_studio_override = False + def set_studio_default(self): + for input_field in self.input_fields: + input_field.set_studio_default() + + if self.is_group: + self._has_studio_override = True + def discard_changes(self): self._is_modified = False self._is_overriden = self._was_overriden @@ -2491,6 +2525,13 @@ class DictFormWidget(QtWidgets.QWidget, ConfigObject): input_field.reset_to_pype_default() self._has_studio_override = False + def set_studio_default(self): + for input_field in self.input_fields: + input_field.set_studio_default() + + if self.is_group: + self._has_studio_override = True + def set_as_overriden(self): if self.is_overriden: return diff --git a/pype/tools/config_setting/config_setting/widgets/widgets.py b/pype/tools/config_setting/config_setting/widgets/widgets.py index e8286fd919..aa1f17a7f4 100644 --- a/pype/tools/config_setting/config_setting/widgets/widgets.py +++ b/pype/tools/config_setting/config_setting/widgets/widgets.py @@ -400,6 +400,18 @@ class AbstractConfigObject: ) ) + def _set_studio_default(self): + self.ignore_value_changes = True + self.set_studio_default() + self.ignore_value_changes = False + + def set_studio_default(self): + raise NotImplementedError( + "{} Method `set_studio_default` not implemented!".format( + repr(self) + ) + ) + def _reset_to_pype_default(self): self.ignore_value_changes = True self.reset_to_pype_default() From 689438f63adfa1f264071c9e9f266a733a272ba6 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 14 Sep 2020 10:11:09 +0200 Subject: [PATCH 581/813] removed separators from PathWidget --- pype/tools/config_setting/config_setting/widgets/inputs.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index fba799e5c0..61a5aac435 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -2089,12 +2089,6 @@ class PathWidget(QtWidgets.QWidget, ConfigObject): "darwin": "MacOS", "linux": "Linux" } - # TODO be able to save and load with separators - platform_separators = { - "windows": ";", - "darwin": ":", - "linux": ":" - } def __init__( self, input_data, parent, From ffa4bd4f073997f3092750f6259a35f68c05f2a3 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 14 Sep 2020 10:32:20 +0200 Subject: [PATCH 582/813] ListWidget has items next to label instead of under --- .../config_setting/config_setting/widgets/inputs.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/config_setting/config_setting/widgets/inputs.py index 61a5aac435..fa809cd2d2 100644 --- a/pype/tools/config_setting/config_setting/widgets/inputs.py +++ b/pype/tools/config_setting/config_setting/widgets/inputs.py @@ -999,14 +999,14 @@ class ListWidget(QtWidgets.QWidget, InputObject): self.input_fields = [] - layout = QtWidgets.QVBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(0) + layout = QtWidgets.QHBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 5) + layout.setSpacing(5) if not label_widget: - label = input_data["label"] - label_widget = QtWidgets.QLabel(label) + label_widget = QtWidgets.QLabel(input_data["label"], self) layout.addWidget(label_widget) + self.label_widget = label_widget inputs_widget = QtWidgets.QWidget(self) @@ -1014,7 +1014,7 @@ class ListWidget(QtWidgets.QWidget, InputObject): layout.addWidget(inputs_widget) inputs_layout = QtWidgets.QVBoxLayout(inputs_widget) - inputs_layout.setContentsMargins(0, 5, 0, 5) + inputs_layout.setContentsMargins(0, 0, 0, 0) inputs_layout.setSpacing(3) self.inputs_widget = inputs_widget From dff5da3a5ef2e3f2c0d7a9f5b684be8c536b899b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 14 Sep 2020 11:42:33 +0200 Subject: [PATCH 583/813] removed label from dict-invisible --- .../config_gui_schema/system_schema/0_system_gui_schema.json | 1 - 1 file changed, 1 deletion(-) diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/0_system_gui_schema.json b/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/0_system_gui_schema.json index bdc0158511..b16545111c 100644 --- a/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/0_system_gui_schema.json +++ b/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/0_system_gui_schema.json @@ -5,7 +5,6 @@ { "type": "dict-invisible", "key": "global", - "label": "Global", "children": [{ "type": "schema", "children": [ From 8f10391b53ae23640c9ffcb016419a57ffa49320 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 14 Sep 2020 12:21:55 +0200 Subject: [PATCH 584/813] renamed config_setting to settings --- pype/tools/helpguide.txt | 153 ++++++++++++++++++ .../{config_setting => settings}/__init__.py | 2 +- .../{config_setting => settings}/__main__.py | 8 +- .../settings}/__init__.py | 0 .../projects_schema/0_project_gui_schema.json | 0 .../projects_schema/1_plugins_gui_schema.json | 0 .../system_schema/0_system_gui_schema.json | 0 .../1_applications_gui_schema.json | 0 .../system_schema/1_intents_gui_schema.json | 0 .../system_schema/1_tools_gui_schema.json | 0 .../system_schema/1_tray_items.json | 0 .../settings}/style/__init__.py | 0 .../settings}/style/pype_icon.png | Bin .../settings}/style/style.css | 0 .../settings}/widgets/__init__.py | 0 .../settings}/widgets/anatomy_inputs.py | 0 .../settings}/widgets/base.py | 0 .../settings}/widgets/inputs.py | 0 .../settings}/widgets/lib.py | 0 .../settings}/widgets/tests.py | 0 .../settings}/widgets/widgets.py | 0 .../settings}/widgets/window.py | 0 22 files changed, 158 insertions(+), 5 deletions(-) create mode 100644 pype/tools/helpguide.txt rename pype/tools/{config_setting => settings}/__init__.py (50%) rename pype/tools/{config_setting => settings}/__main__.py (53%) rename pype/tools/{config_setting/config_setting => settings/settings}/__init__.py (100%) rename pype/tools/{config_setting/config_setting => settings/settings}/config_gui_schema/projects_schema/0_project_gui_schema.json (100%) rename pype/tools/{config_setting/config_setting => settings/settings}/config_gui_schema/projects_schema/1_plugins_gui_schema.json (100%) rename pype/tools/{config_setting/config_setting => settings/settings}/config_gui_schema/system_schema/0_system_gui_schema.json (100%) rename pype/tools/{config_setting/config_setting => settings/settings}/config_gui_schema/system_schema/1_applications_gui_schema.json (100%) rename pype/tools/{config_setting/config_setting => settings/settings}/config_gui_schema/system_schema/1_intents_gui_schema.json (100%) rename pype/tools/{config_setting/config_setting => settings/settings}/config_gui_schema/system_schema/1_tools_gui_schema.json (100%) rename pype/tools/{config_setting/config_setting => settings/settings}/config_gui_schema/system_schema/1_tray_items.json (100%) rename pype/tools/{config_setting/config_setting => settings/settings}/style/__init__.py (100%) rename pype/tools/{config_setting/config_setting => settings/settings}/style/pype_icon.png (100%) rename pype/tools/{config_setting/config_setting => settings/settings}/style/style.css (100%) rename pype/tools/{config_setting/config_setting => settings/settings}/widgets/__init__.py (100%) rename pype/tools/{config_setting/config_setting => settings/settings}/widgets/anatomy_inputs.py (100%) rename pype/tools/{config_setting/config_setting => settings/settings}/widgets/base.py (100%) rename pype/tools/{config_setting/config_setting => settings/settings}/widgets/inputs.py (100%) rename pype/tools/{config_setting/config_setting => settings/settings}/widgets/lib.py (100%) rename pype/tools/{config_setting/config_setting => settings/settings}/widgets/tests.py (100%) rename pype/tools/{config_setting/config_setting => settings/settings}/widgets/widgets.py (100%) rename pype/tools/{config_setting/config_setting => settings/settings}/widgets/window.py (100%) diff --git a/pype/tools/helpguide.txt b/pype/tools/helpguide.txt new file mode 100644 index 0000000000..b0c9471e3e --- /dev/null +++ b/pype/tools/helpguide.txt @@ -0,0 +1,153 @@ +## Basic rules +- configurations does not define GUI, but GUI defines configurations! +- output is always json (yaml is not needed for anatomy templates anymore) +- GUI schema has multiple input types, all inputs are represented by a dictionary +- each input may have "input modifiers" (keys in dictionary) that are required or optional + - only required modifier for all input items is key `"type"` which says what type of item it is +- there are special keys across all inputs + - `"is_file"` - this key is for storing pype defaults in `pype` repo + - reasons of existence: developing new schemas does not require to create defaults manually + - key is validated, must be once in hierarchy else it won't be possible to store pype defaults + - `"is_group"` - define that all values under key in hierarchy will be overriden if any value is modified, this information is also stored to overrides + - key is validated, can be only once in hierarchy but is not required + +## Basic Dictionary inputs +- these inputs wraps another inputs into {key: value} relation + +## dict-invisible +- this input gives ability to wrap another inputs but keep them in same widget without visible divider + - this is used as first input widget +- has required keys `"key"` and `"children"` + - "children" says what children inputs are underneath + - "key" is key under which will be stored value from it's children +- output is dictionary `{the "key": children values}` + +``` +{ + "type": "dict-invisible", + "key": "global", + "children": [{ + ... + }] +} +``` + +## dict +- this is another dictionary input wrapping more inputs but visually makes them different + +``` +{ + "key": "applications", + "type": "dict", + "label": "Applications", + "expandable": true, + "is_group": true, + "is_file": true, +} +``` + +## Inputs for setting any kind of value (`Pure` inputs) +- all these input must have defined `"key"` under which will be stored and `"label"` which will be shown next to input + - unless they are used in different types of inputs (later) "as widgets" in that case `"key"` and `"label"` are not required as there is not place where to set them + +### boolean +- simple checkbox, nothing more to set +``` +{ + "type": "boolean", + "key": "my_boolean_key", + "label": "Do you want to use Pype?" +} +``` + +### number +- number input, can be used for both integer and float + - key `"decimal"` defines how many decimal places will be used, 0 is for integer input (Default: `0`) + - key `"minimum"` as minimum allowed number to enter (Default: `-99999`) + - key `"maxium"` as maximum allowed number to enter (Default: `99999`) +``` +{ + "type": "number", + "key": "fps", + "label": "Frame rate (FPS)" + "decimal": 2, + "minimum": 1, + "maximum": 300000 +} +``` + +### text +- simple text input + - key `"multiline"` allows to enter multiple lines of text (Default: `False`) + +``` +{ + "type": "text", + "key": "deadline_pool", + "label": "Deadline pool" +} +``` + +### raw-json +- a little bit enhanced text input for raw json +- has validations of json format + - empty value is invalid value, always must be at least `{}` of `[]` + +``` +{ + "type": "raw-json", + "key": "profiles", + "label": "Extract Review profiles" +} +``` + +## Inputs for setting value using Pure inputs +- these inputs also have required `"key"` and `"label"` +- they use Pure inputs "as widgets" + +### list +- output is list +- items can be added and removed +- items in list must be the same type + - type of items is defined with key `"object_type"` where Pure input name is entered (e.g. `number`) + - because Pure inputs may have modifiers (`number` input has `minimum`, `maximum` and `decimals`) you can set these in key `"input_modifiers"` + +``` +{ + "type": "list", + "object_type": "number", + "key": "exclude_ports", + "label": "Exclude ports", + "input_modifiers": { + "minimum": 1, + "maximum": 65535 + } +} +``` + +### dict-modifiable +- one of dictionary inputs, this is only used as value input +- items in this input can be removed and added same way as in `list` input +- value items in dictionary must be the same type + - type of items is defined with key `"object_type"` where Pure input name is entered (e.g. `number`) + - because Pure inputs may have modifiers (`number` input has `minimum`, `maximum` and `decimals`) you can set these in key `"input_modifiers"` +- this input can be expandable + - that can be set with key `"expandable"` as `True`/`False` (Default: `True`) + - with key `"expanded"` as `True`/`False` can be set that is expanded when GUI is opened (Default: `False`) + +``` +{ + "type": "dict-modifiable", + "object_type": "number", + "input_modifiers": { + "minimum": 0, + "maximum": 300 + }, + "is_group": true, + "key": "templates_mapping", + "label": "Muster - Templates mapping", + "is_file": true +} +``` + +Currently there are `system configurations` and `project configurations`. Both has `root` schema where all begins. diff --git a/pype/tools/config_setting/__init__.py b/pype/tools/settings/__init__.py similarity index 50% rename from pype/tools/config_setting/__init__.py rename to pype/tools/settings/__init__.py index c3bd49449d..7df121f06e 100644 --- a/pype/tools/config_setting/__init__.py +++ b/pype/tools/settings/__init__.py @@ -1,4 +1,4 @@ -from config_setting import style, MainWidget +from settings import style, MainWidget __all__ = ( diff --git a/pype/tools/config_setting/__main__.py b/pype/tools/settings/__main__.py similarity index 53% rename from pype/tools/config_setting/__main__.py rename to pype/tools/settings/__main__.py index 0e4ab1c0aa..044c2ef495 100644 --- a/pype/tools/config_setting/__main__.py +++ b/pype/tools/settings/__main__.py @@ -1,18 +1,18 @@ import sys -import config_setting +import settings from Qt import QtWidgets, QtGui if __name__ == "__main__": app = QtWidgets.QApplication(sys.argv) - stylesheet = config_setting.style.load_stylesheet() + stylesheet = settings.style.load_stylesheet() app.setStyleSheet(stylesheet) - app.setWindowIcon(QtGui.QIcon(config_setting.style.app_icon_path())) + app.setWindowIcon(QtGui.QIcon(settings.style.app_icon_path())) develop = "-dev" in sys.argv - widget = config_setting.MainWidget(develop) + widget = settings.MainWidget(develop) widget.show() sys.exit(app.exec_()) diff --git a/pype/tools/config_setting/config_setting/__init__.py b/pype/tools/settings/settings/__init__.py similarity index 100% rename from pype/tools/config_setting/config_setting/__init__.py rename to pype/tools/settings/settings/__init__.py diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/0_project_gui_schema.json b/pype/tools/settings/settings/config_gui_schema/projects_schema/0_project_gui_schema.json similarity index 100% rename from pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/0_project_gui_schema.json rename to pype/tools/settings/settings/config_gui_schema/projects_schema/0_project_gui_schema.json diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json b/pype/tools/settings/settings/config_gui_schema/projects_schema/1_plugins_gui_schema.json similarity index 100% rename from pype/tools/config_setting/config_setting/config_gui_schema/projects_schema/1_plugins_gui_schema.json rename to pype/tools/settings/settings/config_gui_schema/projects_schema/1_plugins_gui_schema.json diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/0_system_gui_schema.json b/pype/tools/settings/settings/config_gui_schema/system_schema/0_system_gui_schema.json similarity index 100% rename from pype/tools/config_setting/config_setting/config_gui_schema/system_schema/0_system_gui_schema.json rename to pype/tools/settings/settings/config_gui_schema/system_schema/0_system_gui_schema.json diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/1_applications_gui_schema.json b/pype/tools/settings/settings/config_gui_schema/system_schema/1_applications_gui_schema.json similarity index 100% rename from pype/tools/config_setting/config_setting/config_gui_schema/system_schema/1_applications_gui_schema.json rename to pype/tools/settings/settings/config_gui_schema/system_schema/1_applications_gui_schema.json diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/1_intents_gui_schema.json b/pype/tools/settings/settings/config_gui_schema/system_schema/1_intents_gui_schema.json similarity index 100% rename from pype/tools/config_setting/config_setting/config_gui_schema/system_schema/1_intents_gui_schema.json rename to pype/tools/settings/settings/config_gui_schema/system_schema/1_intents_gui_schema.json diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/1_tools_gui_schema.json b/pype/tools/settings/settings/config_gui_schema/system_schema/1_tools_gui_schema.json similarity index 100% rename from pype/tools/config_setting/config_setting/config_gui_schema/system_schema/1_tools_gui_schema.json rename to pype/tools/settings/settings/config_gui_schema/system_schema/1_tools_gui_schema.json diff --git a/pype/tools/config_setting/config_setting/config_gui_schema/system_schema/1_tray_items.json b/pype/tools/settings/settings/config_gui_schema/system_schema/1_tray_items.json similarity index 100% rename from pype/tools/config_setting/config_setting/config_gui_schema/system_schema/1_tray_items.json rename to pype/tools/settings/settings/config_gui_schema/system_schema/1_tray_items.json diff --git a/pype/tools/config_setting/config_setting/style/__init__.py b/pype/tools/settings/settings/style/__init__.py similarity index 100% rename from pype/tools/config_setting/config_setting/style/__init__.py rename to pype/tools/settings/settings/style/__init__.py diff --git a/pype/tools/config_setting/config_setting/style/pype_icon.png b/pype/tools/settings/settings/style/pype_icon.png similarity index 100% rename from pype/tools/config_setting/config_setting/style/pype_icon.png rename to pype/tools/settings/settings/style/pype_icon.png diff --git a/pype/tools/config_setting/config_setting/style/style.css b/pype/tools/settings/settings/style/style.css similarity index 100% rename from pype/tools/config_setting/config_setting/style/style.css rename to pype/tools/settings/settings/style/style.css diff --git a/pype/tools/config_setting/config_setting/widgets/__init__.py b/pype/tools/settings/settings/widgets/__init__.py similarity index 100% rename from pype/tools/config_setting/config_setting/widgets/__init__.py rename to pype/tools/settings/settings/widgets/__init__.py diff --git a/pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py b/pype/tools/settings/settings/widgets/anatomy_inputs.py similarity index 100% rename from pype/tools/config_setting/config_setting/widgets/anatomy_inputs.py rename to pype/tools/settings/settings/widgets/anatomy_inputs.py diff --git a/pype/tools/config_setting/config_setting/widgets/base.py b/pype/tools/settings/settings/widgets/base.py similarity index 100% rename from pype/tools/config_setting/config_setting/widgets/base.py rename to pype/tools/settings/settings/widgets/base.py diff --git a/pype/tools/config_setting/config_setting/widgets/inputs.py b/pype/tools/settings/settings/widgets/inputs.py similarity index 100% rename from pype/tools/config_setting/config_setting/widgets/inputs.py rename to pype/tools/settings/settings/widgets/inputs.py diff --git a/pype/tools/config_setting/config_setting/widgets/lib.py b/pype/tools/settings/settings/widgets/lib.py similarity index 100% rename from pype/tools/config_setting/config_setting/widgets/lib.py rename to pype/tools/settings/settings/widgets/lib.py diff --git a/pype/tools/config_setting/config_setting/widgets/tests.py b/pype/tools/settings/settings/widgets/tests.py similarity index 100% rename from pype/tools/config_setting/config_setting/widgets/tests.py rename to pype/tools/settings/settings/widgets/tests.py diff --git a/pype/tools/config_setting/config_setting/widgets/widgets.py b/pype/tools/settings/settings/widgets/widgets.py similarity index 100% rename from pype/tools/config_setting/config_setting/widgets/widgets.py rename to pype/tools/settings/settings/widgets/widgets.py diff --git a/pype/tools/config_setting/config_setting/widgets/window.py b/pype/tools/settings/settings/widgets/window.py similarity index 100% rename from pype/tools/config_setting/config_setting/widgets/window.py rename to pype/tools/settings/settings/widgets/window.py From 9e016906458aeaa15634f9f9e1ca03f843e1a4ee Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 14 Sep 2020 12:27:15 +0200 Subject: [PATCH 585/813] folder config_gui_schema renamed to gui_schemas --- .../projects_schema/0_project_gui_schema.json | 0 .../projects_schema/1_plugins_gui_schema.json | 0 .../system_schema/0_system_gui_schema.json | 0 .../system_schema/1_applications_gui_schema.json | 0 .../system_schema/1_intents_gui_schema.json | 0 .../system_schema/1_tools_gui_schema.json | 0 .../system_schema/1_tray_items.json | 0 pype/tools/settings/settings/widgets/lib.py | 2 +- 8 files changed, 1 insertion(+), 1 deletion(-) rename pype/tools/settings/settings/{config_gui_schema => gui_schemas}/projects_schema/0_project_gui_schema.json (100%) rename pype/tools/settings/settings/{config_gui_schema => gui_schemas}/projects_schema/1_plugins_gui_schema.json (100%) rename pype/tools/settings/settings/{config_gui_schema => gui_schemas}/system_schema/0_system_gui_schema.json (100%) rename pype/tools/settings/settings/{config_gui_schema => gui_schemas}/system_schema/1_applications_gui_schema.json (100%) rename pype/tools/settings/settings/{config_gui_schema => gui_schemas}/system_schema/1_intents_gui_schema.json (100%) rename pype/tools/settings/settings/{config_gui_schema => gui_schemas}/system_schema/1_tools_gui_schema.json (100%) rename pype/tools/settings/settings/{config_gui_schema => gui_schemas}/system_schema/1_tray_items.json (100%) diff --git a/pype/tools/settings/settings/config_gui_schema/projects_schema/0_project_gui_schema.json b/pype/tools/settings/settings/gui_schemas/projects_schema/0_project_gui_schema.json similarity index 100% rename from pype/tools/settings/settings/config_gui_schema/projects_schema/0_project_gui_schema.json rename to pype/tools/settings/settings/gui_schemas/projects_schema/0_project_gui_schema.json diff --git a/pype/tools/settings/settings/config_gui_schema/projects_schema/1_plugins_gui_schema.json b/pype/tools/settings/settings/gui_schemas/projects_schema/1_plugins_gui_schema.json similarity index 100% rename from pype/tools/settings/settings/config_gui_schema/projects_schema/1_plugins_gui_schema.json rename to pype/tools/settings/settings/gui_schemas/projects_schema/1_plugins_gui_schema.json diff --git a/pype/tools/settings/settings/config_gui_schema/system_schema/0_system_gui_schema.json b/pype/tools/settings/settings/gui_schemas/system_schema/0_system_gui_schema.json similarity index 100% rename from pype/tools/settings/settings/config_gui_schema/system_schema/0_system_gui_schema.json rename to pype/tools/settings/settings/gui_schemas/system_schema/0_system_gui_schema.json diff --git a/pype/tools/settings/settings/config_gui_schema/system_schema/1_applications_gui_schema.json b/pype/tools/settings/settings/gui_schemas/system_schema/1_applications_gui_schema.json similarity index 100% rename from pype/tools/settings/settings/config_gui_schema/system_schema/1_applications_gui_schema.json rename to pype/tools/settings/settings/gui_schemas/system_schema/1_applications_gui_schema.json diff --git a/pype/tools/settings/settings/config_gui_schema/system_schema/1_intents_gui_schema.json b/pype/tools/settings/settings/gui_schemas/system_schema/1_intents_gui_schema.json similarity index 100% rename from pype/tools/settings/settings/config_gui_schema/system_schema/1_intents_gui_schema.json rename to pype/tools/settings/settings/gui_schemas/system_schema/1_intents_gui_schema.json diff --git a/pype/tools/settings/settings/config_gui_schema/system_schema/1_tools_gui_schema.json b/pype/tools/settings/settings/gui_schemas/system_schema/1_tools_gui_schema.json similarity index 100% rename from pype/tools/settings/settings/config_gui_schema/system_schema/1_tools_gui_schema.json rename to pype/tools/settings/settings/gui_schemas/system_schema/1_tools_gui_schema.json diff --git a/pype/tools/settings/settings/config_gui_schema/system_schema/1_tray_items.json b/pype/tools/settings/settings/gui_schemas/system_schema/1_tray_items.json similarity index 100% rename from pype/tools/settings/settings/config_gui_schema/system_schema/1_tray_items.json rename to pype/tools/settings/settings/gui_schemas/system_schema/1_tray_items.json diff --git a/pype/tools/settings/settings/widgets/lib.py b/pype/tools/settings/settings/widgets/lib.py index daba87de15..9f54b090cb 100644 --- a/pype/tools/settings/settings/widgets/lib.py +++ b/pype/tools/settings/settings/widgets/lib.py @@ -278,7 +278,7 @@ def gui_schema(subfolder, main_schema_name): subfolder, main_schema_name dirpath = os.path.join( os.path.dirname(os.path.dirname(__file__)), - "config_gui_schema", + "gui_schemas", subfolder ) From 1c8d2c3a3065da76e13d2c65480f320faf6645d5 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 14 Sep 2020 12:53:20 +0200 Subject: [PATCH 586/813] removed temp file --- pype/tools/helpguide.txt | 153 --------------------------------------- 1 file changed, 153 deletions(-) delete mode 100644 pype/tools/helpguide.txt diff --git a/pype/tools/helpguide.txt b/pype/tools/helpguide.txt deleted file mode 100644 index b0c9471e3e..0000000000 --- a/pype/tools/helpguide.txt +++ /dev/null @@ -1,153 +0,0 @@ -## Basic rules -- configurations does not define GUI, but GUI defines configurations! -- output is always json (yaml is not needed for anatomy templates anymore) -- GUI schema has multiple input types, all inputs are represented by a dictionary -- each input may have "input modifiers" (keys in dictionary) that are required or optional - - only required modifier for all input items is key `"type"` which says what type of item it is -- there are special keys across all inputs - - `"is_file"` - this key is for storing pype defaults in `pype` repo - - reasons of existence: developing new schemas does not require to create defaults manually - - key is validated, must be once in hierarchy else it won't be possible to store pype defaults - - `"is_group"` - define that all values under key in hierarchy will be overriden if any value is modified, this information is also stored to overrides - - key is validated, can be only once in hierarchy but is not required - -## Basic Dictionary inputs -- these inputs wraps another inputs into {key: value} relation - -## dict-invisible -- this input gives ability to wrap another inputs but keep them in same widget without visible divider - - this is used as first input widget -- has required keys `"key"` and `"children"` - - "children" says what children inputs are underneath - - "key" is key under which will be stored value from it's children -- output is dictionary `{the "key": children values}` - -``` -{ - "type": "dict-invisible", - "key": "global", - "children": [{ - ... - }] -} -``` - -## dict -- this is another dictionary input wrapping more inputs but visually makes them different - -``` -{ - "key": "applications", - "type": "dict", - "label": "Applications", - "expandable": true, - "is_group": true, - "is_file": true, -} -``` - -## Inputs for setting any kind of value (`Pure` inputs) -- all these input must have defined `"key"` under which will be stored and `"label"` which will be shown next to input - - unless they are used in different types of inputs (later) "as widgets" in that case `"key"` and `"label"` are not required as there is not place where to set them - -### boolean -- simple checkbox, nothing more to set -``` -{ - "type": "boolean", - "key": "my_boolean_key", - "label": "Do you want to use Pype?" -} -``` - -### number -- number input, can be used for both integer and float - - key `"decimal"` defines how many decimal places will be used, 0 is for integer input (Default: `0`) - - key `"minimum"` as minimum allowed number to enter (Default: `-99999`) - - key `"maxium"` as maximum allowed number to enter (Default: `99999`) -``` -{ - "type": "number", - "key": "fps", - "label": "Frame rate (FPS)" - "decimal": 2, - "minimum": 1, - "maximum": 300000 -} -``` - -### text -- simple text input - - key `"multiline"` allows to enter multiple lines of text (Default: `False`) - -``` -{ - "type": "text", - "key": "deadline_pool", - "label": "Deadline pool" -} -``` - -### raw-json -- a little bit enhanced text input for raw json -- has validations of json format - - empty value is invalid value, always must be at least `{}` of `[]` - -``` -{ - "type": "raw-json", - "key": "profiles", - "label": "Extract Review profiles" -} -``` - -## Inputs for setting value using Pure inputs -- these inputs also have required `"key"` and `"label"` -- they use Pure inputs "as widgets" - -### list -- output is list -- items can be added and removed -- items in list must be the same type - - type of items is defined with key `"object_type"` where Pure input name is entered (e.g. `number`) - - because Pure inputs may have modifiers (`number` input has `minimum`, `maximum` and `decimals`) you can set these in key `"input_modifiers"` - -``` -{ - "type": "list", - "object_type": "number", - "key": "exclude_ports", - "label": "Exclude ports", - "input_modifiers": { - "minimum": 1, - "maximum": 65535 - } -} -``` - -### dict-modifiable -- one of dictionary inputs, this is only used as value input -- items in this input can be removed and added same way as in `list` input -- value items in dictionary must be the same type - - type of items is defined with key `"object_type"` where Pure input name is entered (e.g. `number`) - - because Pure inputs may have modifiers (`number` input has `minimum`, `maximum` and `decimals`) you can set these in key `"input_modifiers"` -- this input can be expandable - - that can be set with key `"expandable"` as `True`/`False` (Default: `True`) - - with key `"expanded"` as `True`/`False` can be set that is expanded when GUI is opened (Default: `False`) - -``` -{ - "type": "dict-modifiable", - "object_type": "number", - "input_modifiers": { - "minimum": 0, - "maximum": 300 - }, - "is_group": true, - "key": "templates_mapping", - "label": "Muster - Templates mapping", - "is_file": true -} -``` - -Currently there are `system configurations` and `project configurations`. Both has `root` schema where all begins. From 2f2eb3689bf726cf5ecc4613ad6eafe0e1309d9c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 14 Sep 2020 18:13:43 +0200 Subject: [PATCH 587/813] extracted config.py to __init__.py --- pype/configurations/__init__.py | 9 +++++++++ pype/configurations/config.py | 24 ------------------------ pype/configurations/lib.py | 17 +++++++++++++++++ 3 files changed, 26 insertions(+), 24 deletions(-) create mode 100644 pype/configurations/__init__.py delete mode 100644 pype/configurations/config.py diff --git a/pype/configurations/__init__.py b/pype/configurations/__init__.py new file mode 100644 index 0000000000..e3fc53fcfd --- /dev/null +++ b/pype/configurations/__init__.py @@ -0,0 +1,9 @@ +from .lib import ( + system_configurations, + project_configurations +) + +__all__ = ( + "system_configurations", + "project_configurations" +) diff --git a/pype/configurations/config.py b/pype/configurations/config.py deleted file mode 100644 index ed7104427f..0000000000 --- a/pype/configurations/config.py +++ /dev/null @@ -1,24 +0,0 @@ -from .lib import ( - apply_overrides, - default_configuration, - studio_system_configurations, - studio_project_configurations, - project_configurations_overrides -) - - -def system_configurations(): - default_values = default_configuration()["system_configurations"] - studio_values = studio_system_configurations() - return apply_overrides(default_values, studio_values) - - -def project_configurations(project_name): - default_values = default_configuration()["project_configurations"] - studio_values = studio_project_configurations() - - studio_overrides = apply_overrides(default_values, studio_values) - - project_overrides = project_configurations_overrides(project_name) - - return apply_overrides(studio_overrides, project_overrides) diff --git a/pype/configurations/lib.py b/pype/configurations/lib.py index 7e24d25483..6fb90261e1 100644 --- a/pype/configurations/lib.py +++ b/pype/configurations/lib.py @@ -239,3 +239,20 @@ def apply_overrides(global_presets, project_overrides): if not project_overrides: return global_presets return merge_overrides(global_presets, project_overrides) + + +def system_configurations(): + default_values = default_configuration()["system_configurations"] + studio_values = studio_system_configurations() + return apply_overrides(default_values, studio_values) + + +def project_configurations(project_name): + default_values = default_configuration()["project_configurations"] + studio_values = studio_project_configurations() + + studio_overrides = apply_overrides(default_values, studio_values) + + project_overrides = project_configurations_overrides(project_name) + + return apply_overrides(studio_overrides, project_overrides) From 9b466e93728e324462961cfb210bb059c5b4c279 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 14 Sep 2020 18:15:40 +0200 Subject: [PATCH 588/813] renamed configuration constants to settings constant names --- pype/configurations/lib.py | 38 ++++++++++---------- pype/tools/settings/settings/widgets/base.py | 26 +++++++------- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/pype/configurations/lib.py b/pype/configurations/lib.py index 6fb90261e1..101b579b14 100644 --- a/pype/configurations/lib.py +++ b/pype/configurations/lib.py @@ -14,16 +14,16 @@ POP_KEY = "__pop_key__" STUDIO_OVERRIDES_PATH = os.environ["PYPE_PROJECT_CONFIGS"] # File where studio's system overrides are stored -SYSTEM_CONFIGURATIONS_KEY = "system_configurations" -SYSTEM_CONFIGURATIONS_PATH = os.path.join( - STUDIO_OVERRIDES_PATH, SYSTEM_CONFIGURATIONS_KEY + ".json" +SYSTEM_SETTINGS_KEY = "system_configurations" +SYSTEM_SETTINGS_PATH = os.path.join( + STUDIO_OVERRIDES_PATH, SYSTEM_SETTINGS_KEY + ".json" ) # File where studio's default project overrides are stored -PROJECT_CONFIGURATIONS_KEY = "project_configurations" -PROJECT_CONFIGURATIONS_FILENAME = PROJECT_CONFIGURATIONS_KEY + ".json" -PROJECT_CONFIGURATIONS_PATH = os.path.join( - STUDIO_OVERRIDES_PATH, PROJECT_CONFIGURATIONS_FILENAME +PROJECT_SETTINGS_KEY = "project_configurations" +PROJECT_SETTINGS_FILENAME = PROJECT_SETTINGS_KEY + ".json" +PROJECT_SETTINGS_PATH = os.path.join( + STUDIO_OVERRIDES_PATH, PROJECT_SETTINGS_FILENAME ) PROJECT_ANATOMY_KEY = "project_anatomy" @@ -36,19 +36,19 @@ PROJECT_ANATOMY_PATH = os.path.join( DEFAULTS_DIR = os.path.join(os.path.dirname(__file__), "defaults") # Variable where cache of default configurations are stored -_DEFAULT_CONFIGURATIONS = None +_DEFAULT_SETTINGS = None def reset_default_configurations(): - global _DEFAULT_CONFIGURATIONS - _DEFAULT_CONFIGURATIONS = None + global _DEFAULT_SETTINGS + _DEFAULT_SETTINGS = None def default_configuration(): - global _DEFAULT_CONFIGURATIONS - if _DEFAULT_CONFIGURATIONS is None: - _DEFAULT_CONFIGURATIONS = load_jsons_from_dir(DEFAULTS_DIR) - return _DEFAULT_CONFIGURATIONS + global _DEFAULT_SETTINGS + if _DEFAULT_SETTINGS is None: + _DEFAULT_SETTINGS = load_jsons_from_dir(DEFAULTS_DIR) + return _DEFAULT_SETTINGS def load_json(fpath): @@ -157,14 +157,14 @@ def load_jsons_from_dir(path, *args, **kwargs): def studio_system_configurations(): - if os.path.exists(SYSTEM_CONFIGURATIONS_PATH): - return load_json(SYSTEM_CONFIGURATIONS_PATH) + if os.path.exists(SYSTEM_SETTINGS_PATH): + return load_json(SYSTEM_SETTINGS_PATH) return {} def studio_project_configurations(): - if os.path.exists(PROJECT_CONFIGURATIONS_PATH): - return load_json(PROJECT_CONFIGURATIONS_PATH) + if os.path.exists(PROJECT_SETTINGS_PATH): + return load_json(PROJECT_SETTINGS_PATH) return {} @@ -178,7 +178,7 @@ def path_to_project_overrides(project_name): return os.path.join( STUDIO_OVERRIDES_PATH, project_name, - PROJECT_CONFIGURATIONS_FILENAME + PROJECT_SETTINGS_FILENAME ) diff --git a/pype/tools/settings/settings/widgets/base.py b/pype/tools/settings/settings/widgets/base.py index 16192aadf3..e1d167a9f2 100644 --- a/pype/tools/settings/settings/widgets/base.py +++ b/pype/tools/settings/settings/widgets/base.py @@ -3,9 +3,9 @@ import json from Qt import QtWidgets, QtCore, QtGui from pype.configurations.lib import ( SYSTEM_CONFIGURATIONS_KEY, - SYSTEM_CONFIGURATIONS_PATH, - PROJECT_CONFIGURATIONS_KEY, - PROJECT_CONFIGURATIONS_PATH, + SYSTEM_SETTINGS_PATH, + PROJECT_SETTINGS_KEY, + PROJECT_SETTINGS_PATH, PROJECT_ANATOMY_KEY, PROJECT_ANATOMY_PATH, @@ -148,12 +148,12 @@ class SystemWidget(QtWidgets.QWidget): values = lib.convert_gui_data_to_overrides(_data.get("system", {})) - dirpath = os.path.dirname(SYSTEM_CONFIGURATIONS_PATH) + dirpath = os.path.dirname(SYSTEM_SETTINGS_PATH) if not os.path.exists(dirpath): os.makedirs(dirpath) - print("Saving data to:", SYSTEM_CONFIGURATIONS_PATH) - with open(SYSTEM_CONFIGURATIONS_PATH, "w") as file_stream: + print("Saving data to:", SYSTEM_SETTINGS_PATH) + with open(SYSTEM_SETTINGS_PATH, "w") as file_stream: json.dump(values, file_stream, indent=4) self._update_values() @@ -467,7 +467,7 @@ class ProjectWidget(QtWidgets.QWidget): self.is_overidable = True overrides = {"project": { - PROJECT_CONFIGURATIONS_KEY: lib.convert_overrides_to_gui_data( + PROJECT_SETTINGS_KEY: lib.convert_overrides_to_gui_data( _project_overrides ), PROJECT_ANATOMY_KEY: lib.convert_overrides_to_gui_data( @@ -566,7 +566,7 @@ class ProjectWidget(QtWidgets.QWidget): # Saving overrides data project_overrides_data = output_data.get( - PROJECT_CONFIGURATIONS_KEY, {} + PROJECT_SETTINGS_KEY, {} ) project_overrides_json_path = path_to_project_overrides( self.project_name @@ -610,14 +610,14 @@ class ProjectWidget(QtWidgets.QWidget): # Project overrides data project_overrides_data = output_data.get( - PROJECT_CONFIGURATIONS_KEY, {} + PROJECT_SETTINGS_KEY, {} ) - dirpath = os.path.dirname(PROJECT_CONFIGURATIONS_PATH) + dirpath = os.path.dirname(PROJECT_SETTINGS_PATH) if not os.path.exists(dirpath): os.makedirs(dirpath) - print("Saving data to:", PROJECT_CONFIGURATIONS_PATH) - with open(PROJECT_CONFIGURATIONS_PATH, "w") as file_stream: + print("Saving data to:", PROJECT_SETTINGS_PATH) + with open(PROJECT_SETTINGS_PATH, "w") as file_stream: json.dump(project_overrides_data, file_stream, indent=4) # Project Anatomy data @@ -643,7 +643,7 @@ class ProjectWidget(QtWidgets.QWidget): input_field.update_default_values(default_values) studio_values = {"project": { - PROJECT_CONFIGURATIONS_KEY: studio_project_configurations(), + PROJECT_SETTINGS_KEY: studio_project_configurations(), PROJECT_ANATOMY_KEY: studio_project_anatomy() }} for input_field in self.input_fields: From b4860169c58fb8f3a964bb3915668f99fc3d31dc Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 14 Sep 2020 18:17:50 +0200 Subject: [PATCH 589/813] renamed most of variables with configuration in name to settings --- .../settings/widgets/anatomy_inputs.py | 8 ++++---- .../tools/settings/settings/widgets/inputs.py | 20 +++++++++---------- .../settings/settings/widgets/widgets.py | 4 ++-- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/pype/tools/settings/settings/widgets/anatomy_inputs.py b/pype/tools/settings/settings/widgets/anatomy_inputs.py index 09544a2455..0e7e094be5 100644 --- a/pype/tools/settings/settings/widgets/anatomy_inputs.py +++ b/pype/tools/settings/settings/widgets/anatomy_inputs.py @@ -1,10 +1,10 @@ from Qt import QtWidgets, QtCore from .widgets import ExpandingWidget -from .inputs import ConfigObject, ModifiableDict, PathWidget, RawJsonWidget +from .inputs import SettingObject, ModifiableDict, PathWidget, RawJsonWidget from .lib import NOT_SET, TypeToKlass, CHILD_OFFSET, METADATA_KEY -class AnatomyWidget(QtWidgets.QWidget, ConfigObject): +class AnatomyWidget(QtWidgets.QWidget, SettingObject): value_changed = QtCore.Signal(object) template_keys = ( "project[name]", @@ -224,7 +224,7 @@ class AnatomyWidget(QtWidgets.QWidget, ConfigObject): return {self.key: self.item_value()} -class RootsWidget(QtWidgets.QWidget, ConfigObject): +class RootsWidget(QtWidgets.QWidget, SettingObject): value_changed = QtCore.Signal(object) def __init__(self, input_data, parent): @@ -603,7 +603,7 @@ class RootsWidget(QtWidgets.QWidget, ConfigObject): return {self.key: self.item_value()} -class TemplatesWidget(QtWidgets.QWidget, ConfigObject): +class TemplatesWidget(QtWidgets.QWidget, SettingObject): value_changed = QtCore.Signal(object) def __init__(self, input_data, parent): diff --git a/pype/tools/settings/settings/widgets/inputs.py b/pype/tools/settings/settings/widgets/inputs.py index fa809cd2d2..aa72f1c81f 100644 --- a/pype/tools/settings/settings/widgets/inputs.py +++ b/pype/tools/settings/settings/widgets/inputs.py @@ -3,7 +3,7 @@ import logging import collections from Qt import QtWidgets, QtCore, QtGui from .widgets import ( - AbstractConfigObject, + AbstractSettingObject, ExpandingWidget, NumberSpinBox, PathInput @@ -11,7 +11,7 @@ from .widgets import ( from .lib import NOT_SET, METADATA_KEY, TypeToKlass, CHILD_OFFSET -class ConfigObject(AbstractConfigObject): +class SettingObject(AbstractSettingObject): default_input_value = NOT_SET allow_actions = True default_state = "" @@ -88,7 +88,7 @@ class ConfigObject(AbstractConfigObject): @property def any_parent_is_group(self): if self._any_parent_is_group is None: - return super(ConfigObject, self).any_parent_is_group + return super(SettingObject, self).any_parent_is_group return self._any_parent_is_group @property @@ -246,7 +246,7 @@ class ConfigObject(AbstractConfigObject): return item.mouseReleaseEvent(self, event) -class InputObject(ConfigObject): +class InputObject(SettingObject): def update_default_values(self, parent_values): self._state = None self._is_modified = False @@ -879,7 +879,7 @@ class RawJsonWidget(QtWidgets.QWidget, InputObject): return self.text_input.json_value() -class ListItem(QtWidgets.QWidget, ConfigObject): +class ListItem(QtWidgets.QWidget, SettingObject): _btn_size = 20 value_changed = QtCore.Signal(object) @@ -1157,7 +1157,7 @@ class ListWidget(QtWidgets.QWidget, InputObject): return output -class ModifiableDictItem(QtWidgets.QWidget, ConfigObject): +class ModifiableDictItem(QtWidgets.QWidget, SettingObject): _btn_size = 20 value_changed = QtCore.Signal(object) @@ -1534,7 +1534,7 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): # Dictionaries -class DictWidget(QtWidgets.QWidget, ConfigObject): +class DictWidget(QtWidgets.QWidget, SettingObject): value_changed = QtCore.Signal(object) def __init__( @@ -1856,7 +1856,7 @@ class DictWidget(QtWidgets.QWidget, ConfigObject): return {self.key: values}, self.is_group -class DictInvisible(QtWidgets.QWidget, ConfigObject): +class DictInvisible(QtWidgets.QWidget, SettingObject): # TODO is not overridable by itself value_changed = QtCore.Signal(object) allow_actions = False @@ -2081,7 +2081,7 @@ class DictInvisible(QtWidgets.QWidget, ConfigObject): return {self.key: values}, self.is_group -class PathWidget(QtWidgets.QWidget, ConfigObject): +class PathWidget(QtWidgets.QWidget, SettingObject): value_changed = QtCore.Signal(object) platforms = ("windows", "darwin", "linux") platform_labels_mapping = { @@ -2442,7 +2442,7 @@ class FormLabel(QtWidgets.QLabel): self.item = None -class DictFormWidget(QtWidgets.QWidget, ConfigObject): +class DictFormWidget(QtWidgets.QWidget, SettingObject): value_changed = QtCore.Signal(object) allow_actions = False diff --git a/pype/tools/settings/settings/widgets/widgets.py b/pype/tools/settings/settings/widgets/widgets.py index aa1f17a7f4..bb42df6026 100644 --- a/pype/tools/settings/settings/widgets/widgets.py +++ b/pype/tools/settings/settings/widgets/widgets.py @@ -224,7 +224,7 @@ class UnsavedChangesDialog(QtWidgets.QDialog): self.done(2) -class AbstractConfigObject: +class AbstractSettingObject: abstract_attributes = ("_parent", ) def __getattr__(self, name): @@ -232,7 +232,7 @@ class AbstractConfigObject: raise NotImplementedError( "Attribute `{}` is not implemented. {}".format(name, self) ) - return super(AbstractConfigObject, self).__getattribute__(name) + return super(AbstractSettingObject, self).__getattribute__(name) def update_default_values(self, parent_values): raise NotImplementedError( From 111e11b9b15b244659b92baeecf9949648cb0eb3 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 14 Sep 2020 18:20:13 +0200 Subject: [PATCH 590/813] renamed configurations folder in pype co settings --- pype/api.py | 2 +- pype/{configurations => settings}/__init__.py | 0 .../defaults/project_anatomy/colorspace.json | 0 .../defaults/project_anatomy/dataflow.json | 0 .../defaults/project_anatomy/roots.json | 0 .../defaults/project_anatomy/templates.json | 0 .../defaults/project_configurations/ftrack/ftrack_config.json | 0 .../project_configurations/ftrack/ftrack_custom_attributes.json | 0 .../project_configurations/ftrack/partnership_ftrack_cred.json | 0 .../defaults/project_configurations/ftrack/plugins/server.json | 0 .../defaults/project_configurations/ftrack/plugins/user.json | 0 .../project_configurations/ftrack/project_defaults.json | 0 .../defaults/project_configurations/global/creator.json | 0 .../project_configurations/global/project_folder_structure.json | 0 .../defaults/project_configurations/global/sw_folders.json | 0 .../defaults/project_configurations/global/workfiles.json | 0 .../defaults/project_configurations/maya/capture.json | 0 .../project_configurations/muster/templates_mapping.json | 0 .../project_configurations/plugins/celaction/publish.json | 0 .../defaults/project_configurations/plugins/config.json | 0 .../defaults/project_configurations/plugins/ftrack/publish.json | 0 .../defaults/project_configurations/plugins/global/create.json | 0 .../defaults/project_configurations/plugins/global/filter.json | 0 .../defaults/project_configurations/plugins/global/load.json | 0 .../defaults/project_configurations/plugins/global/publish.json | 0 .../defaults/project_configurations/plugins/maya/create.json | 0 .../defaults/project_configurations/plugins/maya/filter.json | 0 .../defaults/project_configurations/plugins/maya/load.json | 0 .../defaults/project_configurations/plugins/maya/publish.json | 0 .../project_configurations/plugins/maya/workfile_build.json | 0 .../defaults/project_configurations/plugins/nuke/create.json | 0 .../defaults/project_configurations/plugins/nuke/load.json | 0 .../defaults/project_configurations/plugins/nuke/publish.json | 0 .../project_configurations/plugins/nuke/workfile_build.json | 0 .../project_configurations/plugins/nukestudio/filter.json | 0 .../project_configurations/plugins/nukestudio/publish.json | 0 .../defaults/project_configurations/plugins/resolve/create.json | 0 .../plugins/standalonepublisher/publish.json | 0 .../defaults/project_configurations/plugins/test/create.json | 0 .../defaults/project_configurations/plugins/test/publish.json | 0 .../defaults/project_configurations/premiere/asset_default.json | 0 .../defaults/project_configurations/premiere/rules_tasks.json | 0 .../project_configurations/standalonepublisher/families.json | 0 .../project_configurations/tools/slates/example_HD.json | 0 .../defaults/project_configurations/unreal/project_setup.json | 0 .../defaults/system_configurations/environments/avalon.json | 0 .../defaults/system_configurations/environments/blender.json | 0 .../defaults/system_configurations/environments/celaction.json | 0 .../defaults/system_configurations/environments/deadline.json | 0 .../defaults/system_configurations/environments/ftrack.json | 0 .../defaults/system_configurations/environments/global.json | 0 .../defaults/system_configurations/environments/harmony.json | 0 .../defaults/system_configurations/environments/houdini.json | 0 .../defaults/system_configurations/environments/maya.json | 0 .../defaults/system_configurations/environments/maya_2018.json | 0 .../defaults/system_configurations/environments/maya_2020.json | 0 .../defaults/system_configurations/environments/mayabatch.json | 0 .../system_configurations/environments/mayabatch_2019.json | 0 .../defaults/system_configurations/environments/mtoa_3.1.1.json | 0 .../defaults/system_configurations/environments/muster.json | 0 .../defaults/system_configurations/environments/nuke.json | 0 .../defaults/system_configurations/environments/nukestudio.json | 0 .../system_configurations/environments/nukestudio_10.0.json | 0 .../defaults/system_configurations/environments/nukex.json | 0 .../defaults/system_configurations/environments/nukex_10.0.json | 0 .../defaults/system_configurations/environments/photoshop.json | 0 .../defaults/system_configurations/environments/premiere.json | 0 .../defaults/system_configurations/environments/resolve.json | 0 .../system_configurations/environments/storyboardpro.json | 0 .../system_configurations/environments/unreal_4.24.json | 0 .../defaults/system_configurations/environments/vray_4300.json | 0 .../defaults/system_configurations/global/applications.json | 0 .../defaults/system_configurations/global/intent.json | 0 .../defaults/system_configurations/global/tools.json | 0 .../defaults/system_configurations/global/tray_modules.json | 0 .../defaults/system_configurations/launchers/blender_2.80.toml | 0 .../defaults/system_configurations/launchers/blender_2.81.toml | 0 .../defaults/system_configurations/launchers/blender_2.82.toml | 0 .../defaults/system_configurations/launchers/blender_2.83.toml | 0 .../system_configurations/launchers/celaction_local.toml | 0 .../system_configurations/launchers/celaction_publish.toml | 0 .../system_configurations/launchers/darwin/blender_2.82 | 0 .../defaults/system_configurations/launchers/darwin/harmony_17 | 0 .../system_configurations/launchers/darwin/harmony_17_launch | 0 .../defaults/system_configurations/launchers/darwin/python3 | 0 .../defaults/system_configurations/launchers/harmony_17.toml | 0 .../defaults/system_configurations/launchers/houdini_16.toml | 0 .../defaults/system_configurations/launchers/houdini_17.toml | 0 .../defaults/system_configurations/launchers/houdini_18.toml | 0 .../defaults/system_configurations/launchers/linux/maya2016 | 0 .../defaults/system_configurations/launchers/linux/maya2017 | 0 .../defaults/system_configurations/launchers/linux/maya2018 | 0 .../defaults/system_configurations/launchers/linux/maya2019 | 0 .../defaults/system_configurations/launchers/linux/maya2020 | 0 .../defaults/system_configurations/launchers/linux/nuke11.3 | 0 .../defaults/system_configurations/launchers/linux/nuke12.0 | 0 .../system_configurations/launchers/linux/nukestudio11.3 | 0 .../system_configurations/launchers/linux/nukestudio12.0 | 0 .../defaults/system_configurations/launchers/linux/nukex11.3 | 0 .../defaults/system_configurations/launchers/linux/nukex12.0 | 0 .../defaults/system_configurations/launchers/maya_2016.toml | 0 .../defaults/system_configurations/launchers/maya_2017.toml | 0 .../defaults/system_configurations/launchers/maya_2018.toml | 0 .../defaults/system_configurations/launchers/maya_2019.toml | 0 .../defaults/system_configurations/launchers/maya_2020.toml | 0 .../system_configurations/launchers/mayabatch_2019.toml | 0 .../system_configurations/launchers/mayabatch_2020.toml | 0 .../defaults/system_configurations/launchers/mayapy2016.toml | 0 .../defaults/system_configurations/launchers/mayapy2017.toml | 0 .../defaults/system_configurations/launchers/mayapy2018.toml | 0 .../defaults/system_configurations/launchers/mayapy2019.toml | 0 .../defaults/system_configurations/launchers/mayapy2020.toml | 0 .../defaults/system_configurations/launchers/myapp.toml | 0 .../defaults/system_configurations/launchers/nuke_10.0.toml | 0 .../defaults/system_configurations/launchers/nuke_11.0.toml | 0 .../defaults/system_configurations/launchers/nuke_11.2.toml | 0 .../defaults/system_configurations/launchers/nuke_11.3.toml | 0 .../defaults/system_configurations/launchers/nuke_12.0.toml | 0 .../system_configurations/launchers/nukestudio_10.0.toml | 0 .../system_configurations/launchers/nukestudio_11.0.toml | 0 .../system_configurations/launchers/nukestudio_11.2.toml | 0 .../system_configurations/launchers/nukestudio_11.3.toml | 0 .../system_configurations/launchers/nukestudio_12.0.toml | 0 .../defaults/system_configurations/launchers/nukex_10.0.toml | 0 .../defaults/system_configurations/launchers/nukex_11.0.toml | 0 .../defaults/system_configurations/launchers/nukex_11.2.toml | 0 .../defaults/system_configurations/launchers/nukex_11.3.toml | 0 .../defaults/system_configurations/launchers/nukex_12.0.toml | 0 .../system_configurations/launchers/photoshop_2020.toml | 0 .../defaults/system_configurations/launchers/premiere_2019.toml | 0 .../defaults/system_configurations/launchers/premiere_2020.toml | 0 .../defaults/system_configurations/launchers/python_2.toml | 0 .../defaults/system_configurations/launchers/python_3.toml | 0 .../defaults/system_configurations/launchers/resolve_16.toml | 0 .../defaults/system_configurations/launchers/shell.toml | 0 .../system_configurations/launchers/storyboardpro_7.toml | 0 .../defaults/system_configurations/launchers/unreal_4.24.toml | 0 .../system_configurations/launchers/windows/blender_2.80.bat | 0 .../system_configurations/launchers/windows/blender_2.81.bat | 0 .../system_configurations/launchers/windows/blender_2.82.bat | 0 .../system_configurations/launchers/windows/blender_2.83.bat | 0 .../system_configurations/launchers/windows/celaction_local.bat | 0 .../launchers/windows/celaction_publish.bat | 0 .../system_configurations/launchers/windows/harmony_17.bat | 0 .../system_configurations/launchers/windows/houdini_16.bat | 0 .../system_configurations/launchers/windows/houdini_17.bat | 0 .../system_configurations/launchers/windows/houdini_18.bat | 0 .../system_configurations/launchers/windows/maya2016.bat | 0 .../system_configurations/launchers/windows/maya2017.bat | 0 .../system_configurations/launchers/windows/maya2018.bat | 0 .../system_configurations/launchers/windows/maya2019.bat | 0 .../system_configurations/launchers/windows/maya2020.bat | 0 .../system_configurations/launchers/windows/mayabatch2019.bat | 0 .../system_configurations/launchers/windows/mayabatch2020.bat | 0 .../system_configurations/launchers/windows/mayapy2016.bat | 0 .../system_configurations/launchers/windows/mayapy2017.bat | 0 .../system_configurations/launchers/windows/mayapy2018.bat | 0 .../system_configurations/launchers/windows/mayapy2019.bat | 0 .../system_configurations/launchers/windows/mayapy2020.bat | 0 .../system_configurations/launchers/windows/nuke10.0.bat | 0 .../system_configurations/launchers/windows/nuke11.0.bat | 0 .../system_configurations/launchers/windows/nuke11.2.bat | 0 .../system_configurations/launchers/windows/nuke11.3.bat | 0 .../system_configurations/launchers/windows/nuke12.0.bat | 0 .../system_configurations/launchers/windows/nukestudio10.0.bat | 0 .../system_configurations/launchers/windows/nukestudio11.0.bat | 0 .../system_configurations/launchers/windows/nukestudio11.2.bat | 0 .../system_configurations/launchers/windows/nukestudio11.3.bat | 0 .../system_configurations/launchers/windows/nukestudio12.0.bat | 0 .../system_configurations/launchers/windows/nukex10.0.bat | 0 .../system_configurations/launchers/windows/nukex11.0.bat | 0 .../system_configurations/launchers/windows/nukex11.2.bat | 0 .../system_configurations/launchers/windows/nukex11.3.bat | 0 .../system_configurations/launchers/windows/nukex12.0.bat | 0 .../system_configurations/launchers/windows/photoshop_2020.bat | 0 .../launchers/windows/premiere_pro_2019.bat | 0 .../launchers/windows/premiere_pro_2020.bat | 0 .../system_configurations/launchers/windows/python3.bat | 0 .../system_configurations/launchers/windows/resolve_16.bat | 0 .../defaults/system_configurations/launchers/windows/shell.bat | 0 .../system_configurations/launchers/windows/storyboardpro_7.bat | 0 .../defaults/system_configurations/launchers/windows/unreal.bat | 0 .../system_configurations/muster/templates_mapping.json | 0 .../system_configurations/standalone_publish/families.json | 0 pype/{configurations => settings}/lib.py | 0 185 files changed, 1 insertion(+), 1 deletion(-) rename pype/{configurations => settings}/__init__.py (100%) rename pype/{configurations => settings}/defaults/project_anatomy/colorspace.json (100%) rename pype/{configurations => settings}/defaults/project_anatomy/dataflow.json (100%) rename pype/{configurations => settings}/defaults/project_anatomy/roots.json (100%) rename pype/{configurations => settings}/defaults/project_anatomy/templates.json (100%) rename pype/{configurations => settings}/defaults/project_configurations/ftrack/ftrack_config.json (100%) rename pype/{configurations => settings}/defaults/project_configurations/ftrack/ftrack_custom_attributes.json (100%) rename pype/{configurations => settings}/defaults/project_configurations/ftrack/partnership_ftrack_cred.json (100%) rename pype/{configurations => settings}/defaults/project_configurations/ftrack/plugins/server.json (100%) rename pype/{configurations => settings}/defaults/project_configurations/ftrack/plugins/user.json (100%) rename pype/{configurations => settings}/defaults/project_configurations/ftrack/project_defaults.json (100%) rename pype/{configurations => settings}/defaults/project_configurations/global/creator.json (100%) rename pype/{configurations => settings}/defaults/project_configurations/global/project_folder_structure.json (100%) rename pype/{configurations => settings}/defaults/project_configurations/global/sw_folders.json (100%) rename pype/{configurations => settings}/defaults/project_configurations/global/workfiles.json (100%) rename pype/{configurations => settings}/defaults/project_configurations/maya/capture.json (100%) rename pype/{configurations => settings}/defaults/project_configurations/muster/templates_mapping.json (100%) rename pype/{configurations => settings}/defaults/project_configurations/plugins/celaction/publish.json (100%) rename pype/{configurations => settings}/defaults/project_configurations/plugins/config.json (100%) rename pype/{configurations => settings}/defaults/project_configurations/plugins/ftrack/publish.json (100%) rename pype/{configurations => settings}/defaults/project_configurations/plugins/global/create.json (100%) rename pype/{configurations => settings}/defaults/project_configurations/plugins/global/filter.json (100%) rename pype/{configurations => settings}/defaults/project_configurations/plugins/global/load.json (100%) rename pype/{configurations => settings}/defaults/project_configurations/plugins/global/publish.json (100%) rename pype/{configurations => settings}/defaults/project_configurations/plugins/maya/create.json (100%) rename pype/{configurations => settings}/defaults/project_configurations/plugins/maya/filter.json (100%) rename pype/{configurations => settings}/defaults/project_configurations/plugins/maya/load.json (100%) rename pype/{configurations => settings}/defaults/project_configurations/plugins/maya/publish.json (100%) rename pype/{configurations => settings}/defaults/project_configurations/plugins/maya/workfile_build.json (100%) rename pype/{configurations => settings}/defaults/project_configurations/plugins/nuke/create.json (100%) rename pype/{configurations => settings}/defaults/project_configurations/plugins/nuke/load.json (100%) rename pype/{configurations => settings}/defaults/project_configurations/plugins/nuke/publish.json (100%) rename pype/{configurations => settings}/defaults/project_configurations/plugins/nuke/workfile_build.json (100%) rename pype/{configurations => settings}/defaults/project_configurations/plugins/nukestudio/filter.json (100%) rename pype/{configurations => settings}/defaults/project_configurations/plugins/nukestudio/publish.json (100%) rename pype/{configurations => settings}/defaults/project_configurations/plugins/resolve/create.json (100%) rename pype/{configurations => settings}/defaults/project_configurations/plugins/standalonepublisher/publish.json (100%) rename pype/{configurations => settings}/defaults/project_configurations/plugins/test/create.json (100%) rename pype/{configurations => settings}/defaults/project_configurations/plugins/test/publish.json (100%) rename pype/{configurations => settings}/defaults/project_configurations/premiere/asset_default.json (100%) rename pype/{configurations => settings}/defaults/project_configurations/premiere/rules_tasks.json (100%) rename pype/{configurations => settings}/defaults/project_configurations/standalonepublisher/families.json (100%) rename pype/{configurations => settings}/defaults/project_configurations/tools/slates/example_HD.json (100%) rename pype/{configurations => settings}/defaults/project_configurations/unreal/project_setup.json (100%) rename pype/{configurations => settings}/defaults/system_configurations/environments/avalon.json (100%) rename pype/{configurations => settings}/defaults/system_configurations/environments/blender.json (100%) rename pype/{configurations => settings}/defaults/system_configurations/environments/celaction.json (100%) rename pype/{configurations => settings}/defaults/system_configurations/environments/deadline.json (100%) rename pype/{configurations => settings}/defaults/system_configurations/environments/ftrack.json (100%) rename pype/{configurations => settings}/defaults/system_configurations/environments/global.json (100%) rename pype/{configurations => settings}/defaults/system_configurations/environments/harmony.json (100%) rename pype/{configurations => settings}/defaults/system_configurations/environments/houdini.json (100%) rename pype/{configurations => settings}/defaults/system_configurations/environments/maya.json (100%) rename pype/{configurations => settings}/defaults/system_configurations/environments/maya_2018.json (100%) rename pype/{configurations => settings}/defaults/system_configurations/environments/maya_2020.json (100%) rename pype/{configurations => settings}/defaults/system_configurations/environments/mayabatch.json (100%) rename pype/{configurations => settings}/defaults/system_configurations/environments/mayabatch_2019.json (100%) rename pype/{configurations => settings}/defaults/system_configurations/environments/mtoa_3.1.1.json (100%) rename pype/{configurations => settings}/defaults/system_configurations/environments/muster.json (100%) rename pype/{configurations => settings}/defaults/system_configurations/environments/nuke.json (100%) rename pype/{configurations => settings}/defaults/system_configurations/environments/nukestudio.json (100%) rename pype/{configurations => settings}/defaults/system_configurations/environments/nukestudio_10.0.json (100%) rename pype/{configurations => settings}/defaults/system_configurations/environments/nukex.json (100%) rename pype/{configurations => settings}/defaults/system_configurations/environments/nukex_10.0.json (100%) rename pype/{configurations => settings}/defaults/system_configurations/environments/photoshop.json (100%) rename pype/{configurations => settings}/defaults/system_configurations/environments/premiere.json (100%) rename pype/{configurations => settings}/defaults/system_configurations/environments/resolve.json (100%) rename pype/{configurations => settings}/defaults/system_configurations/environments/storyboardpro.json (100%) rename pype/{configurations => settings}/defaults/system_configurations/environments/unreal_4.24.json (100%) rename pype/{configurations => settings}/defaults/system_configurations/environments/vray_4300.json (100%) rename pype/{configurations => settings}/defaults/system_configurations/global/applications.json (100%) rename pype/{configurations => settings}/defaults/system_configurations/global/intent.json (100%) rename pype/{configurations => settings}/defaults/system_configurations/global/tools.json (100%) rename pype/{configurations => settings}/defaults/system_configurations/global/tray_modules.json (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/blender_2.80.toml (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/blender_2.81.toml (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/blender_2.82.toml (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/blender_2.83.toml (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/celaction_local.toml (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/celaction_publish.toml (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/darwin/blender_2.82 (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/darwin/harmony_17 (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/darwin/harmony_17_launch (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/darwin/python3 (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/harmony_17.toml (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/houdini_16.toml (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/houdini_17.toml (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/houdini_18.toml (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/linux/maya2016 (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/linux/maya2017 (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/linux/maya2018 (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/linux/maya2019 (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/linux/maya2020 (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/linux/nuke11.3 (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/linux/nuke12.0 (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/linux/nukestudio11.3 (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/linux/nukestudio12.0 (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/linux/nukex11.3 (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/linux/nukex12.0 (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/maya_2016.toml (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/maya_2017.toml (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/maya_2018.toml (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/maya_2019.toml (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/maya_2020.toml (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/mayabatch_2019.toml (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/mayabatch_2020.toml (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/mayapy2016.toml (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/mayapy2017.toml (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/mayapy2018.toml (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/mayapy2019.toml (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/mayapy2020.toml (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/myapp.toml (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/nuke_10.0.toml (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/nuke_11.0.toml (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/nuke_11.2.toml (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/nuke_11.3.toml (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/nuke_12.0.toml (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/nukestudio_10.0.toml (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/nukestudio_11.0.toml (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/nukestudio_11.2.toml (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/nukestudio_11.3.toml (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/nukestudio_12.0.toml (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/nukex_10.0.toml (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/nukex_11.0.toml (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/nukex_11.2.toml (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/nukex_11.3.toml (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/nukex_12.0.toml (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/photoshop_2020.toml (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/premiere_2019.toml (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/premiere_2020.toml (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/python_2.toml (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/python_3.toml (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/resolve_16.toml (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/shell.toml (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/storyboardpro_7.toml (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/unreal_4.24.toml (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/windows/blender_2.80.bat (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/windows/blender_2.81.bat (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/windows/blender_2.82.bat (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/windows/blender_2.83.bat (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/windows/celaction_local.bat (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/windows/celaction_publish.bat (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/windows/harmony_17.bat (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/windows/houdini_16.bat (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/windows/houdini_17.bat (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/windows/houdini_18.bat (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/windows/maya2016.bat (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/windows/maya2017.bat (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/windows/maya2018.bat (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/windows/maya2019.bat (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/windows/maya2020.bat (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/windows/mayabatch2019.bat (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/windows/mayabatch2020.bat (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/windows/mayapy2016.bat (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/windows/mayapy2017.bat (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/windows/mayapy2018.bat (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/windows/mayapy2019.bat (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/windows/mayapy2020.bat (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/windows/nuke10.0.bat (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/windows/nuke11.0.bat (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/windows/nuke11.2.bat (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/windows/nuke11.3.bat (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/windows/nuke12.0.bat (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/windows/nukestudio10.0.bat (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/windows/nukestudio11.0.bat (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/windows/nukestudio11.2.bat (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/windows/nukestudio11.3.bat (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/windows/nukestudio12.0.bat (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/windows/nukex10.0.bat (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/windows/nukex11.0.bat (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/windows/nukex11.2.bat (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/windows/nukex11.3.bat (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/windows/nukex12.0.bat (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/windows/photoshop_2020.bat (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/windows/premiere_pro_2019.bat (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/windows/premiere_pro_2020.bat (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/windows/python3.bat (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/windows/resolve_16.bat (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/windows/shell.bat (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/windows/storyboardpro_7.bat (100%) rename pype/{configurations => settings}/defaults/system_configurations/launchers/windows/unreal.bat (100%) rename pype/{configurations => settings}/defaults/system_configurations/muster/templates_mapping.json (100%) rename pype/{configurations => settings}/defaults/system_configurations/standalone_publish/families.json (100%) rename pype/{configurations => settings}/lib.py (100%) diff --git a/pype/api.py b/pype/api.py index c5cd28d4de..0b3439c4c2 100644 --- a/pype/api.py +++ b/pype/api.py @@ -1,4 +1,4 @@ -from .configurations.config import ( +from .settings import ( system_configurations, project_configurations ) diff --git a/pype/configurations/__init__.py b/pype/settings/__init__.py similarity index 100% rename from pype/configurations/__init__.py rename to pype/settings/__init__.py diff --git a/pype/configurations/defaults/project_anatomy/colorspace.json b/pype/settings/defaults/project_anatomy/colorspace.json similarity index 100% rename from pype/configurations/defaults/project_anatomy/colorspace.json rename to pype/settings/defaults/project_anatomy/colorspace.json diff --git a/pype/configurations/defaults/project_anatomy/dataflow.json b/pype/settings/defaults/project_anatomy/dataflow.json similarity index 100% rename from pype/configurations/defaults/project_anatomy/dataflow.json rename to pype/settings/defaults/project_anatomy/dataflow.json diff --git a/pype/configurations/defaults/project_anatomy/roots.json b/pype/settings/defaults/project_anatomy/roots.json similarity index 100% rename from pype/configurations/defaults/project_anatomy/roots.json rename to pype/settings/defaults/project_anatomy/roots.json diff --git a/pype/configurations/defaults/project_anatomy/templates.json b/pype/settings/defaults/project_anatomy/templates.json similarity index 100% rename from pype/configurations/defaults/project_anatomy/templates.json rename to pype/settings/defaults/project_anatomy/templates.json diff --git a/pype/configurations/defaults/project_configurations/ftrack/ftrack_config.json b/pype/settings/defaults/project_configurations/ftrack/ftrack_config.json similarity index 100% rename from pype/configurations/defaults/project_configurations/ftrack/ftrack_config.json rename to pype/settings/defaults/project_configurations/ftrack/ftrack_config.json diff --git a/pype/configurations/defaults/project_configurations/ftrack/ftrack_custom_attributes.json b/pype/settings/defaults/project_configurations/ftrack/ftrack_custom_attributes.json similarity index 100% rename from pype/configurations/defaults/project_configurations/ftrack/ftrack_custom_attributes.json rename to pype/settings/defaults/project_configurations/ftrack/ftrack_custom_attributes.json diff --git a/pype/configurations/defaults/project_configurations/ftrack/partnership_ftrack_cred.json b/pype/settings/defaults/project_configurations/ftrack/partnership_ftrack_cred.json similarity index 100% rename from pype/configurations/defaults/project_configurations/ftrack/partnership_ftrack_cred.json rename to pype/settings/defaults/project_configurations/ftrack/partnership_ftrack_cred.json diff --git a/pype/configurations/defaults/project_configurations/ftrack/plugins/server.json b/pype/settings/defaults/project_configurations/ftrack/plugins/server.json similarity index 100% rename from pype/configurations/defaults/project_configurations/ftrack/plugins/server.json rename to pype/settings/defaults/project_configurations/ftrack/plugins/server.json diff --git a/pype/configurations/defaults/project_configurations/ftrack/plugins/user.json b/pype/settings/defaults/project_configurations/ftrack/plugins/user.json similarity index 100% rename from pype/configurations/defaults/project_configurations/ftrack/plugins/user.json rename to pype/settings/defaults/project_configurations/ftrack/plugins/user.json diff --git a/pype/configurations/defaults/project_configurations/ftrack/project_defaults.json b/pype/settings/defaults/project_configurations/ftrack/project_defaults.json similarity index 100% rename from pype/configurations/defaults/project_configurations/ftrack/project_defaults.json rename to pype/settings/defaults/project_configurations/ftrack/project_defaults.json diff --git a/pype/configurations/defaults/project_configurations/global/creator.json b/pype/settings/defaults/project_configurations/global/creator.json similarity index 100% rename from pype/configurations/defaults/project_configurations/global/creator.json rename to pype/settings/defaults/project_configurations/global/creator.json diff --git a/pype/configurations/defaults/project_configurations/global/project_folder_structure.json b/pype/settings/defaults/project_configurations/global/project_folder_structure.json similarity index 100% rename from pype/configurations/defaults/project_configurations/global/project_folder_structure.json rename to pype/settings/defaults/project_configurations/global/project_folder_structure.json diff --git a/pype/configurations/defaults/project_configurations/global/sw_folders.json b/pype/settings/defaults/project_configurations/global/sw_folders.json similarity index 100% rename from pype/configurations/defaults/project_configurations/global/sw_folders.json rename to pype/settings/defaults/project_configurations/global/sw_folders.json diff --git a/pype/configurations/defaults/project_configurations/global/workfiles.json b/pype/settings/defaults/project_configurations/global/workfiles.json similarity index 100% rename from pype/configurations/defaults/project_configurations/global/workfiles.json rename to pype/settings/defaults/project_configurations/global/workfiles.json diff --git a/pype/configurations/defaults/project_configurations/maya/capture.json b/pype/settings/defaults/project_configurations/maya/capture.json similarity index 100% rename from pype/configurations/defaults/project_configurations/maya/capture.json rename to pype/settings/defaults/project_configurations/maya/capture.json diff --git a/pype/configurations/defaults/project_configurations/muster/templates_mapping.json b/pype/settings/defaults/project_configurations/muster/templates_mapping.json similarity index 100% rename from pype/configurations/defaults/project_configurations/muster/templates_mapping.json rename to pype/settings/defaults/project_configurations/muster/templates_mapping.json diff --git a/pype/configurations/defaults/project_configurations/plugins/celaction/publish.json b/pype/settings/defaults/project_configurations/plugins/celaction/publish.json similarity index 100% rename from pype/configurations/defaults/project_configurations/plugins/celaction/publish.json rename to pype/settings/defaults/project_configurations/plugins/celaction/publish.json diff --git a/pype/configurations/defaults/project_configurations/plugins/config.json b/pype/settings/defaults/project_configurations/plugins/config.json similarity index 100% rename from pype/configurations/defaults/project_configurations/plugins/config.json rename to pype/settings/defaults/project_configurations/plugins/config.json diff --git a/pype/configurations/defaults/project_configurations/plugins/ftrack/publish.json b/pype/settings/defaults/project_configurations/plugins/ftrack/publish.json similarity index 100% rename from pype/configurations/defaults/project_configurations/plugins/ftrack/publish.json rename to pype/settings/defaults/project_configurations/plugins/ftrack/publish.json diff --git a/pype/configurations/defaults/project_configurations/plugins/global/create.json b/pype/settings/defaults/project_configurations/plugins/global/create.json similarity index 100% rename from pype/configurations/defaults/project_configurations/plugins/global/create.json rename to pype/settings/defaults/project_configurations/plugins/global/create.json diff --git a/pype/configurations/defaults/project_configurations/plugins/global/filter.json b/pype/settings/defaults/project_configurations/plugins/global/filter.json similarity index 100% rename from pype/configurations/defaults/project_configurations/plugins/global/filter.json rename to pype/settings/defaults/project_configurations/plugins/global/filter.json diff --git a/pype/configurations/defaults/project_configurations/plugins/global/load.json b/pype/settings/defaults/project_configurations/plugins/global/load.json similarity index 100% rename from pype/configurations/defaults/project_configurations/plugins/global/load.json rename to pype/settings/defaults/project_configurations/plugins/global/load.json diff --git a/pype/configurations/defaults/project_configurations/plugins/global/publish.json b/pype/settings/defaults/project_configurations/plugins/global/publish.json similarity index 100% rename from pype/configurations/defaults/project_configurations/plugins/global/publish.json rename to pype/settings/defaults/project_configurations/plugins/global/publish.json diff --git a/pype/configurations/defaults/project_configurations/plugins/maya/create.json b/pype/settings/defaults/project_configurations/plugins/maya/create.json similarity index 100% rename from pype/configurations/defaults/project_configurations/plugins/maya/create.json rename to pype/settings/defaults/project_configurations/plugins/maya/create.json diff --git a/pype/configurations/defaults/project_configurations/plugins/maya/filter.json b/pype/settings/defaults/project_configurations/plugins/maya/filter.json similarity index 100% rename from pype/configurations/defaults/project_configurations/plugins/maya/filter.json rename to pype/settings/defaults/project_configurations/plugins/maya/filter.json diff --git a/pype/configurations/defaults/project_configurations/plugins/maya/load.json b/pype/settings/defaults/project_configurations/plugins/maya/load.json similarity index 100% rename from pype/configurations/defaults/project_configurations/plugins/maya/load.json rename to pype/settings/defaults/project_configurations/plugins/maya/load.json diff --git a/pype/configurations/defaults/project_configurations/plugins/maya/publish.json b/pype/settings/defaults/project_configurations/plugins/maya/publish.json similarity index 100% rename from pype/configurations/defaults/project_configurations/plugins/maya/publish.json rename to pype/settings/defaults/project_configurations/plugins/maya/publish.json diff --git a/pype/configurations/defaults/project_configurations/plugins/maya/workfile_build.json b/pype/settings/defaults/project_configurations/plugins/maya/workfile_build.json similarity index 100% rename from pype/configurations/defaults/project_configurations/plugins/maya/workfile_build.json rename to pype/settings/defaults/project_configurations/plugins/maya/workfile_build.json diff --git a/pype/configurations/defaults/project_configurations/plugins/nuke/create.json b/pype/settings/defaults/project_configurations/plugins/nuke/create.json similarity index 100% rename from pype/configurations/defaults/project_configurations/plugins/nuke/create.json rename to pype/settings/defaults/project_configurations/plugins/nuke/create.json diff --git a/pype/configurations/defaults/project_configurations/plugins/nuke/load.json b/pype/settings/defaults/project_configurations/plugins/nuke/load.json similarity index 100% rename from pype/configurations/defaults/project_configurations/plugins/nuke/load.json rename to pype/settings/defaults/project_configurations/plugins/nuke/load.json diff --git a/pype/configurations/defaults/project_configurations/plugins/nuke/publish.json b/pype/settings/defaults/project_configurations/plugins/nuke/publish.json similarity index 100% rename from pype/configurations/defaults/project_configurations/plugins/nuke/publish.json rename to pype/settings/defaults/project_configurations/plugins/nuke/publish.json diff --git a/pype/configurations/defaults/project_configurations/plugins/nuke/workfile_build.json b/pype/settings/defaults/project_configurations/plugins/nuke/workfile_build.json similarity index 100% rename from pype/configurations/defaults/project_configurations/plugins/nuke/workfile_build.json rename to pype/settings/defaults/project_configurations/plugins/nuke/workfile_build.json diff --git a/pype/configurations/defaults/project_configurations/plugins/nukestudio/filter.json b/pype/settings/defaults/project_configurations/plugins/nukestudio/filter.json similarity index 100% rename from pype/configurations/defaults/project_configurations/plugins/nukestudio/filter.json rename to pype/settings/defaults/project_configurations/plugins/nukestudio/filter.json diff --git a/pype/configurations/defaults/project_configurations/plugins/nukestudio/publish.json b/pype/settings/defaults/project_configurations/plugins/nukestudio/publish.json similarity index 100% rename from pype/configurations/defaults/project_configurations/plugins/nukestudio/publish.json rename to pype/settings/defaults/project_configurations/plugins/nukestudio/publish.json diff --git a/pype/configurations/defaults/project_configurations/plugins/resolve/create.json b/pype/settings/defaults/project_configurations/plugins/resolve/create.json similarity index 100% rename from pype/configurations/defaults/project_configurations/plugins/resolve/create.json rename to pype/settings/defaults/project_configurations/plugins/resolve/create.json diff --git a/pype/configurations/defaults/project_configurations/plugins/standalonepublisher/publish.json b/pype/settings/defaults/project_configurations/plugins/standalonepublisher/publish.json similarity index 100% rename from pype/configurations/defaults/project_configurations/plugins/standalonepublisher/publish.json rename to pype/settings/defaults/project_configurations/plugins/standalonepublisher/publish.json diff --git a/pype/configurations/defaults/project_configurations/plugins/test/create.json b/pype/settings/defaults/project_configurations/plugins/test/create.json similarity index 100% rename from pype/configurations/defaults/project_configurations/plugins/test/create.json rename to pype/settings/defaults/project_configurations/plugins/test/create.json diff --git a/pype/configurations/defaults/project_configurations/plugins/test/publish.json b/pype/settings/defaults/project_configurations/plugins/test/publish.json similarity index 100% rename from pype/configurations/defaults/project_configurations/plugins/test/publish.json rename to pype/settings/defaults/project_configurations/plugins/test/publish.json diff --git a/pype/configurations/defaults/project_configurations/premiere/asset_default.json b/pype/settings/defaults/project_configurations/premiere/asset_default.json similarity index 100% rename from pype/configurations/defaults/project_configurations/premiere/asset_default.json rename to pype/settings/defaults/project_configurations/premiere/asset_default.json diff --git a/pype/configurations/defaults/project_configurations/premiere/rules_tasks.json b/pype/settings/defaults/project_configurations/premiere/rules_tasks.json similarity index 100% rename from pype/configurations/defaults/project_configurations/premiere/rules_tasks.json rename to pype/settings/defaults/project_configurations/premiere/rules_tasks.json diff --git a/pype/configurations/defaults/project_configurations/standalonepublisher/families.json b/pype/settings/defaults/project_configurations/standalonepublisher/families.json similarity index 100% rename from pype/configurations/defaults/project_configurations/standalonepublisher/families.json rename to pype/settings/defaults/project_configurations/standalonepublisher/families.json diff --git a/pype/configurations/defaults/project_configurations/tools/slates/example_HD.json b/pype/settings/defaults/project_configurations/tools/slates/example_HD.json similarity index 100% rename from pype/configurations/defaults/project_configurations/tools/slates/example_HD.json rename to pype/settings/defaults/project_configurations/tools/slates/example_HD.json diff --git a/pype/configurations/defaults/project_configurations/unreal/project_setup.json b/pype/settings/defaults/project_configurations/unreal/project_setup.json similarity index 100% rename from pype/configurations/defaults/project_configurations/unreal/project_setup.json rename to pype/settings/defaults/project_configurations/unreal/project_setup.json diff --git a/pype/configurations/defaults/system_configurations/environments/avalon.json b/pype/settings/defaults/system_configurations/environments/avalon.json similarity index 100% rename from pype/configurations/defaults/system_configurations/environments/avalon.json rename to pype/settings/defaults/system_configurations/environments/avalon.json diff --git a/pype/configurations/defaults/system_configurations/environments/blender.json b/pype/settings/defaults/system_configurations/environments/blender.json similarity index 100% rename from pype/configurations/defaults/system_configurations/environments/blender.json rename to pype/settings/defaults/system_configurations/environments/blender.json diff --git a/pype/configurations/defaults/system_configurations/environments/celaction.json b/pype/settings/defaults/system_configurations/environments/celaction.json similarity index 100% rename from pype/configurations/defaults/system_configurations/environments/celaction.json rename to pype/settings/defaults/system_configurations/environments/celaction.json diff --git a/pype/configurations/defaults/system_configurations/environments/deadline.json b/pype/settings/defaults/system_configurations/environments/deadline.json similarity index 100% rename from pype/configurations/defaults/system_configurations/environments/deadline.json rename to pype/settings/defaults/system_configurations/environments/deadline.json diff --git a/pype/configurations/defaults/system_configurations/environments/ftrack.json b/pype/settings/defaults/system_configurations/environments/ftrack.json similarity index 100% rename from pype/configurations/defaults/system_configurations/environments/ftrack.json rename to pype/settings/defaults/system_configurations/environments/ftrack.json diff --git a/pype/configurations/defaults/system_configurations/environments/global.json b/pype/settings/defaults/system_configurations/environments/global.json similarity index 100% rename from pype/configurations/defaults/system_configurations/environments/global.json rename to pype/settings/defaults/system_configurations/environments/global.json diff --git a/pype/configurations/defaults/system_configurations/environments/harmony.json b/pype/settings/defaults/system_configurations/environments/harmony.json similarity index 100% rename from pype/configurations/defaults/system_configurations/environments/harmony.json rename to pype/settings/defaults/system_configurations/environments/harmony.json diff --git a/pype/configurations/defaults/system_configurations/environments/houdini.json b/pype/settings/defaults/system_configurations/environments/houdini.json similarity index 100% rename from pype/configurations/defaults/system_configurations/environments/houdini.json rename to pype/settings/defaults/system_configurations/environments/houdini.json diff --git a/pype/configurations/defaults/system_configurations/environments/maya.json b/pype/settings/defaults/system_configurations/environments/maya.json similarity index 100% rename from pype/configurations/defaults/system_configurations/environments/maya.json rename to pype/settings/defaults/system_configurations/environments/maya.json diff --git a/pype/configurations/defaults/system_configurations/environments/maya_2018.json b/pype/settings/defaults/system_configurations/environments/maya_2018.json similarity index 100% rename from pype/configurations/defaults/system_configurations/environments/maya_2018.json rename to pype/settings/defaults/system_configurations/environments/maya_2018.json diff --git a/pype/configurations/defaults/system_configurations/environments/maya_2020.json b/pype/settings/defaults/system_configurations/environments/maya_2020.json similarity index 100% rename from pype/configurations/defaults/system_configurations/environments/maya_2020.json rename to pype/settings/defaults/system_configurations/environments/maya_2020.json diff --git a/pype/configurations/defaults/system_configurations/environments/mayabatch.json b/pype/settings/defaults/system_configurations/environments/mayabatch.json similarity index 100% rename from pype/configurations/defaults/system_configurations/environments/mayabatch.json rename to pype/settings/defaults/system_configurations/environments/mayabatch.json diff --git a/pype/configurations/defaults/system_configurations/environments/mayabatch_2019.json b/pype/settings/defaults/system_configurations/environments/mayabatch_2019.json similarity index 100% rename from pype/configurations/defaults/system_configurations/environments/mayabatch_2019.json rename to pype/settings/defaults/system_configurations/environments/mayabatch_2019.json diff --git a/pype/configurations/defaults/system_configurations/environments/mtoa_3.1.1.json b/pype/settings/defaults/system_configurations/environments/mtoa_3.1.1.json similarity index 100% rename from pype/configurations/defaults/system_configurations/environments/mtoa_3.1.1.json rename to pype/settings/defaults/system_configurations/environments/mtoa_3.1.1.json diff --git a/pype/configurations/defaults/system_configurations/environments/muster.json b/pype/settings/defaults/system_configurations/environments/muster.json similarity index 100% rename from pype/configurations/defaults/system_configurations/environments/muster.json rename to pype/settings/defaults/system_configurations/environments/muster.json diff --git a/pype/configurations/defaults/system_configurations/environments/nuke.json b/pype/settings/defaults/system_configurations/environments/nuke.json similarity index 100% rename from pype/configurations/defaults/system_configurations/environments/nuke.json rename to pype/settings/defaults/system_configurations/environments/nuke.json diff --git a/pype/configurations/defaults/system_configurations/environments/nukestudio.json b/pype/settings/defaults/system_configurations/environments/nukestudio.json similarity index 100% rename from pype/configurations/defaults/system_configurations/environments/nukestudio.json rename to pype/settings/defaults/system_configurations/environments/nukestudio.json diff --git a/pype/configurations/defaults/system_configurations/environments/nukestudio_10.0.json b/pype/settings/defaults/system_configurations/environments/nukestudio_10.0.json similarity index 100% rename from pype/configurations/defaults/system_configurations/environments/nukestudio_10.0.json rename to pype/settings/defaults/system_configurations/environments/nukestudio_10.0.json diff --git a/pype/configurations/defaults/system_configurations/environments/nukex.json b/pype/settings/defaults/system_configurations/environments/nukex.json similarity index 100% rename from pype/configurations/defaults/system_configurations/environments/nukex.json rename to pype/settings/defaults/system_configurations/environments/nukex.json diff --git a/pype/configurations/defaults/system_configurations/environments/nukex_10.0.json b/pype/settings/defaults/system_configurations/environments/nukex_10.0.json similarity index 100% rename from pype/configurations/defaults/system_configurations/environments/nukex_10.0.json rename to pype/settings/defaults/system_configurations/environments/nukex_10.0.json diff --git a/pype/configurations/defaults/system_configurations/environments/photoshop.json b/pype/settings/defaults/system_configurations/environments/photoshop.json similarity index 100% rename from pype/configurations/defaults/system_configurations/environments/photoshop.json rename to pype/settings/defaults/system_configurations/environments/photoshop.json diff --git a/pype/configurations/defaults/system_configurations/environments/premiere.json b/pype/settings/defaults/system_configurations/environments/premiere.json similarity index 100% rename from pype/configurations/defaults/system_configurations/environments/premiere.json rename to pype/settings/defaults/system_configurations/environments/premiere.json diff --git a/pype/configurations/defaults/system_configurations/environments/resolve.json b/pype/settings/defaults/system_configurations/environments/resolve.json similarity index 100% rename from pype/configurations/defaults/system_configurations/environments/resolve.json rename to pype/settings/defaults/system_configurations/environments/resolve.json diff --git a/pype/configurations/defaults/system_configurations/environments/storyboardpro.json b/pype/settings/defaults/system_configurations/environments/storyboardpro.json similarity index 100% rename from pype/configurations/defaults/system_configurations/environments/storyboardpro.json rename to pype/settings/defaults/system_configurations/environments/storyboardpro.json diff --git a/pype/configurations/defaults/system_configurations/environments/unreal_4.24.json b/pype/settings/defaults/system_configurations/environments/unreal_4.24.json similarity index 100% rename from pype/configurations/defaults/system_configurations/environments/unreal_4.24.json rename to pype/settings/defaults/system_configurations/environments/unreal_4.24.json diff --git a/pype/configurations/defaults/system_configurations/environments/vray_4300.json b/pype/settings/defaults/system_configurations/environments/vray_4300.json similarity index 100% rename from pype/configurations/defaults/system_configurations/environments/vray_4300.json rename to pype/settings/defaults/system_configurations/environments/vray_4300.json diff --git a/pype/configurations/defaults/system_configurations/global/applications.json b/pype/settings/defaults/system_configurations/global/applications.json similarity index 100% rename from pype/configurations/defaults/system_configurations/global/applications.json rename to pype/settings/defaults/system_configurations/global/applications.json diff --git a/pype/configurations/defaults/system_configurations/global/intent.json b/pype/settings/defaults/system_configurations/global/intent.json similarity index 100% rename from pype/configurations/defaults/system_configurations/global/intent.json rename to pype/settings/defaults/system_configurations/global/intent.json diff --git a/pype/configurations/defaults/system_configurations/global/tools.json b/pype/settings/defaults/system_configurations/global/tools.json similarity index 100% rename from pype/configurations/defaults/system_configurations/global/tools.json rename to pype/settings/defaults/system_configurations/global/tools.json diff --git a/pype/configurations/defaults/system_configurations/global/tray_modules.json b/pype/settings/defaults/system_configurations/global/tray_modules.json similarity index 100% rename from pype/configurations/defaults/system_configurations/global/tray_modules.json rename to pype/settings/defaults/system_configurations/global/tray_modules.json diff --git a/pype/configurations/defaults/system_configurations/launchers/blender_2.80.toml b/pype/settings/defaults/system_configurations/launchers/blender_2.80.toml similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/blender_2.80.toml rename to pype/settings/defaults/system_configurations/launchers/blender_2.80.toml diff --git a/pype/configurations/defaults/system_configurations/launchers/blender_2.81.toml b/pype/settings/defaults/system_configurations/launchers/blender_2.81.toml similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/blender_2.81.toml rename to pype/settings/defaults/system_configurations/launchers/blender_2.81.toml diff --git a/pype/configurations/defaults/system_configurations/launchers/blender_2.82.toml b/pype/settings/defaults/system_configurations/launchers/blender_2.82.toml similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/blender_2.82.toml rename to pype/settings/defaults/system_configurations/launchers/blender_2.82.toml diff --git a/pype/configurations/defaults/system_configurations/launchers/blender_2.83.toml b/pype/settings/defaults/system_configurations/launchers/blender_2.83.toml similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/blender_2.83.toml rename to pype/settings/defaults/system_configurations/launchers/blender_2.83.toml diff --git a/pype/configurations/defaults/system_configurations/launchers/celaction_local.toml b/pype/settings/defaults/system_configurations/launchers/celaction_local.toml similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/celaction_local.toml rename to pype/settings/defaults/system_configurations/launchers/celaction_local.toml diff --git a/pype/configurations/defaults/system_configurations/launchers/celaction_publish.toml b/pype/settings/defaults/system_configurations/launchers/celaction_publish.toml similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/celaction_publish.toml rename to pype/settings/defaults/system_configurations/launchers/celaction_publish.toml diff --git a/pype/configurations/defaults/system_configurations/launchers/darwin/blender_2.82 b/pype/settings/defaults/system_configurations/launchers/darwin/blender_2.82 similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/darwin/blender_2.82 rename to pype/settings/defaults/system_configurations/launchers/darwin/blender_2.82 diff --git a/pype/configurations/defaults/system_configurations/launchers/darwin/harmony_17 b/pype/settings/defaults/system_configurations/launchers/darwin/harmony_17 similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/darwin/harmony_17 rename to pype/settings/defaults/system_configurations/launchers/darwin/harmony_17 diff --git a/pype/configurations/defaults/system_configurations/launchers/darwin/harmony_17_launch b/pype/settings/defaults/system_configurations/launchers/darwin/harmony_17_launch similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/darwin/harmony_17_launch rename to pype/settings/defaults/system_configurations/launchers/darwin/harmony_17_launch diff --git a/pype/configurations/defaults/system_configurations/launchers/darwin/python3 b/pype/settings/defaults/system_configurations/launchers/darwin/python3 similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/darwin/python3 rename to pype/settings/defaults/system_configurations/launchers/darwin/python3 diff --git a/pype/configurations/defaults/system_configurations/launchers/harmony_17.toml b/pype/settings/defaults/system_configurations/launchers/harmony_17.toml similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/harmony_17.toml rename to pype/settings/defaults/system_configurations/launchers/harmony_17.toml diff --git a/pype/configurations/defaults/system_configurations/launchers/houdini_16.toml b/pype/settings/defaults/system_configurations/launchers/houdini_16.toml similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/houdini_16.toml rename to pype/settings/defaults/system_configurations/launchers/houdini_16.toml diff --git a/pype/configurations/defaults/system_configurations/launchers/houdini_17.toml b/pype/settings/defaults/system_configurations/launchers/houdini_17.toml similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/houdini_17.toml rename to pype/settings/defaults/system_configurations/launchers/houdini_17.toml diff --git a/pype/configurations/defaults/system_configurations/launchers/houdini_18.toml b/pype/settings/defaults/system_configurations/launchers/houdini_18.toml similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/houdini_18.toml rename to pype/settings/defaults/system_configurations/launchers/houdini_18.toml diff --git a/pype/configurations/defaults/system_configurations/launchers/linux/maya2016 b/pype/settings/defaults/system_configurations/launchers/linux/maya2016 similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/linux/maya2016 rename to pype/settings/defaults/system_configurations/launchers/linux/maya2016 diff --git a/pype/configurations/defaults/system_configurations/launchers/linux/maya2017 b/pype/settings/defaults/system_configurations/launchers/linux/maya2017 similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/linux/maya2017 rename to pype/settings/defaults/system_configurations/launchers/linux/maya2017 diff --git a/pype/configurations/defaults/system_configurations/launchers/linux/maya2018 b/pype/settings/defaults/system_configurations/launchers/linux/maya2018 similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/linux/maya2018 rename to pype/settings/defaults/system_configurations/launchers/linux/maya2018 diff --git a/pype/configurations/defaults/system_configurations/launchers/linux/maya2019 b/pype/settings/defaults/system_configurations/launchers/linux/maya2019 similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/linux/maya2019 rename to pype/settings/defaults/system_configurations/launchers/linux/maya2019 diff --git a/pype/configurations/defaults/system_configurations/launchers/linux/maya2020 b/pype/settings/defaults/system_configurations/launchers/linux/maya2020 similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/linux/maya2020 rename to pype/settings/defaults/system_configurations/launchers/linux/maya2020 diff --git a/pype/configurations/defaults/system_configurations/launchers/linux/nuke11.3 b/pype/settings/defaults/system_configurations/launchers/linux/nuke11.3 similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/linux/nuke11.3 rename to pype/settings/defaults/system_configurations/launchers/linux/nuke11.3 diff --git a/pype/configurations/defaults/system_configurations/launchers/linux/nuke12.0 b/pype/settings/defaults/system_configurations/launchers/linux/nuke12.0 similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/linux/nuke12.0 rename to pype/settings/defaults/system_configurations/launchers/linux/nuke12.0 diff --git a/pype/configurations/defaults/system_configurations/launchers/linux/nukestudio11.3 b/pype/settings/defaults/system_configurations/launchers/linux/nukestudio11.3 similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/linux/nukestudio11.3 rename to pype/settings/defaults/system_configurations/launchers/linux/nukestudio11.3 diff --git a/pype/configurations/defaults/system_configurations/launchers/linux/nukestudio12.0 b/pype/settings/defaults/system_configurations/launchers/linux/nukestudio12.0 similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/linux/nukestudio12.0 rename to pype/settings/defaults/system_configurations/launchers/linux/nukestudio12.0 diff --git a/pype/configurations/defaults/system_configurations/launchers/linux/nukex11.3 b/pype/settings/defaults/system_configurations/launchers/linux/nukex11.3 similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/linux/nukex11.3 rename to pype/settings/defaults/system_configurations/launchers/linux/nukex11.3 diff --git a/pype/configurations/defaults/system_configurations/launchers/linux/nukex12.0 b/pype/settings/defaults/system_configurations/launchers/linux/nukex12.0 similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/linux/nukex12.0 rename to pype/settings/defaults/system_configurations/launchers/linux/nukex12.0 diff --git a/pype/configurations/defaults/system_configurations/launchers/maya_2016.toml b/pype/settings/defaults/system_configurations/launchers/maya_2016.toml similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/maya_2016.toml rename to pype/settings/defaults/system_configurations/launchers/maya_2016.toml diff --git a/pype/configurations/defaults/system_configurations/launchers/maya_2017.toml b/pype/settings/defaults/system_configurations/launchers/maya_2017.toml similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/maya_2017.toml rename to pype/settings/defaults/system_configurations/launchers/maya_2017.toml diff --git a/pype/configurations/defaults/system_configurations/launchers/maya_2018.toml b/pype/settings/defaults/system_configurations/launchers/maya_2018.toml similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/maya_2018.toml rename to pype/settings/defaults/system_configurations/launchers/maya_2018.toml diff --git a/pype/configurations/defaults/system_configurations/launchers/maya_2019.toml b/pype/settings/defaults/system_configurations/launchers/maya_2019.toml similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/maya_2019.toml rename to pype/settings/defaults/system_configurations/launchers/maya_2019.toml diff --git a/pype/configurations/defaults/system_configurations/launchers/maya_2020.toml b/pype/settings/defaults/system_configurations/launchers/maya_2020.toml similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/maya_2020.toml rename to pype/settings/defaults/system_configurations/launchers/maya_2020.toml diff --git a/pype/configurations/defaults/system_configurations/launchers/mayabatch_2019.toml b/pype/settings/defaults/system_configurations/launchers/mayabatch_2019.toml similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/mayabatch_2019.toml rename to pype/settings/defaults/system_configurations/launchers/mayabatch_2019.toml diff --git a/pype/configurations/defaults/system_configurations/launchers/mayabatch_2020.toml b/pype/settings/defaults/system_configurations/launchers/mayabatch_2020.toml similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/mayabatch_2020.toml rename to pype/settings/defaults/system_configurations/launchers/mayabatch_2020.toml diff --git a/pype/configurations/defaults/system_configurations/launchers/mayapy2016.toml b/pype/settings/defaults/system_configurations/launchers/mayapy2016.toml similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/mayapy2016.toml rename to pype/settings/defaults/system_configurations/launchers/mayapy2016.toml diff --git a/pype/configurations/defaults/system_configurations/launchers/mayapy2017.toml b/pype/settings/defaults/system_configurations/launchers/mayapy2017.toml similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/mayapy2017.toml rename to pype/settings/defaults/system_configurations/launchers/mayapy2017.toml diff --git a/pype/configurations/defaults/system_configurations/launchers/mayapy2018.toml b/pype/settings/defaults/system_configurations/launchers/mayapy2018.toml similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/mayapy2018.toml rename to pype/settings/defaults/system_configurations/launchers/mayapy2018.toml diff --git a/pype/configurations/defaults/system_configurations/launchers/mayapy2019.toml b/pype/settings/defaults/system_configurations/launchers/mayapy2019.toml similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/mayapy2019.toml rename to pype/settings/defaults/system_configurations/launchers/mayapy2019.toml diff --git a/pype/configurations/defaults/system_configurations/launchers/mayapy2020.toml b/pype/settings/defaults/system_configurations/launchers/mayapy2020.toml similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/mayapy2020.toml rename to pype/settings/defaults/system_configurations/launchers/mayapy2020.toml diff --git a/pype/configurations/defaults/system_configurations/launchers/myapp.toml b/pype/settings/defaults/system_configurations/launchers/myapp.toml similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/myapp.toml rename to pype/settings/defaults/system_configurations/launchers/myapp.toml diff --git a/pype/configurations/defaults/system_configurations/launchers/nuke_10.0.toml b/pype/settings/defaults/system_configurations/launchers/nuke_10.0.toml similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/nuke_10.0.toml rename to pype/settings/defaults/system_configurations/launchers/nuke_10.0.toml diff --git a/pype/configurations/defaults/system_configurations/launchers/nuke_11.0.toml b/pype/settings/defaults/system_configurations/launchers/nuke_11.0.toml similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/nuke_11.0.toml rename to pype/settings/defaults/system_configurations/launchers/nuke_11.0.toml diff --git a/pype/configurations/defaults/system_configurations/launchers/nuke_11.2.toml b/pype/settings/defaults/system_configurations/launchers/nuke_11.2.toml similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/nuke_11.2.toml rename to pype/settings/defaults/system_configurations/launchers/nuke_11.2.toml diff --git a/pype/configurations/defaults/system_configurations/launchers/nuke_11.3.toml b/pype/settings/defaults/system_configurations/launchers/nuke_11.3.toml similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/nuke_11.3.toml rename to pype/settings/defaults/system_configurations/launchers/nuke_11.3.toml diff --git a/pype/configurations/defaults/system_configurations/launchers/nuke_12.0.toml b/pype/settings/defaults/system_configurations/launchers/nuke_12.0.toml similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/nuke_12.0.toml rename to pype/settings/defaults/system_configurations/launchers/nuke_12.0.toml diff --git a/pype/configurations/defaults/system_configurations/launchers/nukestudio_10.0.toml b/pype/settings/defaults/system_configurations/launchers/nukestudio_10.0.toml similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/nukestudio_10.0.toml rename to pype/settings/defaults/system_configurations/launchers/nukestudio_10.0.toml diff --git a/pype/configurations/defaults/system_configurations/launchers/nukestudio_11.0.toml b/pype/settings/defaults/system_configurations/launchers/nukestudio_11.0.toml similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/nukestudio_11.0.toml rename to pype/settings/defaults/system_configurations/launchers/nukestudio_11.0.toml diff --git a/pype/configurations/defaults/system_configurations/launchers/nukestudio_11.2.toml b/pype/settings/defaults/system_configurations/launchers/nukestudio_11.2.toml similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/nukestudio_11.2.toml rename to pype/settings/defaults/system_configurations/launchers/nukestudio_11.2.toml diff --git a/pype/configurations/defaults/system_configurations/launchers/nukestudio_11.3.toml b/pype/settings/defaults/system_configurations/launchers/nukestudio_11.3.toml similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/nukestudio_11.3.toml rename to pype/settings/defaults/system_configurations/launchers/nukestudio_11.3.toml diff --git a/pype/configurations/defaults/system_configurations/launchers/nukestudio_12.0.toml b/pype/settings/defaults/system_configurations/launchers/nukestudio_12.0.toml similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/nukestudio_12.0.toml rename to pype/settings/defaults/system_configurations/launchers/nukestudio_12.0.toml diff --git a/pype/configurations/defaults/system_configurations/launchers/nukex_10.0.toml b/pype/settings/defaults/system_configurations/launchers/nukex_10.0.toml similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/nukex_10.0.toml rename to pype/settings/defaults/system_configurations/launchers/nukex_10.0.toml diff --git a/pype/configurations/defaults/system_configurations/launchers/nukex_11.0.toml b/pype/settings/defaults/system_configurations/launchers/nukex_11.0.toml similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/nukex_11.0.toml rename to pype/settings/defaults/system_configurations/launchers/nukex_11.0.toml diff --git a/pype/configurations/defaults/system_configurations/launchers/nukex_11.2.toml b/pype/settings/defaults/system_configurations/launchers/nukex_11.2.toml similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/nukex_11.2.toml rename to pype/settings/defaults/system_configurations/launchers/nukex_11.2.toml diff --git a/pype/configurations/defaults/system_configurations/launchers/nukex_11.3.toml b/pype/settings/defaults/system_configurations/launchers/nukex_11.3.toml similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/nukex_11.3.toml rename to pype/settings/defaults/system_configurations/launchers/nukex_11.3.toml diff --git a/pype/configurations/defaults/system_configurations/launchers/nukex_12.0.toml b/pype/settings/defaults/system_configurations/launchers/nukex_12.0.toml similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/nukex_12.0.toml rename to pype/settings/defaults/system_configurations/launchers/nukex_12.0.toml diff --git a/pype/configurations/defaults/system_configurations/launchers/photoshop_2020.toml b/pype/settings/defaults/system_configurations/launchers/photoshop_2020.toml similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/photoshop_2020.toml rename to pype/settings/defaults/system_configurations/launchers/photoshop_2020.toml diff --git a/pype/configurations/defaults/system_configurations/launchers/premiere_2019.toml b/pype/settings/defaults/system_configurations/launchers/premiere_2019.toml similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/premiere_2019.toml rename to pype/settings/defaults/system_configurations/launchers/premiere_2019.toml diff --git a/pype/configurations/defaults/system_configurations/launchers/premiere_2020.toml b/pype/settings/defaults/system_configurations/launchers/premiere_2020.toml similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/premiere_2020.toml rename to pype/settings/defaults/system_configurations/launchers/premiere_2020.toml diff --git a/pype/configurations/defaults/system_configurations/launchers/python_2.toml b/pype/settings/defaults/system_configurations/launchers/python_2.toml similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/python_2.toml rename to pype/settings/defaults/system_configurations/launchers/python_2.toml diff --git a/pype/configurations/defaults/system_configurations/launchers/python_3.toml b/pype/settings/defaults/system_configurations/launchers/python_3.toml similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/python_3.toml rename to pype/settings/defaults/system_configurations/launchers/python_3.toml diff --git a/pype/configurations/defaults/system_configurations/launchers/resolve_16.toml b/pype/settings/defaults/system_configurations/launchers/resolve_16.toml similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/resolve_16.toml rename to pype/settings/defaults/system_configurations/launchers/resolve_16.toml diff --git a/pype/configurations/defaults/system_configurations/launchers/shell.toml b/pype/settings/defaults/system_configurations/launchers/shell.toml similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/shell.toml rename to pype/settings/defaults/system_configurations/launchers/shell.toml diff --git a/pype/configurations/defaults/system_configurations/launchers/storyboardpro_7.toml b/pype/settings/defaults/system_configurations/launchers/storyboardpro_7.toml similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/storyboardpro_7.toml rename to pype/settings/defaults/system_configurations/launchers/storyboardpro_7.toml diff --git a/pype/configurations/defaults/system_configurations/launchers/unreal_4.24.toml b/pype/settings/defaults/system_configurations/launchers/unreal_4.24.toml similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/unreal_4.24.toml rename to pype/settings/defaults/system_configurations/launchers/unreal_4.24.toml diff --git a/pype/configurations/defaults/system_configurations/launchers/windows/blender_2.80.bat b/pype/settings/defaults/system_configurations/launchers/windows/blender_2.80.bat similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/windows/blender_2.80.bat rename to pype/settings/defaults/system_configurations/launchers/windows/blender_2.80.bat diff --git a/pype/configurations/defaults/system_configurations/launchers/windows/blender_2.81.bat b/pype/settings/defaults/system_configurations/launchers/windows/blender_2.81.bat similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/windows/blender_2.81.bat rename to pype/settings/defaults/system_configurations/launchers/windows/blender_2.81.bat diff --git a/pype/configurations/defaults/system_configurations/launchers/windows/blender_2.82.bat b/pype/settings/defaults/system_configurations/launchers/windows/blender_2.82.bat similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/windows/blender_2.82.bat rename to pype/settings/defaults/system_configurations/launchers/windows/blender_2.82.bat diff --git a/pype/configurations/defaults/system_configurations/launchers/windows/blender_2.83.bat b/pype/settings/defaults/system_configurations/launchers/windows/blender_2.83.bat similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/windows/blender_2.83.bat rename to pype/settings/defaults/system_configurations/launchers/windows/blender_2.83.bat diff --git a/pype/configurations/defaults/system_configurations/launchers/windows/celaction_local.bat b/pype/settings/defaults/system_configurations/launchers/windows/celaction_local.bat similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/windows/celaction_local.bat rename to pype/settings/defaults/system_configurations/launchers/windows/celaction_local.bat diff --git a/pype/configurations/defaults/system_configurations/launchers/windows/celaction_publish.bat b/pype/settings/defaults/system_configurations/launchers/windows/celaction_publish.bat similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/windows/celaction_publish.bat rename to pype/settings/defaults/system_configurations/launchers/windows/celaction_publish.bat diff --git a/pype/configurations/defaults/system_configurations/launchers/windows/harmony_17.bat b/pype/settings/defaults/system_configurations/launchers/windows/harmony_17.bat similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/windows/harmony_17.bat rename to pype/settings/defaults/system_configurations/launchers/windows/harmony_17.bat diff --git a/pype/configurations/defaults/system_configurations/launchers/windows/houdini_16.bat b/pype/settings/defaults/system_configurations/launchers/windows/houdini_16.bat similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/windows/houdini_16.bat rename to pype/settings/defaults/system_configurations/launchers/windows/houdini_16.bat diff --git a/pype/configurations/defaults/system_configurations/launchers/windows/houdini_17.bat b/pype/settings/defaults/system_configurations/launchers/windows/houdini_17.bat similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/windows/houdini_17.bat rename to pype/settings/defaults/system_configurations/launchers/windows/houdini_17.bat diff --git a/pype/configurations/defaults/system_configurations/launchers/windows/houdini_18.bat b/pype/settings/defaults/system_configurations/launchers/windows/houdini_18.bat similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/windows/houdini_18.bat rename to pype/settings/defaults/system_configurations/launchers/windows/houdini_18.bat diff --git a/pype/configurations/defaults/system_configurations/launchers/windows/maya2016.bat b/pype/settings/defaults/system_configurations/launchers/windows/maya2016.bat similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/windows/maya2016.bat rename to pype/settings/defaults/system_configurations/launchers/windows/maya2016.bat diff --git a/pype/configurations/defaults/system_configurations/launchers/windows/maya2017.bat b/pype/settings/defaults/system_configurations/launchers/windows/maya2017.bat similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/windows/maya2017.bat rename to pype/settings/defaults/system_configurations/launchers/windows/maya2017.bat diff --git a/pype/configurations/defaults/system_configurations/launchers/windows/maya2018.bat b/pype/settings/defaults/system_configurations/launchers/windows/maya2018.bat similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/windows/maya2018.bat rename to pype/settings/defaults/system_configurations/launchers/windows/maya2018.bat diff --git a/pype/configurations/defaults/system_configurations/launchers/windows/maya2019.bat b/pype/settings/defaults/system_configurations/launchers/windows/maya2019.bat similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/windows/maya2019.bat rename to pype/settings/defaults/system_configurations/launchers/windows/maya2019.bat diff --git a/pype/configurations/defaults/system_configurations/launchers/windows/maya2020.bat b/pype/settings/defaults/system_configurations/launchers/windows/maya2020.bat similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/windows/maya2020.bat rename to pype/settings/defaults/system_configurations/launchers/windows/maya2020.bat diff --git a/pype/configurations/defaults/system_configurations/launchers/windows/mayabatch2019.bat b/pype/settings/defaults/system_configurations/launchers/windows/mayabatch2019.bat similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/windows/mayabatch2019.bat rename to pype/settings/defaults/system_configurations/launchers/windows/mayabatch2019.bat diff --git a/pype/configurations/defaults/system_configurations/launchers/windows/mayabatch2020.bat b/pype/settings/defaults/system_configurations/launchers/windows/mayabatch2020.bat similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/windows/mayabatch2020.bat rename to pype/settings/defaults/system_configurations/launchers/windows/mayabatch2020.bat diff --git a/pype/configurations/defaults/system_configurations/launchers/windows/mayapy2016.bat b/pype/settings/defaults/system_configurations/launchers/windows/mayapy2016.bat similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/windows/mayapy2016.bat rename to pype/settings/defaults/system_configurations/launchers/windows/mayapy2016.bat diff --git a/pype/configurations/defaults/system_configurations/launchers/windows/mayapy2017.bat b/pype/settings/defaults/system_configurations/launchers/windows/mayapy2017.bat similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/windows/mayapy2017.bat rename to pype/settings/defaults/system_configurations/launchers/windows/mayapy2017.bat diff --git a/pype/configurations/defaults/system_configurations/launchers/windows/mayapy2018.bat b/pype/settings/defaults/system_configurations/launchers/windows/mayapy2018.bat similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/windows/mayapy2018.bat rename to pype/settings/defaults/system_configurations/launchers/windows/mayapy2018.bat diff --git a/pype/configurations/defaults/system_configurations/launchers/windows/mayapy2019.bat b/pype/settings/defaults/system_configurations/launchers/windows/mayapy2019.bat similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/windows/mayapy2019.bat rename to pype/settings/defaults/system_configurations/launchers/windows/mayapy2019.bat diff --git a/pype/configurations/defaults/system_configurations/launchers/windows/mayapy2020.bat b/pype/settings/defaults/system_configurations/launchers/windows/mayapy2020.bat similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/windows/mayapy2020.bat rename to pype/settings/defaults/system_configurations/launchers/windows/mayapy2020.bat diff --git a/pype/configurations/defaults/system_configurations/launchers/windows/nuke10.0.bat b/pype/settings/defaults/system_configurations/launchers/windows/nuke10.0.bat similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/windows/nuke10.0.bat rename to pype/settings/defaults/system_configurations/launchers/windows/nuke10.0.bat diff --git a/pype/configurations/defaults/system_configurations/launchers/windows/nuke11.0.bat b/pype/settings/defaults/system_configurations/launchers/windows/nuke11.0.bat similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/windows/nuke11.0.bat rename to pype/settings/defaults/system_configurations/launchers/windows/nuke11.0.bat diff --git a/pype/configurations/defaults/system_configurations/launchers/windows/nuke11.2.bat b/pype/settings/defaults/system_configurations/launchers/windows/nuke11.2.bat similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/windows/nuke11.2.bat rename to pype/settings/defaults/system_configurations/launchers/windows/nuke11.2.bat diff --git a/pype/configurations/defaults/system_configurations/launchers/windows/nuke11.3.bat b/pype/settings/defaults/system_configurations/launchers/windows/nuke11.3.bat similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/windows/nuke11.3.bat rename to pype/settings/defaults/system_configurations/launchers/windows/nuke11.3.bat diff --git a/pype/configurations/defaults/system_configurations/launchers/windows/nuke12.0.bat b/pype/settings/defaults/system_configurations/launchers/windows/nuke12.0.bat similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/windows/nuke12.0.bat rename to pype/settings/defaults/system_configurations/launchers/windows/nuke12.0.bat diff --git a/pype/configurations/defaults/system_configurations/launchers/windows/nukestudio10.0.bat b/pype/settings/defaults/system_configurations/launchers/windows/nukestudio10.0.bat similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/windows/nukestudio10.0.bat rename to pype/settings/defaults/system_configurations/launchers/windows/nukestudio10.0.bat diff --git a/pype/configurations/defaults/system_configurations/launchers/windows/nukestudio11.0.bat b/pype/settings/defaults/system_configurations/launchers/windows/nukestudio11.0.bat similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/windows/nukestudio11.0.bat rename to pype/settings/defaults/system_configurations/launchers/windows/nukestudio11.0.bat diff --git a/pype/configurations/defaults/system_configurations/launchers/windows/nukestudio11.2.bat b/pype/settings/defaults/system_configurations/launchers/windows/nukestudio11.2.bat similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/windows/nukestudio11.2.bat rename to pype/settings/defaults/system_configurations/launchers/windows/nukestudio11.2.bat diff --git a/pype/configurations/defaults/system_configurations/launchers/windows/nukestudio11.3.bat b/pype/settings/defaults/system_configurations/launchers/windows/nukestudio11.3.bat similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/windows/nukestudio11.3.bat rename to pype/settings/defaults/system_configurations/launchers/windows/nukestudio11.3.bat diff --git a/pype/configurations/defaults/system_configurations/launchers/windows/nukestudio12.0.bat b/pype/settings/defaults/system_configurations/launchers/windows/nukestudio12.0.bat similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/windows/nukestudio12.0.bat rename to pype/settings/defaults/system_configurations/launchers/windows/nukestudio12.0.bat diff --git a/pype/configurations/defaults/system_configurations/launchers/windows/nukex10.0.bat b/pype/settings/defaults/system_configurations/launchers/windows/nukex10.0.bat similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/windows/nukex10.0.bat rename to pype/settings/defaults/system_configurations/launchers/windows/nukex10.0.bat diff --git a/pype/configurations/defaults/system_configurations/launchers/windows/nukex11.0.bat b/pype/settings/defaults/system_configurations/launchers/windows/nukex11.0.bat similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/windows/nukex11.0.bat rename to pype/settings/defaults/system_configurations/launchers/windows/nukex11.0.bat diff --git a/pype/configurations/defaults/system_configurations/launchers/windows/nukex11.2.bat b/pype/settings/defaults/system_configurations/launchers/windows/nukex11.2.bat similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/windows/nukex11.2.bat rename to pype/settings/defaults/system_configurations/launchers/windows/nukex11.2.bat diff --git a/pype/configurations/defaults/system_configurations/launchers/windows/nukex11.3.bat b/pype/settings/defaults/system_configurations/launchers/windows/nukex11.3.bat similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/windows/nukex11.3.bat rename to pype/settings/defaults/system_configurations/launchers/windows/nukex11.3.bat diff --git a/pype/configurations/defaults/system_configurations/launchers/windows/nukex12.0.bat b/pype/settings/defaults/system_configurations/launchers/windows/nukex12.0.bat similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/windows/nukex12.0.bat rename to pype/settings/defaults/system_configurations/launchers/windows/nukex12.0.bat diff --git a/pype/configurations/defaults/system_configurations/launchers/windows/photoshop_2020.bat b/pype/settings/defaults/system_configurations/launchers/windows/photoshop_2020.bat similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/windows/photoshop_2020.bat rename to pype/settings/defaults/system_configurations/launchers/windows/photoshop_2020.bat diff --git a/pype/configurations/defaults/system_configurations/launchers/windows/premiere_pro_2019.bat b/pype/settings/defaults/system_configurations/launchers/windows/premiere_pro_2019.bat similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/windows/premiere_pro_2019.bat rename to pype/settings/defaults/system_configurations/launchers/windows/premiere_pro_2019.bat diff --git a/pype/configurations/defaults/system_configurations/launchers/windows/premiere_pro_2020.bat b/pype/settings/defaults/system_configurations/launchers/windows/premiere_pro_2020.bat similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/windows/premiere_pro_2020.bat rename to pype/settings/defaults/system_configurations/launchers/windows/premiere_pro_2020.bat diff --git a/pype/configurations/defaults/system_configurations/launchers/windows/python3.bat b/pype/settings/defaults/system_configurations/launchers/windows/python3.bat similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/windows/python3.bat rename to pype/settings/defaults/system_configurations/launchers/windows/python3.bat diff --git a/pype/configurations/defaults/system_configurations/launchers/windows/resolve_16.bat b/pype/settings/defaults/system_configurations/launchers/windows/resolve_16.bat similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/windows/resolve_16.bat rename to pype/settings/defaults/system_configurations/launchers/windows/resolve_16.bat diff --git a/pype/configurations/defaults/system_configurations/launchers/windows/shell.bat b/pype/settings/defaults/system_configurations/launchers/windows/shell.bat similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/windows/shell.bat rename to pype/settings/defaults/system_configurations/launchers/windows/shell.bat diff --git a/pype/configurations/defaults/system_configurations/launchers/windows/storyboardpro_7.bat b/pype/settings/defaults/system_configurations/launchers/windows/storyboardpro_7.bat similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/windows/storyboardpro_7.bat rename to pype/settings/defaults/system_configurations/launchers/windows/storyboardpro_7.bat diff --git a/pype/configurations/defaults/system_configurations/launchers/windows/unreal.bat b/pype/settings/defaults/system_configurations/launchers/windows/unreal.bat similarity index 100% rename from pype/configurations/defaults/system_configurations/launchers/windows/unreal.bat rename to pype/settings/defaults/system_configurations/launchers/windows/unreal.bat diff --git a/pype/configurations/defaults/system_configurations/muster/templates_mapping.json b/pype/settings/defaults/system_configurations/muster/templates_mapping.json similarity index 100% rename from pype/configurations/defaults/system_configurations/muster/templates_mapping.json rename to pype/settings/defaults/system_configurations/muster/templates_mapping.json diff --git a/pype/configurations/defaults/system_configurations/standalone_publish/families.json b/pype/settings/defaults/system_configurations/standalone_publish/families.json similarity index 100% rename from pype/configurations/defaults/system_configurations/standalone_publish/families.json rename to pype/settings/defaults/system_configurations/standalone_publish/families.json diff --git a/pype/configurations/lib.py b/pype/settings/lib.py similarity index 100% rename from pype/configurations/lib.py rename to pype/settings/lib.py From 148ec89e94909e7bf08884999bf0ec5f801c2d54 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 14 Sep 2020 18:23:45 +0200 Subject: [PATCH 591/813] fix few imports --- pype/tools/settings/settings/widgets/base.py | 4 ++-- pype/tools/settings/settings/widgets/lib.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pype/tools/settings/settings/widgets/base.py b/pype/tools/settings/settings/widgets/base.py index e1d167a9f2..6a7a65cb0f 100644 --- a/pype/tools/settings/settings/widgets/base.py +++ b/pype/tools/settings/settings/widgets/base.py @@ -1,8 +1,8 @@ import os import json from Qt import QtWidgets, QtCore, QtGui -from pype.configurations.lib import ( - SYSTEM_CONFIGURATIONS_KEY, +from pype.settings.lib import ( + SYSTEM_SETTINGS_KEY, SYSTEM_SETTINGS_PATH, PROJECT_SETTINGS_KEY, PROJECT_SETTINGS_PATH, diff --git a/pype/tools/settings/settings/widgets/lib.py b/pype/tools/settings/settings/widgets/lib.py index 9f54b090cb..0c3f01cef1 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.configurations.lib import OVERRIDEN_KEY +from pype.settings.lib import OVERRIDEN_KEY from queue import Queue From d0859f4b60077f8a3886225dd81648edf39cd0ee Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 14 Sep 2020 18:24:30 +0200 Subject: [PATCH 592/813] renamed folders in defaults --- .../ftrack/ftrack_config.json | 0 .../ftrack/ftrack_custom_attributes.json | 0 .../ftrack/partnership_ftrack_cred.json | 0 .../ftrack/plugins/server.json | 0 .../ftrack/plugins/user.json | 0 .../ftrack/project_defaults.json | 0 .../global/creator.json | 0 .../global/project_folder_structure.json | 0 .../global/sw_folders.json | 0 .../global/workfiles.json | 0 .../maya/capture.json | 0 .../muster/templates_mapping.json | 0 .../plugins/celaction/publish.json | 0 .../plugins/config.json | 0 .../plugins/ftrack/publish.json | 0 .../plugins/global/create.json | 0 .../plugins/global/filter.json | 0 .../plugins/global/load.json | 0 .../plugins/global/publish.json | 0 .../plugins/maya/create.json | 0 .../plugins/maya/filter.json | 0 .../plugins/maya/load.json | 0 .../plugins/maya/publish.json | 0 .../plugins/maya/workfile_build.json | 0 .../plugins/nuke/create.json | 0 .../plugins/nuke/load.json | 0 .../plugins/nuke/publish.json | 0 .../plugins/nuke/workfile_build.json | 0 .../plugins/nukestudio/filter.json | 0 .../plugins/nukestudio/publish.json | 0 .../plugins/resolve/create.json | 0 .../plugins/standalonepublisher/publish.json | 0 .../plugins/test/create.json | 0 .../plugins/test/publish.json | 0 .../premiere/asset_default.json | 0 .../premiere/rules_tasks.json | 0 .../standalonepublisher/families.json | 0 .../tools/slates/example_HD.json | 0 .../unreal/project_setup.json | 0 .../environments/avalon.json | 0 .../environments/blender.json | 0 .../environments/celaction.json | 0 .../environments/deadline.json | 0 .../environments/ftrack.json | 0 .../environments/global.json | 0 .../environments/harmony.json | 0 .../environments/houdini.json | 0 .../environments/maya.json | 0 .../environments/maya_2018.json | 0 .../environments/maya_2020.json | 0 .../environments/mayabatch.json | 0 .../environments/mayabatch_2019.json | 0 .../environments/mtoa_3.1.1.json | 0 .../environments/muster.json | 0 .../environments/nuke.json | 0 .../environments/nukestudio.json | 0 .../environments/nukestudio_10.0.json | 0 .../environments/nukex.json | 0 .../environments/nukex_10.0.json | 0 .../environments/photoshop.json | 0 .../environments/premiere.json | 0 .../environments/resolve.json | 0 .../environments/storyboardpro.json | 0 .../environments/unreal_4.24.json | 0 .../environments/vray_4300.json | 0 .../global/applications.json | 0 .../{system_configurations => system_settings}/global/intent.json | 0 .../{system_configurations => system_settings}/global/tools.json | 0 .../global/tray_modules.json | 0 .../launchers/blender_2.80.toml | 0 .../launchers/blender_2.81.toml | 0 .../launchers/blender_2.82.toml | 0 .../launchers/blender_2.83.toml | 0 .../launchers/celaction_local.toml | 0 .../launchers/celaction_publish.toml | 0 .../launchers/darwin/blender_2.82 | 0 .../launchers/darwin/harmony_17 | 0 .../launchers/darwin/harmony_17_launch | 0 .../launchers/darwin/python3 | 0 .../launchers/harmony_17.toml | 0 .../launchers/houdini_16.toml | 0 .../launchers/houdini_17.toml | 0 .../launchers/houdini_18.toml | 0 .../launchers/linux/maya2016 | 0 .../launchers/linux/maya2017 | 0 .../launchers/linux/maya2018 | 0 .../launchers/linux/maya2019 | 0 .../launchers/linux/maya2020 | 0 .../launchers/linux/nuke11.3 | 0 .../launchers/linux/nuke12.0 | 0 .../launchers/linux/nukestudio11.3 | 0 .../launchers/linux/nukestudio12.0 | 0 .../launchers/linux/nukex11.3 | 0 .../launchers/linux/nukex12.0 | 0 .../launchers/maya_2016.toml | 0 .../launchers/maya_2017.toml | 0 .../launchers/maya_2018.toml | 0 .../launchers/maya_2019.toml | 0 .../launchers/maya_2020.toml | 0 .../launchers/mayabatch_2019.toml | 0 .../launchers/mayabatch_2020.toml | 0 .../launchers/mayapy2016.toml | 0 .../launchers/mayapy2017.toml | 0 .../launchers/mayapy2018.toml | 0 .../launchers/mayapy2019.toml | 0 .../launchers/mayapy2020.toml | 0 .../launchers/myapp.toml | 0 .../launchers/nuke_10.0.toml | 0 .../launchers/nuke_11.0.toml | 0 .../launchers/nuke_11.2.toml | 0 .../launchers/nuke_11.3.toml | 0 .../launchers/nuke_12.0.toml | 0 .../launchers/nukestudio_10.0.toml | 0 .../launchers/nukestudio_11.0.toml | 0 .../launchers/nukestudio_11.2.toml | 0 .../launchers/nukestudio_11.3.toml | 0 .../launchers/nukestudio_12.0.toml | 0 .../launchers/nukex_10.0.toml | 0 .../launchers/nukex_11.0.toml | 0 .../launchers/nukex_11.2.toml | 0 .../launchers/nukex_11.3.toml | 0 .../launchers/nukex_12.0.toml | 0 .../launchers/photoshop_2020.toml | 0 .../launchers/premiere_2019.toml | 0 .../launchers/premiere_2020.toml | 0 .../launchers/python_2.toml | 0 .../launchers/python_3.toml | 0 .../launchers/resolve_16.toml | 0 .../launchers/shell.toml | 0 .../launchers/storyboardpro_7.toml | 0 .../launchers/unreal_4.24.toml | 0 .../launchers/windows/blender_2.80.bat | 0 .../launchers/windows/blender_2.81.bat | 0 .../launchers/windows/blender_2.82.bat | 0 .../launchers/windows/blender_2.83.bat | 0 .../launchers/windows/celaction_local.bat | 0 .../launchers/windows/celaction_publish.bat | 0 .../launchers/windows/harmony_17.bat | 0 .../launchers/windows/houdini_16.bat | 0 .../launchers/windows/houdini_17.bat | 0 .../launchers/windows/houdini_18.bat | 0 .../launchers/windows/maya2016.bat | 0 .../launchers/windows/maya2017.bat | 0 .../launchers/windows/maya2018.bat | 0 .../launchers/windows/maya2019.bat | 0 .../launchers/windows/maya2020.bat | 0 .../launchers/windows/mayabatch2019.bat | 0 .../launchers/windows/mayabatch2020.bat | 0 .../launchers/windows/mayapy2016.bat | 0 .../launchers/windows/mayapy2017.bat | 0 .../launchers/windows/mayapy2018.bat | 0 .../launchers/windows/mayapy2019.bat | 0 .../launchers/windows/mayapy2020.bat | 0 .../launchers/windows/nuke10.0.bat | 0 .../launchers/windows/nuke11.0.bat | 0 .../launchers/windows/nuke11.2.bat | 0 .../launchers/windows/nuke11.3.bat | 0 .../launchers/windows/nuke12.0.bat | 0 .../launchers/windows/nukestudio10.0.bat | 0 .../launchers/windows/nukestudio11.0.bat | 0 .../launchers/windows/nukestudio11.2.bat | 0 .../launchers/windows/nukestudio11.3.bat | 0 .../launchers/windows/nukestudio12.0.bat | 0 .../launchers/windows/nukex10.0.bat | 0 .../launchers/windows/nukex11.0.bat | 0 .../launchers/windows/nukex11.2.bat | 0 .../launchers/windows/nukex11.3.bat | 0 .../launchers/windows/nukex12.0.bat | 0 .../launchers/windows/photoshop_2020.bat | 0 .../launchers/windows/premiere_pro_2019.bat | 0 .../launchers/windows/premiere_pro_2020.bat | 0 .../launchers/windows/python3.bat | 0 .../launchers/windows/resolve_16.bat | 0 .../launchers/windows/shell.bat | 0 .../launchers/windows/storyboardpro_7.bat | 0 .../launchers/windows/unreal.bat | 0 .../muster/templates_mapping.json | 0 .../standalone_publish/families.json | 0 178 files changed, 0 insertions(+), 0 deletions(-) rename pype/settings/defaults/{project_configurations => project_settings}/ftrack/ftrack_config.json (100%) rename pype/settings/defaults/{project_configurations => project_settings}/ftrack/ftrack_custom_attributes.json (100%) rename pype/settings/defaults/{project_configurations => project_settings}/ftrack/partnership_ftrack_cred.json (100%) rename pype/settings/defaults/{project_configurations => project_settings}/ftrack/plugins/server.json (100%) rename pype/settings/defaults/{project_configurations => project_settings}/ftrack/plugins/user.json (100%) rename pype/settings/defaults/{project_configurations => project_settings}/ftrack/project_defaults.json (100%) rename pype/settings/defaults/{project_configurations => project_settings}/global/creator.json (100%) rename pype/settings/defaults/{project_configurations => project_settings}/global/project_folder_structure.json (100%) rename pype/settings/defaults/{project_configurations => project_settings}/global/sw_folders.json (100%) rename pype/settings/defaults/{project_configurations => project_settings}/global/workfiles.json (100%) rename pype/settings/defaults/{project_configurations => project_settings}/maya/capture.json (100%) rename pype/settings/defaults/{project_configurations => project_settings}/muster/templates_mapping.json (100%) rename pype/settings/defaults/{project_configurations => project_settings}/plugins/celaction/publish.json (100%) rename pype/settings/defaults/{project_configurations => project_settings}/plugins/config.json (100%) rename pype/settings/defaults/{project_configurations => project_settings}/plugins/ftrack/publish.json (100%) rename pype/settings/defaults/{project_configurations => project_settings}/plugins/global/create.json (100%) rename pype/settings/defaults/{project_configurations => project_settings}/plugins/global/filter.json (100%) rename pype/settings/defaults/{project_configurations => project_settings}/plugins/global/load.json (100%) rename pype/settings/defaults/{project_configurations => project_settings}/plugins/global/publish.json (100%) rename pype/settings/defaults/{project_configurations => project_settings}/plugins/maya/create.json (100%) rename pype/settings/defaults/{project_configurations => project_settings}/plugins/maya/filter.json (100%) rename pype/settings/defaults/{project_configurations => project_settings}/plugins/maya/load.json (100%) rename pype/settings/defaults/{project_configurations => project_settings}/plugins/maya/publish.json (100%) rename pype/settings/defaults/{project_configurations => project_settings}/plugins/maya/workfile_build.json (100%) rename pype/settings/defaults/{project_configurations => project_settings}/plugins/nuke/create.json (100%) rename pype/settings/defaults/{project_configurations => project_settings}/plugins/nuke/load.json (100%) rename pype/settings/defaults/{project_configurations => project_settings}/plugins/nuke/publish.json (100%) rename pype/settings/defaults/{project_configurations => project_settings}/plugins/nuke/workfile_build.json (100%) rename pype/settings/defaults/{project_configurations => project_settings}/plugins/nukestudio/filter.json (100%) rename pype/settings/defaults/{project_configurations => project_settings}/plugins/nukestudio/publish.json (100%) rename pype/settings/defaults/{project_configurations => project_settings}/plugins/resolve/create.json (100%) rename pype/settings/defaults/{project_configurations => project_settings}/plugins/standalonepublisher/publish.json (100%) rename pype/settings/defaults/{project_configurations => project_settings}/plugins/test/create.json (100%) rename pype/settings/defaults/{project_configurations => project_settings}/plugins/test/publish.json (100%) rename pype/settings/defaults/{project_configurations => project_settings}/premiere/asset_default.json (100%) rename pype/settings/defaults/{project_configurations => project_settings}/premiere/rules_tasks.json (100%) rename pype/settings/defaults/{project_configurations => project_settings}/standalonepublisher/families.json (100%) rename pype/settings/defaults/{project_configurations => project_settings}/tools/slates/example_HD.json (100%) rename pype/settings/defaults/{project_configurations => project_settings}/unreal/project_setup.json (100%) rename pype/settings/defaults/{system_configurations => system_settings}/environments/avalon.json (100%) rename pype/settings/defaults/{system_configurations => system_settings}/environments/blender.json (100%) rename pype/settings/defaults/{system_configurations => system_settings}/environments/celaction.json (100%) rename pype/settings/defaults/{system_configurations => system_settings}/environments/deadline.json (100%) rename pype/settings/defaults/{system_configurations => system_settings}/environments/ftrack.json (100%) rename pype/settings/defaults/{system_configurations => system_settings}/environments/global.json (100%) rename pype/settings/defaults/{system_configurations => system_settings}/environments/harmony.json (100%) rename pype/settings/defaults/{system_configurations => system_settings}/environments/houdini.json (100%) rename pype/settings/defaults/{system_configurations => system_settings}/environments/maya.json (100%) rename pype/settings/defaults/{system_configurations => system_settings}/environments/maya_2018.json (100%) rename pype/settings/defaults/{system_configurations => system_settings}/environments/maya_2020.json (100%) rename pype/settings/defaults/{system_configurations => system_settings}/environments/mayabatch.json (100%) rename pype/settings/defaults/{system_configurations => system_settings}/environments/mayabatch_2019.json (100%) rename pype/settings/defaults/{system_configurations => system_settings}/environments/mtoa_3.1.1.json (100%) rename pype/settings/defaults/{system_configurations => system_settings}/environments/muster.json (100%) rename pype/settings/defaults/{system_configurations => system_settings}/environments/nuke.json (100%) rename pype/settings/defaults/{system_configurations => system_settings}/environments/nukestudio.json (100%) rename pype/settings/defaults/{system_configurations => system_settings}/environments/nukestudio_10.0.json (100%) rename pype/settings/defaults/{system_configurations => system_settings}/environments/nukex.json (100%) rename pype/settings/defaults/{system_configurations => system_settings}/environments/nukex_10.0.json (100%) rename pype/settings/defaults/{system_configurations => system_settings}/environments/photoshop.json (100%) rename pype/settings/defaults/{system_configurations => system_settings}/environments/premiere.json (100%) rename pype/settings/defaults/{system_configurations => system_settings}/environments/resolve.json (100%) rename pype/settings/defaults/{system_configurations => system_settings}/environments/storyboardpro.json (100%) rename pype/settings/defaults/{system_configurations => system_settings}/environments/unreal_4.24.json (100%) rename pype/settings/defaults/{system_configurations => system_settings}/environments/vray_4300.json (100%) rename pype/settings/defaults/{system_configurations => system_settings}/global/applications.json (100%) rename pype/settings/defaults/{system_configurations => system_settings}/global/intent.json (100%) rename pype/settings/defaults/{system_configurations => system_settings}/global/tools.json (100%) rename pype/settings/defaults/{system_configurations => system_settings}/global/tray_modules.json (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/blender_2.80.toml (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/blender_2.81.toml (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/blender_2.82.toml (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/blender_2.83.toml (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/celaction_local.toml (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/celaction_publish.toml (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/darwin/blender_2.82 (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/darwin/harmony_17 (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/darwin/harmony_17_launch (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/darwin/python3 (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/harmony_17.toml (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/houdini_16.toml (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/houdini_17.toml (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/houdini_18.toml (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/linux/maya2016 (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/linux/maya2017 (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/linux/maya2018 (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/linux/maya2019 (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/linux/maya2020 (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/linux/nuke11.3 (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/linux/nuke12.0 (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/linux/nukestudio11.3 (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/linux/nukestudio12.0 (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/linux/nukex11.3 (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/linux/nukex12.0 (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/maya_2016.toml (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/maya_2017.toml (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/maya_2018.toml (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/maya_2019.toml (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/maya_2020.toml (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/mayabatch_2019.toml (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/mayabatch_2020.toml (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/mayapy2016.toml (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/mayapy2017.toml (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/mayapy2018.toml (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/mayapy2019.toml (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/mayapy2020.toml (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/myapp.toml (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/nuke_10.0.toml (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/nuke_11.0.toml (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/nuke_11.2.toml (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/nuke_11.3.toml (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/nuke_12.0.toml (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/nukestudio_10.0.toml (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/nukestudio_11.0.toml (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/nukestudio_11.2.toml (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/nukestudio_11.3.toml (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/nukestudio_12.0.toml (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/nukex_10.0.toml (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/nukex_11.0.toml (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/nukex_11.2.toml (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/nukex_11.3.toml (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/nukex_12.0.toml (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/photoshop_2020.toml (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/premiere_2019.toml (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/premiere_2020.toml (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/python_2.toml (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/python_3.toml (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/resolve_16.toml (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/shell.toml (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/storyboardpro_7.toml (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/unreal_4.24.toml (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/windows/blender_2.80.bat (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/windows/blender_2.81.bat (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/windows/blender_2.82.bat (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/windows/blender_2.83.bat (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/windows/celaction_local.bat (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/windows/celaction_publish.bat (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/windows/harmony_17.bat (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/windows/houdini_16.bat (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/windows/houdini_17.bat (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/windows/houdini_18.bat (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/windows/maya2016.bat (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/windows/maya2017.bat (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/windows/maya2018.bat (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/windows/maya2019.bat (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/windows/maya2020.bat (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/windows/mayabatch2019.bat (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/windows/mayabatch2020.bat (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/windows/mayapy2016.bat (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/windows/mayapy2017.bat (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/windows/mayapy2018.bat (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/windows/mayapy2019.bat (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/windows/mayapy2020.bat (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/windows/nuke10.0.bat (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/windows/nuke11.0.bat (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/windows/nuke11.2.bat (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/windows/nuke11.3.bat (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/windows/nuke12.0.bat (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/windows/nukestudio10.0.bat (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/windows/nukestudio11.0.bat (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/windows/nukestudio11.2.bat (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/windows/nukestudio11.3.bat (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/windows/nukestudio12.0.bat (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/windows/nukex10.0.bat (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/windows/nukex11.0.bat (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/windows/nukex11.2.bat (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/windows/nukex11.3.bat (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/windows/nukex12.0.bat (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/windows/photoshop_2020.bat (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/windows/premiere_pro_2019.bat (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/windows/premiere_pro_2020.bat (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/windows/python3.bat (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/windows/resolve_16.bat (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/windows/shell.bat (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/windows/storyboardpro_7.bat (100%) rename pype/settings/defaults/{system_configurations => system_settings}/launchers/windows/unreal.bat (100%) rename pype/settings/defaults/{system_configurations => system_settings}/muster/templates_mapping.json (100%) rename pype/settings/defaults/{system_configurations => system_settings}/standalone_publish/families.json (100%) diff --git a/pype/settings/defaults/project_configurations/ftrack/ftrack_config.json b/pype/settings/defaults/project_settings/ftrack/ftrack_config.json similarity index 100% rename from pype/settings/defaults/project_configurations/ftrack/ftrack_config.json rename to pype/settings/defaults/project_settings/ftrack/ftrack_config.json diff --git a/pype/settings/defaults/project_configurations/ftrack/ftrack_custom_attributes.json b/pype/settings/defaults/project_settings/ftrack/ftrack_custom_attributes.json similarity index 100% rename from pype/settings/defaults/project_configurations/ftrack/ftrack_custom_attributes.json rename to pype/settings/defaults/project_settings/ftrack/ftrack_custom_attributes.json diff --git a/pype/settings/defaults/project_configurations/ftrack/partnership_ftrack_cred.json b/pype/settings/defaults/project_settings/ftrack/partnership_ftrack_cred.json similarity index 100% rename from pype/settings/defaults/project_configurations/ftrack/partnership_ftrack_cred.json rename to pype/settings/defaults/project_settings/ftrack/partnership_ftrack_cred.json diff --git a/pype/settings/defaults/project_configurations/ftrack/plugins/server.json b/pype/settings/defaults/project_settings/ftrack/plugins/server.json similarity index 100% rename from pype/settings/defaults/project_configurations/ftrack/plugins/server.json rename to pype/settings/defaults/project_settings/ftrack/plugins/server.json diff --git a/pype/settings/defaults/project_configurations/ftrack/plugins/user.json b/pype/settings/defaults/project_settings/ftrack/plugins/user.json similarity index 100% rename from pype/settings/defaults/project_configurations/ftrack/plugins/user.json rename to pype/settings/defaults/project_settings/ftrack/plugins/user.json diff --git a/pype/settings/defaults/project_configurations/ftrack/project_defaults.json b/pype/settings/defaults/project_settings/ftrack/project_defaults.json similarity index 100% rename from pype/settings/defaults/project_configurations/ftrack/project_defaults.json rename to pype/settings/defaults/project_settings/ftrack/project_defaults.json diff --git a/pype/settings/defaults/project_configurations/global/creator.json b/pype/settings/defaults/project_settings/global/creator.json similarity index 100% rename from pype/settings/defaults/project_configurations/global/creator.json rename to pype/settings/defaults/project_settings/global/creator.json diff --git a/pype/settings/defaults/project_configurations/global/project_folder_structure.json b/pype/settings/defaults/project_settings/global/project_folder_structure.json similarity index 100% rename from pype/settings/defaults/project_configurations/global/project_folder_structure.json rename to pype/settings/defaults/project_settings/global/project_folder_structure.json diff --git a/pype/settings/defaults/project_configurations/global/sw_folders.json b/pype/settings/defaults/project_settings/global/sw_folders.json similarity index 100% rename from pype/settings/defaults/project_configurations/global/sw_folders.json rename to pype/settings/defaults/project_settings/global/sw_folders.json diff --git a/pype/settings/defaults/project_configurations/global/workfiles.json b/pype/settings/defaults/project_settings/global/workfiles.json similarity index 100% rename from pype/settings/defaults/project_configurations/global/workfiles.json rename to pype/settings/defaults/project_settings/global/workfiles.json diff --git a/pype/settings/defaults/project_configurations/maya/capture.json b/pype/settings/defaults/project_settings/maya/capture.json similarity index 100% rename from pype/settings/defaults/project_configurations/maya/capture.json rename to pype/settings/defaults/project_settings/maya/capture.json diff --git a/pype/settings/defaults/project_configurations/muster/templates_mapping.json b/pype/settings/defaults/project_settings/muster/templates_mapping.json similarity index 100% rename from pype/settings/defaults/project_configurations/muster/templates_mapping.json rename to pype/settings/defaults/project_settings/muster/templates_mapping.json diff --git a/pype/settings/defaults/project_configurations/plugins/celaction/publish.json b/pype/settings/defaults/project_settings/plugins/celaction/publish.json similarity index 100% rename from pype/settings/defaults/project_configurations/plugins/celaction/publish.json rename to pype/settings/defaults/project_settings/plugins/celaction/publish.json diff --git a/pype/settings/defaults/project_configurations/plugins/config.json b/pype/settings/defaults/project_settings/plugins/config.json similarity index 100% rename from pype/settings/defaults/project_configurations/plugins/config.json rename to pype/settings/defaults/project_settings/plugins/config.json diff --git a/pype/settings/defaults/project_configurations/plugins/ftrack/publish.json b/pype/settings/defaults/project_settings/plugins/ftrack/publish.json similarity index 100% rename from pype/settings/defaults/project_configurations/plugins/ftrack/publish.json rename to pype/settings/defaults/project_settings/plugins/ftrack/publish.json diff --git a/pype/settings/defaults/project_configurations/plugins/global/create.json b/pype/settings/defaults/project_settings/plugins/global/create.json similarity index 100% rename from pype/settings/defaults/project_configurations/plugins/global/create.json rename to pype/settings/defaults/project_settings/plugins/global/create.json diff --git a/pype/settings/defaults/project_configurations/plugins/global/filter.json b/pype/settings/defaults/project_settings/plugins/global/filter.json similarity index 100% rename from pype/settings/defaults/project_configurations/plugins/global/filter.json rename to pype/settings/defaults/project_settings/plugins/global/filter.json diff --git a/pype/settings/defaults/project_configurations/plugins/global/load.json b/pype/settings/defaults/project_settings/plugins/global/load.json similarity index 100% rename from pype/settings/defaults/project_configurations/plugins/global/load.json rename to pype/settings/defaults/project_settings/plugins/global/load.json diff --git a/pype/settings/defaults/project_configurations/plugins/global/publish.json b/pype/settings/defaults/project_settings/plugins/global/publish.json similarity index 100% rename from pype/settings/defaults/project_configurations/plugins/global/publish.json rename to pype/settings/defaults/project_settings/plugins/global/publish.json diff --git a/pype/settings/defaults/project_configurations/plugins/maya/create.json b/pype/settings/defaults/project_settings/plugins/maya/create.json similarity index 100% rename from pype/settings/defaults/project_configurations/plugins/maya/create.json rename to pype/settings/defaults/project_settings/plugins/maya/create.json diff --git a/pype/settings/defaults/project_configurations/plugins/maya/filter.json b/pype/settings/defaults/project_settings/plugins/maya/filter.json similarity index 100% rename from pype/settings/defaults/project_configurations/plugins/maya/filter.json rename to pype/settings/defaults/project_settings/plugins/maya/filter.json diff --git a/pype/settings/defaults/project_configurations/plugins/maya/load.json b/pype/settings/defaults/project_settings/plugins/maya/load.json similarity index 100% rename from pype/settings/defaults/project_configurations/plugins/maya/load.json rename to pype/settings/defaults/project_settings/plugins/maya/load.json diff --git a/pype/settings/defaults/project_configurations/plugins/maya/publish.json b/pype/settings/defaults/project_settings/plugins/maya/publish.json similarity index 100% rename from pype/settings/defaults/project_configurations/plugins/maya/publish.json rename to pype/settings/defaults/project_settings/plugins/maya/publish.json diff --git a/pype/settings/defaults/project_configurations/plugins/maya/workfile_build.json b/pype/settings/defaults/project_settings/plugins/maya/workfile_build.json similarity index 100% rename from pype/settings/defaults/project_configurations/plugins/maya/workfile_build.json rename to pype/settings/defaults/project_settings/plugins/maya/workfile_build.json diff --git a/pype/settings/defaults/project_configurations/plugins/nuke/create.json b/pype/settings/defaults/project_settings/plugins/nuke/create.json similarity index 100% rename from pype/settings/defaults/project_configurations/plugins/nuke/create.json rename to pype/settings/defaults/project_settings/plugins/nuke/create.json diff --git a/pype/settings/defaults/project_configurations/plugins/nuke/load.json b/pype/settings/defaults/project_settings/plugins/nuke/load.json similarity index 100% rename from pype/settings/defaults/project_configurations/plugins/nuke/load.json rename to pype/settings/defaults/project_settings/plugins/nuke/load.json diff --git a/pype/settings/defaults/project_configurations/plugins/nuke/publish.json b/pype/settings/defaults/project_settings/plugins/nuke/publish.json similarity index 100% rename from pype/settings/defaults/project_configurations/plugins/nuke/publish.json rename to pype/settings/defaults/project_settings/plugins/nuke/publish.json diff --git a/pype/settings/defaults/project_configurations/plugins/nuke/workfile_build.json b/pype/settings/defaults/project_settings/plugins/nuke/workfile_build.json similarity index 100% rename from pype/settings/defaults/project_configurations/plugins/nuke/workfile_build.json rename to pype/settings/defaults/project_settings/plugins/nuke/workfile_build.json diff --git a/pype/settings/defaults/project_configurations/plugins/nukestudio/filter.json b/pype/settings/defaults/project_settings/plugins/nukestudio/filter.json similarity index 100% rename from pype/settings/defaults/project_configurations/plugins/nukestudio/filter.json rename to pype/settings/defaults/project_settings/plugins/nukestudio/filter.json diff --git a/pype/settings/defaults/project_configurations/plugins/nukestudio/publish.json b/pype/settings/defaults/project_settings/plugins/nukestudio/publish.json similarity index 100% rename from pype/settings/defaults/project_configurations/plugins/nukestudio/publish.json rename to pype/settings/defaults/project_settings/plugins/nukestudio/publish.json diff --git a/pype/settings/defaults/project_configurations/plugins/resolve/create.json b/pype/settings/defaults/project_settings/plugins/resolve/create.json similarity index 100% rename from pype/settings/defaults/project_configurations/plugins/resolve/create.json rename to pype/settings/defaults/project_settings/plugins/resolve/create.json diff --git a/pype/settings/defaults/project_configurations/plugins/standalonepublisher/publish.json b/pype/settings/defaults/project_settings/plugins/standalonepublisher/publish.json similarity index 100% rename from pype/settings/defaults/project_configurations/plugins/standalonepublisher/publish.json rename to pype/settings/defaults/project_settings/plugins/standalonepublisher/publish.json diff --git a/pype/settings/defaults/project_configurations/plugins/test/create.json b/pype/settings/defaults/project_settings/plugins/test/create.json similarity index 100% rename from pype/settings/defaults/project_configurations/plugins/test/create.json rename to pype/settings/defaults/project_settings/plugins/test/create.json diff --git a/pype/settings/defaults/project_configurations/plugins/test/publish.json b/pype/settings/defaults/project_settings/plugins/test/publish.json similarity index 100% rename from pype/settings/defaults/project_configurations/plugins/test/publish.json rename to pype/settings/defaults/project_settings/plugins/test/publish.json diff --git a/pype/settings/defaults/project_configurations/premiere/asset_default.json b/pype/settings/defaults/project_settings/premiere/asset_default.json similarity index 100% rename from pype/settings/defaults/project_configurations/premiere/asset_default.json rename to pype/settings/defaults/project_settings/premiere/asset_default.json diff --git a/pype/settings/defaults/project_configurations/premiere/rules_tasks.json b/pype/settings/defaults/project_settings/premiere/rules_tasks.json similarity index 100% rename from pype/settings/defaults/project_configurations/premiere/rules_tasks.json rename to pype/settings/defaults/project_settings/premiere/rules_tasks.json diff --git a/pype/settings/defaults/project_configurations/standalonepublisher/families.json b/pype/settings/defaults/project_settings/standalonepublisher/families.json similarity index 100% rename from pype/settings/defaults/project_configurations/standalonepublisher/families.json rename to pype/settings/defaults/project_settings/standalonepublisher/families.json diff --git a/pype/settings/defaults/project_configurations/tools/slates/example_HD.json b/pype/settings/defaults/project_settings/tools/slates/example_HD.json similarity index 100% rename from pype/settings/defaults/project_configurations/tools/slates/example_HD.json rename to pype/settings/defaults/project_settings/tools/slates/example_HD.json diff --git a/pype/settings/defaults/project_configurations/unreal/project_setup.json b/pype/settings/defaults/project_settings/unreal/project_setup.json similarity index 100% rename from pype/settings/defaults/project_configurations/unreal/project_setup.json rename to pype/settings/defaults/project_settings/unreal/project_setup.json diff --git a/pype/settings/defaults/system_configurations/environments/avalon.json b/pype/settings/defaults/system_settings/environments/avalon.json similarity index 100% rename from pype/settings/defaults/system_configurations/environments/avalon.json rename to pype/settings/defaults/system_settings/environments/avalon.json diff --git a/pype/settings/defaults/system_configurations/environments/blender.json b/pype/settings/defaults/system_settings/environments/blender.json similarity index 100% rename from pype/settings/defaults/system_configurations/environments/blender.json rename to pype/settings/defaults/system_settings/environments/blender.json diff --git a/pype/settings/defaults/system_configurations/environments/celaction.json b/pype/settings/defaults/system_settings/environments/celaction.json similarity index 100% rename from pype/settings/defaults/system_configurations/environments/celaction.json rename to pype/settings/defaults/system_settings/environments/celaction.json diff --git a/pype/settings/defaults/system_configurations/environments/deadline.json b/pype/settings/defaults/system_settings/environments/deadline.json similarity index 100% rename from pype/settings/defaults/system_configurations/environments/deadline.json rename to pype/settings/defaults/system_settings/environments/deadline.json diff --git a/pype/settings/defaults/system_configurations/environments/ftrack.json b/pype/settings/defaults/system_settings/environments/ftrack.json similarity index 100% rename from pype/settings/defaults/system_configurations/environments/ftrack.json rename to pype/settings/defaults/system_settings/environments/ftrack.json diff --git a/pype/settings/defaults/system_configurations/environments/global.json b/pype/settings/defaults/system_settings/environments/global.json similarity index 100% rename from pype/settings/defaults/system_configurations/environments/global.json rename to pype/settings/defaults/system_settings/environments/global.json diff --git a/pype/settings/defaults/system_configurations/environments/harmony.json b/pype/settings/defaults/system_settings/environments/harmony.json similarity index 100% rename from pype/settings/defaults/system_configurations/environments/harmony.json rename to pype/settings/defaults/system_settings/environments/harmony.json diff --git a/pype/settings/defaults/system_configurations/environments/houdini.json b/pype/settings/defaults/system_settings/environments/houdini.json similarity index 100% rename from pype/settings/defaults/system_configurations/environments/houdini.json rename to pype/settings/defaults/system_settings/environments/houdini.json diff --git a/pype/settings/defaults/system_configurations/environments/maya.json b/pype/settings/defaults/system_settings/environments/maya.json similarity index 100% rename from pype/settings/defaults/system_configurations/environments/maya.json rename to pype/settings/defaults/system_settings/environments/maya.json diff --git a/pype/settings/defaults/system_configurations/environments/maya_2018.json b/pype/settings/defaults/system_settings/environments/maya_2018.json similarity index 100% rename from pype/settings/defaults/system_configurations/environments/maya_2018.json rename to pype/settings/defaults/system_settings/environments/maya_2018.json diff --git a/pype/settings/defaults/system_configurations/environments/maya_2020.json b/pype/settings/defaults/system_settings/environments/maya_2020.json similarity index 100% rename from pype/settings/defaults/system_configurations/environments/maya_2020.json rename to pype/settings/defaults/system_settings/environments/maya_2020.json diff --git a/pype/settings/defaults/system_configurations/environments/mayabatch.json b/pype/settings/defaults/system_settings/environments/mayabatch.json similarity index 100% rename from pype/settings/defaults/system_configurations/environments/mayabatch.json rename to pype/settings/defaults/system_settings/environments/mayabatch.json diff --git a/pype/settings/defaults/system_configurations/environments/mayabatch_2019.json b/pype/settings/defaults/system_settings/environments/mayabatch_2019.json similarity index 100% rename from pype/settings/defaults/system_configurations/environments/mayabatch_2019.json rename to pype/settings/defaults/system_settings/environments/mayabatch_2019.json diff --git a/pype/settings/defaults/system_configurations/environments/mtoa_3.1.1.json b/pype/settings/defaults/system_settings/environments/mtoa_3.1.1.json similarity index 100% rename from pype/settings/defaults/system_configurations/environments/mtoa_3.1.1.json rename to pype/settings/defaults/system_settings/environments/mtoa_3.1.1.json diff --git a/pype/settings/defaults/system_configurations/environments/muster.json b/pype/settings/defaults/system_settings/environments/muster.json similarity index 100% rename from pype/settings/defaults/system_configurations/environments/muster.json rename to pype/settings/defaults/system_settings/environments/muster.json diff --git a/pype/settings/defaults/system_configurations/environments/nuke.json b/pype/settings/defaults/system_settings/environments/nuke.json similarity index 100% rename from pype/settings/defaults/system_configurations/environments/nuke.json rename to pype/settings/defaults/system_settings/environments/nuke.json diff --git a/pype/settings/defaults/system_configurations/environments/nukestudio.json b/pype/settings/defaults/system_settings/environments/nukestudio.json similarity index 100% rename from pype/settings/defaults/system_configurations/environments/nukestudio.json rename to pype/settings/defaults/system_settings/environments/nukestudio.json diff --git a/pype/settings/defaults/system_configurations/environments/nukestudio_10.0.json b/pype/settings/defaults/system_settings/environments/nukestudio_10.0.json similarity index 100% rename from pype/settings/defaults/system_configurations/environments/nukestudio_10.0.json rename to pype/settings/defaults/system_settings/environments/nukestudio_10.0.json diff --git a/pype/settings/defaults/system_configurations/environments/nukex.json b/pype/settings/defaults/system_settings/environments/nukex.json similarity index 100% rename from pype/settings/defaults/system_configurations/environments/nukex.json rename to pype/settings/defaults/system_settings/environments/nukex.json diff --git a/pype/settings/defaults/system_configurations/environments/nukex_10.0.json b/pype/settings/defaults/system_settings/environments/nukex_10.0.json similarity index 100% rename from pype/settings/defaults/system_configurations/environments/nukex_10.0.json rename to pype/settings/defaults/system_settings/environments/nukex_10.0.json diff --git a/pype/settings/defaults/system_configurations/environments/photoshop.json b/pype/settings/defaults/system_settings/environments/photoshop.json similarity index 100% rename from pype/settings/defaults/system_configurations/environments/photoshop.json rename to pype/settings/defaults/system_settings/environments/photoshop.json diff --git a/pype/settings/defaults/system_configurations/environments/premiere.json b/pype/settings/defaults/system_settings/environments/premiere.json similarity index 100% rename from pype/settings/defaults/system_configurations/environments/premiere.json rename to pype/settings/defaults/system_settings/environments/premiere.json diff --git a/pype/settings/defaults/system_configurations/environments/resolve.json b/pype/settings/defaults/system_settings/environments/resolve.json similarity index 100% rename from pype/settings/defaults/system_configurations/environments/resolve.json rename to pype/settings/defaults/system_settings/environments/resolve.json diff --git a/pype/settings/defaults/system_configurations/environments/storyboardpro.json b/pype/settings/defaults/system_settings/environments/storyboardpro.json similarity index 100% rename from pype/settings/defaults/system_configurations/environments/storyboardpro.json rename to pype/settings/defaults/system_settings/environments/storyboardpro.json diff --git a/pype/settings/defaults/system_configurations/environments/unreal_4.24.json b/pype/settings/defaults/system_settings/environments/unreal_4.24.json similarity index 100% rename from pype/settings/defaults/system_configurations/environments/unreal_4.24.json rename to pype/settings/defaults/system_settings/environments/unreal_4.24.json diff --git a/pype/settings/defaults/system_configurations/environments/vray_4300.json b/pype/settings/defaults/system_settings/environments/vray_4300.json similarity index 100% rename from pype/settings/defaults/system_configurations/environments/vray_4300.json rename to pype/settings/defaults/system_settings/environments/vray_4300.json diff --git a/pype/settings/defaults/system_configurations/global/applications.json b/pype/settings/defaults/system_settings/global/applications.json similarity index 100% rename from pype/settings/defaults/system_configurations/global/applications.json rename to pype/settings/defaults/system_settings/global/applications.json diff --git a/pype/settings/defaults/system_configurations/global/intent.json b/pype/settings/defaults/system_settings/global/intent.json similarity index 100% rename from pype/settings/defaults/system_configurations/global/intent.json rename to pype/settings/defaults/system_settings/global/intent.json diff --git a/pype/settings/defaults/system_configurations/global/tools.json b/pype/settings/defaults/system_settings/global/tools.json similarity index 100% rename from pype/settings/defaults/system_configurations/global/tools.json rename to pype/settings/defaults/system_settings/global/tools.json diff --git a/pype/settings/defaults/system_configurations/global/tray_modules.json b/pype/settings/defaults/system_settings/global/tray_modules.json similarity index 100% rename from pype/settings/defaults/system_configurations/global/tray_modules.json rename to pype/settings/defaults/system_settings/global/tray_modules.json diff --git a/pype/settings/defaults/system_configurations/launchers/blender_2.80.toml b/pype/settings/defaults/system_settings/launchers/blender_2.80.toml similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/blender_2.80.toml rename to pype/settings/defaults/system_settings/launchers/blender_2.80.toml diff --git a/pype/settings/defaults/system_configurations/launchers/blender_2.81.toml b/pype/settings/defaults/system_settings/launchers/blender_2.81.toml similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/blender_2.81.toml rename to pype/settings/defaults/system_settings/launchers/blender_2.81.toml diff --git a/pype/settings/defaults/system_configurations/launchers/blender_2.82.toml b/pype/settings/defaults/system_settings/launchers/blender_2.82.toml similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/blender_2.82.toml rename to pype/settings/defaults/system_settings/launchers/blender_2.82.toml diff --git a/pype/settings/defaults/system_configurations/launchers/blender_2.83.toml b/pype/settings/defaults/system_settings/launchers/blender_2.83.toml similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/blender_2.83.toml rename to pype/settings/defaults/system_settings/launchers/blender_2.83.toml diff --git a/pype/settings/defaults/system_configurations/launchers/celaction_local.toml b/pype/settings/defaults/system_settings/launchers/celaction_local.toml similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/celaction_local.toml rename to pype/settings/defaults/system_settings/launchers/celaction_local.toml diff --git a/pype/settings/defaults/system_configurations/launchers/celaction_publish.toml b/pype/settings/defaults/system_settings/launchers/celaction_publish.toml similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/celaction_publish.toml rename to pype/settings/defaults/system_settings/launchers/celaction_publish.toml diff --git a/pype/settings/defaults/system_configurations/launchers/darwin/blender_2.82 b/pype/settings/defaults/system_settings/launchers/darwin/blender_2.82 similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/darwin/blender_2.82 rename to pype/settings/defaults/system_settings/launchers/darwin/blender_2.82 diff --git a/pype/settings/defaults/system_configurations/launchers/darwin/harmony_17 b/pype/settings/defaults/system_settings/launchers/darwin/harmony_17 similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/darwin/harmony_17 rename to pype/settings/defaults/system_settings/launchers/darwin/harmony_17 diff --git a/pype/settings/defaults/system_configurations/launchers/darwin/harmony_17_launch b/pype/settings/defaults/system_settings/launchers/darwin/harmony_17_launch similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/darwin/harmony_17_launch rename to pype/settings/defaults/system_settings/launchers/darwin/harmony_17_launch diff --git a/pype/settings/defaults/system_configurations/launchers/darwin/python3 b/pype/settings/defaults/system_settings/launchers/darwin/python3 similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/darwin/python3 rename to pype/settings/defaults/system_settings/launchers/darwin/python3 diff --git a/pype/settings/defaults/system_configurations/launchers/harmony_17.toml b/pype/settings/defaults/system_settings/launchers/harmony_17.toml similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/harmony_17.toml rename to pype/settings/defaults/system_settings/launchers/harmony_17.toml diff --git a/pype/settings/defaults/system_configurations/launchers/houdini_16.toml b/pype/settings/defaults/system_settings/launchers/houdini_16.toml similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/houdini_16.toml rename to pype/settings/defaults/system_settings/launchers/houdini_16.toml diff --git a/pype/settings/defaults/system_configurations/launchers/houdini_17.toml b/pype/settings/defaults/system_settings/launchers/houdini_17.toml similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/houdini_17.toml rename to pype/settings/defaults/system_settings/launchers/houdini_17.toml diff --git a/pype/settings/defaults/system_configurations/launchers/houdini_18.toml b/pype/settings/defaults/system_settings/launchers/houdini_18.toml similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/houdini_18.toml rename to pype/settings/defaults/system_settings/launchers/houdini_18.toml diff --git a/pype/settings/defaults/system_configurations/launchers/linux/maya2016 b/pype/settings/defaults/system_settings/launchers/linux/maya2016 similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/linux/maya2016 rename to pype/settings/defaults/system_settings/launchers/linux/maya2016 diff --git a/pype/settings/defaults/system_configurations/launchers/linux/maya2017 b/pype/settings/defaults/system_settings/launchers/linux/maya2017 similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/linux/maya2017 rename to pype/settings/defaults/system_settings/launchers/linux/maya2017 diff --git a/pype/settings/defaults/system_configurations/launchers/linux/maya2018 b/pype/settings/defaults/system_settings/launchers/linux/maya2018 similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/linux/maya2018 rename to pype/settings/defaults/system_settings/launchers/linux/maya2018 diff --git a/pype/settings/defaults/system_configurations/launchers/linux/maya2019 b/pype/settings/defaults/system_settings/launchers/linux/maya2019 similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/linux/maya2019 rename to pype/settings/defaults/system_settings/launchers/linux/maya2019 diff --git a/pype/settings/defaults/system_configurations/launchers/linux/maya2020 b/pype/settings/defaults/system_settings/launchers/linux/maya2020 similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/linux/maya2020 rename to pype/settings/defaults/system_settings/launchers/linux/maya2020 diff --git a/pype/settings/defaults/system_configurations/launchers/linux/nuke11.3 b/pype/settings/defaults/system_settings/launchers/linux/nuke11.3 similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/linux/nuke11.3 rename to pype/settings/defaults/system_settings/launchers/linux/nuke11.3 diff --git a/pype/settings/defaults/system_configurations/launchers/linux/nuke12.0 b/pype/settings/defaults/system_settings/launchers/linux/nuke12.0 similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/linux/nuke12.0 rename to pype/settings/defaults/system_settings/launchers/linux/nuke12.0 diff --git a/pype/settings/defaults/system_configurations/launchers/linux/nukestudio11.3 b/pype/settings/defaults/system_settings/launchers/linux/nukestudio11.3 similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/linux/nukestudio11.3 rename to pype/settings/defaults/system_settings/launchers/linux/nukestudio11.3 diff --git a/pype/settings/defaults/system_configurations/launchers/linux/nukestudio12.0 b/pype/settings/defaults/system_settings/launchers/linux/nukestudio12.0 similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/linux/nukestudio12.0 rename to pype/settings/defaults/system_settings/launchers/linux/nukestudio12.0 diff --git a/pype/settings/defaults/system_configurations/launchers/linux/nukex11.3 b/pype/settings/defaults/system_settings/launchers/linux/nukex11.3 similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/linux/nukex11.3 rename to pype/settings/defaults/system_settings/launchers/linux/nukex11.3 diff --git a/pype/settings/defaults/system_configurations/launchers/linux/nukex12.0 b/pype/settings/defaults/system_settings/launchers/linux/nukex12.0 similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/linux/nukex12.0 rename to pype/settings/defaults/system_settings/launchers/linux/nukex12.0 diff --git a/pype/settings/defaults/system_configurations/launchers/maya_2016.toml b/pype/settings/defaults/system_settings/launchers/maya_2016.toml similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/maya_2016.toml rename to pype/settings/defaults/system_settings/launchers/maya_2016.toml diff --git a/pype/settings/defaults/system_configurations/launchers/maya_2017.toml b/pype/settings/defaults/system_settings/launchers/maya_2017.toml similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/maya_2017.toml rename to pype/settings/defaults/system_settings/launchers/maya_2017.toml diff --git a/pype/settings/defaults/system_configurations/launchers/maya_2018.toml b/pype/settings/defaults/system_settings/launchers/maya_2018.toml similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/maya_2018.toml rename to pype/settings/defaults/system_settings/launchers/maya_2018.toml diff --git a/pype/settings/defaults/system_configurations/launchers/maya_2019.toml b/pype/settings/defaults/system_settings/launchers/maya_2019.toml similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/maya_2019.toml rename to pype/settings/defaults/system_settings/launchers/maya_2019.toml diff --git a/pype/settings/defaults/system_configurations/launchers/maya_2020.toml b/pype/settings/defaults/system_settings/launchers/maya_2020.toml similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/maya_2020.toml rename to pype/settings/defaults/system_settings/launchers/maya_2020.toml diff --git a/pype/settings/defaults/system_configurations/launchers/mayabatch_2019.toml b/pype/settings/defaults/system_settings/launchers/mayabatch_2019.toml similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/mayabatch_2019.toml rename to pype/settings/defaults/system_settings/launchers/mayabatch_2019.toml diff --git a/pype/settings/defaults/system_configurations/launchers/mayabatch_2020.toml b/pype/settings/defaults/system_settings/launchers/mayabatch_2020.toml similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/mayabatch_2020.toml rename to pype/settings/defaults/system_settings/launchers/mayabatch_2020.toml diff --git a/pype/settings/defaults/system_configurations/launchers/mayapy2016.toml b/pype/settings/defaults/system_settings/launchers/mayapy2016.toml similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/mayapy2016.toml rename to pype/settings/defaults/system_settings/launchers/mayapy2016.toml diff --git a/pype/settings/defaults/system_configurations/launchers/mayapy2017.toml b/pype/settings/defaults/system_settings/launchers/mayapy2017.toml similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/mayapy2017.toml rename to pype/settings/defaults/system_settings/launchers/mayapy2017.toml diff --git a/pype/settings/defaults/system_configurations/launchers/mayapy2018.toml b/pype/settings/defaults/system_settings/launchers/mayapy2018.toml similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/mayapy2018.toml rename to pype/settings/defaults/system_settings/launchers/mayapy2018.toml diff --git a/pype/settings/defaults/system_configurations/launchers/mayapy2019.toml b/pype/settings/defaults/system_settings/launchers/mayapy2019.toml similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/mayapy2019.toml rename to pype/settings/defaults/system_settings/launchers/mayapy2019.toml diff --git a/pype/settings/defaults/system_configurations/launchers/mayapy2020.toml b/pype/settings/defaults/system_settings/launchers/mayapy2020.toml similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/mayapy2020.toml rename to pype/settings/defaults/system_settings/launchers/mayapy2020.toml diff --git a/pype/settings/defaults/system_configurations/launchers/myapp.toml b/pype/settings/defaults/system_settings/launchers/myapp.toml similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/myapp.toml rename to pype/settings/defaults/system_settings/launchers/myapp.toml diff --git a/pype/settings/defaults/system_configurations/launchers/nuke_10.0.toml b/pype/settings/defaults/system_settings/launchers/nuke_10.0.toml similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/nuke_10.0.toml rename to pype/settings/defaults/system_settings/launchers/nuke_10.0.toml diff --git a/pype/settings/defaults/system_configurations/launchers/nuke_11.0.toml b/pype/settings/defaults/system_settings/launchers/nuke_11.0.toml similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/nuke_11.0.toml rename to pype/settings/defaults/system_settings/launchers/nuke_11.0.toml diff --git a/pype/settings/defaults/system_configurations/launchers/nuke_11.2.toml b/pype/settings/defaults/system_settings/launchers/nuke_11.2.toml similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/nuke_11.2.toml rename to pype/settings/defaults/system_settings/launchers/nuke_11.2.toml diff --git a/pype/settings/defaults/system_configurations/launchers/nuke_11.3.toml b/pype/settings/defaults/system_settings/launchers/nuke_11.3.toml similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/nuke_11.3.toml rename to pype/settings/defaults/system_settings/launchers/nuke_11.3.toml diff --git a/pype/settings/defaults/system_configurations/launchers/nuke_12.0.toml b/pype/settings/defaults/system_settings/launchers/nuke_12.0.toml similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/nuke_12.0.toml rename to pype/settings/defaults/system_settings/launchers/nuke_12.0.toml diff --git a/pype/settings/defaults/system_configurations/launchers/nukestudio_10.0.toml b/pype/settings/defaults/system_settings/launchers/nukestudio_10.0.toml similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/nukestudio_10.0.toml rename to pype/settings/defaults/system_settings/launchers/nukestudio_10.0.toml diff --git a/pype/settings/defaults/system_configurations/launchers/nukestudio_11.0.toml b/pype/settings/defaults/system_settings/launchers/nukestudio_11.0.toml similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/nukestudio_11.0.toml rename to pype/settings/defaults/system_settings/launchers/nukestudio_11.0.toml diff --git a/pype/settings/defaults/system_configurations/launchers/nukestudio_11.2.toml b/pype/settings/defaults/system_settings/launchers/nukestudio_11.2.toml similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/nukestudio_11.2.toml rename to pype/settings/defaults/system_settings/launchers/nukestudio_11.2.toml diff --git a/pype/settings/defaults/system_configurations/launchers/nukestudio_11.3.toml b/pype/settings/defaults/system_settings/launchers/nukestudio_11.3.toml similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/nukestudio_11.3.toml rename to pype/settings/defaults/system_settings/launchers/nukestudio_11.3.toml diff --git a/pype/settings/defaults/system_configurations/launchers/nukestudio_12.0.toml b/pype/settings/defaults/system_settings/launchers/nukestudio_12.0.toml similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/nukestudio_12.0.toml rename to pype/settings/defaults/system_settings/launchers/nukestudio_12.0.toml diff --git a/pype/settings/defaults/system_configurations/launchers/nukex_10.0.toml b/pype/settings/defaults/system_settings/launchers/nukex_10.0.toml similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/nukex_10.0.toml rename to pype/settings/defaults/system_settings/launchers/nukex_10.0.toml diff --git a/pype/settings/defaults/system_configurations/launchers/nukex_11.0.toml b/pype/settings/defaults/system_settings/launchers/nukex_11.0.toml similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/nukex_11.0.toml rename to pype/settings/defaults/system_settings/launchers/nukex_11.0.toml diff --git a/pype/settings/defaults/system_configurations/launchers/nukex_11.2.toml b/pype/settings/defaults/system_settings/launchers/nukex_11.2.toml similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/nukex_11.2.toml rename to pype/settings/defaults/system_settings/launchers/nukex_11.2.toml diff --git a/pype/settings/defaults/system_configurations/launchers/nukex_11.3.toml b/pype/settings/defaults/system_settings/launchers/nukex_11.3.toml similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/nukex_11.3.toml rename to pype/settings/defaults/system_settings/launchers/nukex_11.3.toml diff --git a/pype/settings/defaults/system_configurations/launchers/nukex_12.0.toml b/pype/settings/defaults/system_settings/launchers/nukex_12.0.toml similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/nukex_12.0.toml rename to pype/settings/defaults/system_settings/launchers/nukex_12.0.toml diff --git a/pype/settings/defaults/system_configurations/launchers/photoshop_2020.toml b/pype/settings/defaults/system_settings/launchers/photoshop_2020.toml similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/photoshop_2020.toml rename to pype/settings/defaults/system_settings/launchers/photoshop_2020.toml diff --git a/pype/settings/defaults/system_configurations/launchers/premiere_2019.toml b/pype/settings/defaults/system_settings/launchers/premiere_2019.toml similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/premiere_2019.toml rename to pype/settings/defaults/system_settings/launchers/premiere_2019.toml diff --git a/pype/settings/defaults/system_configurations/launchers/premiere_2020.toml b/pype/settings/defaults/system_settings/launchers/premiere_2020.toml similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/premiere_2020.toml rename to pype/settings/defaults/system_settings/launchers/premiere_2020.toml diff --git a/pype/settings/defaults/system_configurations/launchers/python_2.toml b/pype/settings/defaults/system_settings/launchers/python_2.toml similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/python_2.toml rename to pype/settings/defaults/system_settings/launchers/python_2.toml diff --git a/pype/settings/defaults/system_configurations/launchers/python_3.toml b/pype/settings/defaults/system_settings/launchers/python_3.toml similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/python_3.toml rename to pype/settings/defaults/system_settings/launchers/python_3.toml diff --git a/pype/settings/defaults/system_configurations/launchers/resolve_16.toml b/pype/settings/defaults/system_settings/launchers/resolve_16.toml similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/resolve_16.toml rename to pype/settings/defaults/system_settings/launchers/resolve_16.toml diff --git a/pype/settings/defaults/system_configurations/launchers/shell.toml b/pype/settings/defaults/system_settings/launchers/shell.toml similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/shell.toml rename to pype/settings/defaults/system_settings/launchers/shell.toml diff --git a/pype/settings/defaults/system_configurations/launchers/storyboardpro_7.toml b/pype/settings/defaults/system_settings/launchers/storyboardpro_7.toml similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/storyboardpro_7.toml rename to pype/settings/defaults/system_settings/launchers/storyboardpro_7.toml diff --git a/pype/settings/defaults/system_configurations/launchers/unreal_4.24.toml b/pype/settings/defaults/system_settings/launchers/unreal_4.24.toml similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/unreal_4.24.toml rename to pype/settings/defaults/system_settings/launchers/unreal_4.24.toml diff --git a/pype/settings/defaults/system_configurations/launchers/windows/blender_2.80.bat b/pype/settings/defaults/system_settings/launchers/windows/blender_2.80.bat similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/windows/blender_2.80.bat rename to pype/settings/defaults/system_settings/launchers/windows/blender_2.80.bat diff --git a/pype/settings/defaults/system_configurations/launchers/windows/blender_2.81.bat b/pype/settings/defaults/system_settings/launchers/windows/blender_2.81.bat similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/windows/blender_2.81.bat rename to pype/settings/defaults/system_settings/launchers/windows/blender_2.81.bat diff --git a/pype/settings/defaults/system_configurations/launchers/windows/blender_2.82.bat b/pype/settings/defaults/system_settings/launchers/windows/blender_2.82.bat similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/windows/blender_2.82.bat rename to pype/settings/defaults/system_settings/launchers/windows/blender_2.82.bat diff --git a/pype/settings/defaults/system_configurations/launchers/windows/blender_2.83.bat b/pype/settings/defaults/system_settings/launchers/windows/blender_2.83.bat similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/windows/blender_2.83.bat rename to pype/settings/defaults/system_settings/launchers/windows/blender_2.83.bat diff --git a/pype/settings/defaults/system_configurations/launchers/windows/celaction_local.bat b/pype/settings/defaults/system_settings/launchers/windows/celaction_local.bat similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/windows/celaction_local.bat rename to pype/settings/defaults/system_settings/launchers/windows/celaction_local.bat diff --git a/pype/settings/defaults/system_configurations/launchers/windows/celaction_publish.bat b/pype/settings/defaults/system_settings/launchers/windows/celaction_publish.bat similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/windows/celaction_publish.bat rename to pype/settings/defaults/system_settings/launchers/windows/celaction_publish.bat diff --git a/pype/settings/defaults/system_configurations/launchers/windows/harmony_17.bat b/pype/settings/defaults/system_settings/launchers/windows/harmony_17.bat similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/windows/harmony_17.bat rename to pype/settings/defaults/system_settings/launchers/windows/harmony_17.bat diff --git a/pype/settings/defaults/system_configurations/launchers/windows/houdini_16.bat b/pype/settings/defaults/system_settings/launchers/windows/houdini_16.bat similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/windows/houdini_16.bat rename to pype/settings/defaults/system_settings/launchers/windows/houdini_16.bat diff --git a/pype/settings/defaults/system_configurations/launchers/windows/houdini_17.bat b/pype/settings/defaults/system_settings/launchers/windows/houdini_17.bat similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/windows/houdini_17.bat rename to pype/settings/defaults/system_settings/launchers/windows/houdini_17.bat diff --git a/pype/settings/defaults/system_configurations/launchers/windows/houdini_18.bat b/pype/settings/defaults/system_settings/launchers/windows/houdini_18.bat similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/windows/houdini_18.bat rename to pype/settings/defaults/system_settings/launchers/windows/houdini_18.bat diff --git a/pype/settings/defaults/system_configurations/launchers/windows/maya2016.bat b/pype/settings/defaults/system_settings/launchers/windows/maya2016.bat similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/windows/maya2016.bat rename to pype/settings/defaults/system_settings/launchers/windows/maya2016.bat diff --git a/pype/settings/defaults/system_configurations/launchers/windows/maya2017.bat b/pype/settings/defaults/system_settings/launchers/windows/maya2017.bat similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/windows/maya2017.bat rename to pype/settings/defaults/system_settings/launchers/windows/maya2017.bat diff --git a/pype/settings/defaults/system_configurations/launchers/windows/maya2018.bat b/pype/settings/defaults/system_settings/launchers/windows/maya2018.bat similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/windows/maya2018.bat rename to pype/settings/defaults/system_settings/launchers/windows/maya2018.bat diff --git a/pype/settings/defaults/system_configurations/launchers/windows/maya2019.bat b/pype/settings/defaults/system_settings/launchers/windows/maya2019.bat similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/windows/maya2019.bat rename to pype/settings/defaults/system_settings/launchers/windows/maya2019.bat diff --git a/pype/settings/defaults/system_configurations/launchers/windows/maya2020.bat b/pype/settings/defaults/system_settings/launchers/windows/maya2020.bat similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/windows/maya2020.bat rename to pype/settings/defaults/system_settings/launchers/windows/maya2020.bat diff --git a/pype/settings/defaults/system_configurations/launchers/windows/mayabatch2019.bat b/pype/settings/defaults/system_settings/launchers/windows/mayabatch2019.bat similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/windows/mayabatch2019.bat rename to pype/settings/defaults/system_settings/launchers/windows/mayabatch2019.bat diff --git a/pype/settings/defaults/system_configurations/launchers/windows/mayabatch2020.bat b/pype/settings/defaults/system_settings/launchers/windows/mayabatch2020.bat similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/windows/mayabatch2020.bat rename to pype/settings/defaults/system_settings/launchers/windows/mayabatch2020.bat diff --git a/pype/settings/defaults/system_configurations/launchers/windows/mayapy2016.bat b/pype/settings/defaults/system_settings/launchers/windows/mayapy2016.bat similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/windows/mayapy2016.bat rename to pype/settings/defaults/system_settings/launchers/windows/mayapy2016.bat diff --git a/pype/settings/defaults/system_configurations/launchers/windows/mayapy2017.bat b/pype/settings/defaults/system_settings/launchers/windows/mayapy2017.bat similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/windows/mayapy2017.bat rename to pype/settings/defaults/system_settings/launchers/windows/mayapy2017.bat diff --git a/pype/settings/defaults/system_configurations/launchers/windows/mayapy2018.bat b/pype/settings/defaults/system_settings/launchers/windows/mayapy2018.bat similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/windows/mayapy2018.bat rename to pype/settings/defaults/system_settings/launchers/windows/mayapy2018.bat diff --git a/pype/settings/defaults/system_configurations/launchers/windows/mayapy2019.bat b/pype/settings/defaults/system_settings/launchers/windows/mayapy2019.bat similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/windows/mayapy2019.bat rename to pype/settings/defaults/system_settings/launchers/windows/mayapy2019.bat diff --git a/pype/settings/defaults/system_configurations/launchers/windows/mayapy2020.bat b/pype/settings/defaults/system_settings/launchers/windows/mayapy2020.bat similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/windows/mayapy2020.bat rename to pype/settings/defaults/system_settings/launchers/windows/mayapy2020.bat diff --git a/pype/settings/defaults/system_configurations/launchers/windows/nuke10.0.bat b/pype/settings/defaults/system_settings/launchers/windows/nuke10.0.bat similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/windows/nuke10.0.bat rename to pype/settings/defaults/system_settings/launchers/windows/nuke10.0.bat diff --git a/pype/settings/defaults/system_configurations/launchers/windows/nuke11.0.bat b/pype/settings/defaults/system_settings/launchers/windows/nuke11.0.bat similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/windows/nuke11.0.bat rename to pype/settings/defaults/system_settings/launchers/windows/nuke11.0.bat diff --git a/pype/settings/defaults/system_configurations/launchers/windows/nuke11.2.bat b/pype/settings/defaults/system_settings/launchers/windows/nuke11.2.bat similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/windows/nuke11.2.bat rename to pype/settings/defaults/system_settings/launchers/windows/nuke11.2.bat diff --git a/pype/settings/defaults/system_configurations/launchers/windows/nuke11.3.bat b/pype/settings/defaults/system_settings/launchers/windows/nuke11.3.bat similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/windows/nuke11.3.bat rename to pype/settings/defaults/system_settings/launchers/windows/nuke11.3.bat diff --git a/pype/settings/defaults/system_configurations/launchers/windows/nuke12.0.bat b/pype/settings/defaults/system_settings/launchers/windows/nuke12.0.bat similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/windows/nuke12.0.bat rename to pype/settings/defaults/system_settings/launchers/windows/nuke12.0.bat diff --git a/pype/settings/defaults/system_configurations/launchers/windows/nukestudio10.0.bat b/pype/settings/defaults/system_settings/launchers/windows/nukestudio10.0.bat similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/windows/nukestudio10.0.bat rename to pype/settings/defaults/system_settings/launchers/windows/nukestudio10.0.bat diff --git a/pype/settings/defaults/system_configurations/launchers/windows/nukestudio11.0.bat b/pype/settings/defaults/system_settings/launchers/windows/nukestudio11.0.bat similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/windows/nukestudio11.0.bat rename to pype/settings/defaults/system_settings/launchers/windows/nukestudio11.0.bat diff --git a/pype/settings/defaults/system_configurations/launchers/windows/nukestudio11.2.bat b/pype/settings/defaults/system_settings/launchers/windows/nukestudio11.2.bat similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/windows/nukestudio11.2.bat rename to pype/settings/defaults/system_settings/launchers/windows/nukestudio11.2.bat diff --git a/pype/settings/defaults/system_configurations/launchers/windows/nukestudio11.3.bat b/pype/settings/defaults/system_settings/launchers/windows/nukestudio11.3.bat similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/windows/nukestudio11.3.bat rename to pype/settings/defaults/system_settings/launchers/windows/nukestudio11.3.bat diff --git a/pype/settings/defaults/system_configurations/launchers/windows/nukestudio12.0.bat b/pype/settings/defaults/system_settings/launchers/windows/nukestudio12.0.bat similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/windows/nukestudio12.0.bat rename to pype/settings/defaults/system_settings/launchers/windows/nukestudio12.0.bat diff --git a/pype/settings/defaults/system_configurations/launchers/windows/nukex10.0.bat b/pype/settings/defaults/system_settings/launchers/windows/nukex10.0.bat similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/windows/nukex10.0.bat rename to pype/settings/defaults/system_settings/launchers/windows/nukex10.0.bat diff --git a/pype/settings/defaults/system_configurations/launchers/windows/nukex11.0.bat b/pype/settings/defaults/system_settings/launchers/windows/nukex11.0.bat similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/windows/nukex11.0.bat rename to pype/settings/defaults/system_settings/launchers/windows/nukex11.0.bat diff --git a/pype/settings/defaults/system_configurations/launchers/windows/nukex11.2.bat b/pype/settings/defaults/system_settings/launchers/windows/nukex11.2.bat similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/windows/nukex11.2.bat rename to pype/settings/defaults/system_settings/launchers/windows/nukex11.2.bat diff --git a/pype/settings/defaults/system_configurations/launchers/windows/nukex11.3.bat b/pype/settings/defaults/system_settings/launchers/windows/nukex11.3.bat similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/windows/nukex11.3.bat rename to pype/settings/defaults/system_settings/launchers/windows/nukex11.3.bat diff --git a/pype/settings/defaults/system_configurations/launchers/windows/nukex12.0.bat b/pype/settings/defaults/system_settings/launchers/windows/nukex12.0.bat similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/windows/nukex12.0.bat rename to pype/settings/defaults/system_settings/launchers/windows/nukex12.0.bat diff --git a/pype/settings/defaults/system_configurations/launchers/windows/photoshop_2020.bat b/pype/settings/defaults/system_settings/launchers/windows/photoshop_2020.bat similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/windows/photoshop_2020.bat rename to pype/settings/defaults/system_settings/launchers/windows/photoshop_2020.bat diff --git a/pype/settings/defaults/system_configurations/launchers/windows/premiere_pro_2019.bat b/pype/settings/defaults/system_settings/launchers/windows/premiere_pro_2019.bat similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/windows/premiere_pro_2019.bat rename to pype/settings/defaults/system_settings/launchers/windows/premiere_pro_2019.bat diff --git a/pype/settings/defaults/system_configurations/launchers/windows/premiere_pro_2020.bat b/pype/settings/defaults/system_settings/launchers/windows/premiere_pro_2020.bat similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/windows/premiere_pro_2020.bat rename to pype/settings/defaults/system_settings/launchers/windows/premiere_pro_2020.bat diff --git a/pype/settings/defaults/system_configurations/launchers/windows/python3.bat b/pype/settings/defaults/system_settings/launchers/windows/python3.bat similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/windows/python3.bat rename to pype/settings/defaults/system_settings/launchers/windows/python3.bat diff --git a/pype/settings/defaults/system_configurations/launchers/windows/resolve_16.bat b/pype/settings/defaults/system_settings/launchers/windows/resolve_16.bat similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/windows/resolve_16.bat rename to pype/settings/defaults/system_settings/launchers/windows/resolve_16.bat diff --git a/pype/settings/defaults/system_configurations/launchers/windows/shell.bat b/pype/settings/defaults/system_settings/launchers/windows/shell.bat similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/windows/shell.bat rename to pype/settings/defaults/system_settings/launchers/windows/shell.bat diff --git a/pype/settings/defaults/system_configurations/launchers/windows/storyboardpro_7.bat b/pype/settings/defaults/system_settings/launchers/windows/storyboardpro_7.bat similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/windows/storyboardpro_7.bat rename to pype/settings/defaults/system_settings/launchers/windows/storyboardpro_7.bat diff --git a/pype/settings/defaults/system_configurations/launchers/windows/unreal.bat b/pype/settings/defaults/system_settings/launchers/windows/unreal.bat similarity index 100% rename from pype/settings/defaults/system_configurations/launchers/windows/unreal.bat rename to pype/settings/defaults/system_settings/launchers/windows/unreal.bat diff --git a/pype/settings/defaults/system_configurations/muster/templates_mapping.json b/pype/settings/defaults/system_settings/muster/templates_mapping.json similarity index 100% rename from pype/settings/defaults/system_configurations/muster/templates_mapping.json rename to pype/settings/defaults/system_settings/muster/templates_mapping.json diff --git a/pype/settings/defaults/system_configurations/standalone_publish/families.json b/pype/settings/defaults/system_settings/standalone_publish/families.json similarity index 100% rename from pype/settings/defaults/system_configurations/standalone_publish/families.json rename to pype/settings/defaults/system_settings/standalone_publish/families.json From 5b50b8ede47d0507f56cf4f26701dca0ada4a110 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 14 Sep 2020 18:31:47 +0200 Subject: [PATCH 593/813] all variables for setting not contain setting instead of configuration --- pype/api.py | 8 ++-- pype/settings/__init__.py | 8 ++-- pype/settings/lib.py | 42 +++++++++---------- .../projects_schema/0_project_gui_schema.json | 2 +- pype/tools/settings/settings/widgets/base.py | 26 ++++++------ 5 files changed, 43 insertions(+), 43 deletions(-) diff --git a/pype/api.py b/pype/api.py index 0b3439c4c2..021080b4d5 100644 --- a/pype/api.py +++ b/pype/api.py @@ -1,6 +1,6 @@ from .settings import ( - system_configurations, - project_configurations + system_settings, + project_settings ) from pypeapp import ( Logger, @@ -53,8 +53,8 @@ from .lib import ( from .lib import _subprocess as subprocess __all__ = [ - "system_configurations", - "project_configurations", + "system_settings", + "project_settings", "Logger", "Anatomy", diff --git a/pype/settings/__init__.py b/pype/settings/__init__.py index e3fc53fcfd..7e73d541a4 100644 --- a/pype/settings/__init__.py +++ b/pype/settings/__init__.py @@ -1,9 +1,9 @@ from .lib import ( - system_configurations, - project_configurations + system_settings, + project_settings ) __all__ = ( - "system_configurations", - "project_configurations" + "system_settings", + "project_settings" ) diff --git a/pype/settings/lib.py b/pype/settings/lib.py index 101b579b14..388557ca9b 100644 --- a/pype/settings/lib.py +++ b/pype/settings/lib.py @@ -14,13 +14,13 @@ POP_KEY = "__pop_key__" STUDIO_OVERRIDES_PATH = os.environ["PYPE_PROJECT_CONFIGS"] # File where studio's system overrides are stored -SYSTEM_SETTINGS_KEY = "system_configurations" +SYSTEM_SETTINGS_KEY = "system_settings" SYSTEM_SETTINGS_PATH = os.path.join( STUDIO_OVERRIDES_PATH, SYSTEM_SETTINGS_KEY + ".json" ) # File where studio's default project overrides are stored -PROJECT_SETTINGS_KEY = "project_configurations" +PROJECT_SETTINGS_KEY = "project_settings" PROJECT_SETTINGS_FILENAME = PROJECT_SETTINGS_KEY + ".json" PROJECT_SETTINGS_PATH = os.path.join( STUDIO_OVERRIDES_PATH, PROJECT_SETTINGS_FILENAME @@ -32,19 +32,19 @@ PROJECT_ANATOMY_PATH = os.path.join( STUDIO_OVERRIDES_PATH, PROJECT_ANATOMY_FILENAME ) -# Path to default configurations +# Path to default settings DEFAULTS_DIR = os.path.join(os.path.dirname(__file__), "defaults") -# Variable where cache of default configurations are stored +# Variable where cache of default settings are stored _DEFAULT_SETTINGS = None -def reset_default_configurations(): +def reset_default_settings(): global _DEFAULT_SETTINGS _DEFAULT_SETTINGS = None -def default_configuration(): +def default_settings(): global _DEFAULT_SETTINGS if _DEFAULT_SETTINGS is None: _DEFAULT_SETTINGS = load_jsons_from_dir(DEFAULTS_DIR) @@ -156,13 +156,13 @@ def load_jsons_from_dir(path, *args, **kwargs): return output -def studio_system_configurations(): +def studio_system_settings(): if os.path.exists(SYSTEM_SETTINGS_PATH): return load_json(SYSTEM_SETTINGS_PATH) return {} -def studio_project_configurations(): +def studio_project_settings(): if os.path.exists(PROJECT_SETTINGS_PATH): return load_json(PROJECT_SETTINGS_PATH) return {} @@ -190,7 +190,7 @@ def path_to_project_anatomy(project_name): ) -def project_configurations_overrides(project_name): +def project_settings_overrides(project_name): if not project_name: return {} @@ -234,25 +234,25 @@ def merge_overrides(global_dict, override_dict): return global_dict -def apply_overrides(global_presets, project_overrides): - global_presets = copy.deepcopy(global_presets) - if not project_overrides: - return global_presets - return merge_overrides(global_presets, project_overrides) +def apply_overrides(source_data, override_data): + if not override_data: + return source_data + _source_data = copy.deepcopy(source_data) + return merge_overrides(_source_data, override_data) -def system_configurations(): - default_values = default_configuration()["system_configurations"] - studio_values = studio_system_configurations() +def system_settings(): + default_values = default_settings()[SYSTEM_SETTINGS_KEY] + studio_values = studio_system_settings() return apply_overrides(default_values, studio_values) -def project_configurations(project_name): - default_values = default_configuration()["project_configurations"] - studio_values = studio_project_configurations() +def project_settings(project_name): + default_values = default_settings()[PROJECT_SETTINGS_KEY] + studio_values = studio_project_settings() studio_overrides = apply_overrides(default_values, studio_values) - project_overrides = project_configurations_overrides(project_name) + project_overrides = project_settings_overrides(project_name) return apply_overrides(studio_overrides, project_overrides) diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/0_project_gui_schema.json b/pype/tools/settings/settings/gui_schemas/projects_schema/0_project_gui_schema.json index 3d7cac68fd..fa7c6a366d 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/0_project_gui_schema.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/0_project_gui_schema.json @@ -18,7 +18,7 @@ ] }, { "type": "dict-invisible", - "key": "project_configurations", + "key": "project_settings", "children": [ { "type": "schema", diff --git a/pype/tools/settings/settings/widgets/base.py b/pype/tools/settings/settings/widgets/base.py index 6a7a65cb0f..90e3aca677 100644 --- a/pype/tools/settings/settings/widgets/base.py +++ b/pype/tools/settings/settings/widgets/base.py @@ -11,14 +11,14 @@ from pype.settings.lib import ( DEFAULTS_DIR, - reset_default_configurations, - default_configuration, + reset_default_settings, + default_settings, - studio_system_configurations, - studio_project_configurations, + studio_system_settings, + studio_project_settings, studio_project_anatomy, - project_configurations_overrides, + project_settings_overrides, project_anatomy_overrides, path_to_project_overrides, @@ -179,7 +179,7 @@ class SystemWidget(QtWidgets.QWidget): all_values = all_values["system"] prject_defaults_dir = os.path.join( - DEFAULTS_DIR, SYSTEM_CONFIGURATIONS_KEY + DEFAULTS_DIR, SYSTEM_SETTINGS_KEY ) keys_to_file = lib.file_keys_from_schema(self.schema) for key_sequence in keys_to_file: @@ -200,7 +200,7 @@ class SystemWidget(QtWidgets.QWidget): with open(output_path, "w") as file_stream: json.dump(new_values, file_stream, indent=4) - reset_default_configurations() + reset_default_settings() self._update_values() @@ -208,12 +208,12 @@ class SystemWidget(QtWidgets.QWidget): self.ignore_value_changes = True default_values = { - "system": default_configuration()["system_configurations"] + "system": default_settings()[SYSTEM_SETTINGS_KEY] } for input_field in self.input_fields: input_field.update_default_values(default_values) - system_values = {"system": studio_system_configurations()} + system_values = {"system": studio_system_settings()} for input_field in self.input_fields: input_field.update_studio_values(system_values) @@ -462,7 +462,7 @@ class ProjectWidget(QtWidgets.QWidget): _project_anatomy = lib.NOT_SET self.is_overidable = False else: - _project_overrides = project_configurations_overrides(project_name) + _project_overrides = project_settings_overrides(project_name) _project_anatomy = project_anatomy_overrides(project_name) self.is_overidable = True @@ -519,7 +519,7 @@ class ProjectWidget(QtWidgets.QWidget): with open(output_path, "w") as file_stream: json.dump(new_values, file_stream, indent=4) - reset_default_configurations() + reset_default_settings() self._update_values() @@ -638,12 +638,12 @@ class ProjectWidget(QtWidgets.QWidget): def _update_values(self): self.ignore_value_changes = True - default_values = {"project": default_configuration()} + default_values = {"project": default_settings()} for input_field in self.input_fields: input_field.update_default_values(default_values) studio_values = {"project": { - PROJECT_SETTINGS_KEY: studio_project_configurations(), + PROJECT_SETTINGS_KEY: studio_project_settings(), PROJECT_ANATOMY_KEY: studio_project_anatomy() }} for input_field in self.input_fields: From ee612be204b6109c4f0672750339eb13a45732fc Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 14 Sep 2020 18:35:39 +0200 Subject: [PATCH 594/813] implemented set as studio override for anatomy items --- pype/tools/settings/settings/widgets/anatomy_inputs.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pype/tools/settings/settings/widgets/anatomy_inputs.py b/pype/tools/settings/settings/widgets/anatomy_inputs.py index 0e7e094be5..50a6022e3c 100644 --- a/pype/tools/settings/settings/widgets/anatomy_inputs.py +++ b/pype/tools/settings/settings/widgets/anatomy_inputs.py @@ -570,6 +570,13 @@ class RootsWidget(QtWidgets.QWidget, SettingObject): self.singleroot_widget.reset_to_pype_default() self._has_studio_override = False + def set_studio_default(self): + if self.is_multiroot: + self.multiroot_widget.reset_to_pype_default() + else: + self.singleroot_widget.reset_to_pype_default() + self._has_studio_override = True + def discard_changes(self): self._is_overriden = self._was_overriden self._is_modified = False @@ -718,6 +725,9 @@ class TemplatesWidget(QtWidgets.QWidget, SettingObject): def reset_to_pype_default(self): self.value_input.reset_to_pype_default() + def set_studio_default(self): + self.value_input.set_studio_default() + def discard_changes(self): self.value_input.discard_changes() From 38e8f5962826bd8d5edd6dc32f290b37234d6a36 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 14 Sep 2020 18:38:51 +0200 Subject: [PATCH 595/813] renamed inputs to item_types --- pype/tools/settings/settings/widgets/__init__.py | 9 ++++----- .../widgets/{anatomy_inputs.py => anatomy_types.py} | 4 +++- .../settings/widgets/{inputs.py => item_types.py} | 0 3 files changed, 7 insertions(+), 6 deletions(-) rename pype/tools/settings/settings/widgets/{anatomy_inputs.py => anatomy_types.py} (99%) rename pype/tools/settings/settings/widgets/{inputs.py => item_types.py} (100%) diff --git a/pype/tools/settings/settings/widgets/__init__.py b/pype/tools/settings/settings/widgets/__init__.py index 034692898c..e5ea4b3129 100644 --- a/pype/tools/settings/settings/widgets/__init__.py +++ b/pype/tools/settings/settings/widgets/__init__.py @@ -1,10 +1,9 @@ from .window import MainWidget -# TODO properly register inputs to TypeToKlass class -from . import inputs -from . import anatomy_inputs +from . import item_types +from . import anatomy_type __all__ = [ "MainWidget", - "inputs", - "anatomy_inputs" + "item_types", + "anatomy_type" ] diff --git a/pype/tools/settings/settings/widgets/anatomy_inputs.py b/pype/tools/settings/settings/widgets/anatomy_types.py similarity index 99% rename from pype/tools/settings/settings/widgets/anatomy_inputs.py rename to pype/tools/settings/settings/widgets/anatomy_types.py index 50a6022e3c..9433d7a823 100644 --- a/pype/tools/settings/settings/widgets/anatomy_inputs.py +++ b/pype/tools/settings/settings/widgets/anatomy_types.py @@ -1,6 +1,8 @@ from Qt import QtWidgets, QtCore from .widgets import ExpandingWidget -from .inputs import SettingObject, ModifiableDict, PathWidget, RawJsonWidget +from .item_types import ( + SettingObject, ModifiableDict, PathWidget, RawJsonWidget +) from .lib import NOT_SET, TypeToKlass, CHILD_OFFSET, METADATA_KEY diff --git a/pype/tools/settings/settings/widgets/inputs.py b/pype/tools/settings/settings/widgets/item_types.py similarity index 100% rename from pype/tools/settings/settings/widgets/inputs.py rename to pype/tools/settings/settings/widgets/item_types.py From 0884ba26900cc7ba8e626a7a3d84a8f092a6f5bd Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 14 Sep 2020 18:39:24 +0200 Subject: [PATCH 596/813] fixed typo --- pype/tools/settings/settings/widgets/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/tools/settings/settings/widgets/__init__.py b/pype/tools/settings/settings/widgets/__init__.py index e5ea4b3129..361fd9d23d 100644 --- a/pype/tools/settings/settings/widgets/__init__.py +++ b/pype/tools/settings/settings/widgets/__init__.py @@ -1,9 +1,9 @@ from .window import MainWidget from . import item_types -from . import anatomy_type +from . import anatomy_types __all__ = [ "MainWidget", "item_types", - "anatomy_type" + "anatomy_types" ] From cd36685ef8406a68b0761d5d04f346c3c511addd Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 14 Sep 2020 18:49:15 +0200 Subject: [PATCH 597/813] fix studio overrides for anatomy roots --- pype/tools/settings/settings/widgets/anatomy_types.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/tools/settings/settings/widgets/anatomy_types.py b/pype/tools/settings/settings/widgets/anatomy_types.py index 9433d7a823..0d94f6f86f 100644 --- a/pype/tools/settings/settings/widgets/anatomy_types.py +++ b/pype/tools/settings/settings/widgets/anatomy_types.py @@ -519,9 +519,9 @@ class RootsWidget(QtWidgets.QWidget, SettingObject): @property def child_has_studio_override(self): if self.is_multiroot: - return self.multiroot_widget.child_has_studio_override + return self.multiroot_widget.has_studio_override else: - return self.singleroot_widget.child_has_studio_override + return self.singleroot_widget.has_studio_override @property def child_modified(self): From 35beb0a657182657069e2180135118a41a348141 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 14 Sep 2020 18:54:09 +0200 Subject: [PATCH 598/813] fixed setting pype default for anatomy --- pype/tools/settings/settings/widgets/anatomy_types.py | 5 +++++ pype/tools/settings/settings/widgets/item_types.py | 8 +++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/pype/tools/settings/settings/widgets/anatomy_types.py b/pype/tools/settings/settings/widgets/anatomy_types.py index 0d94f6f86f..dd4b5dc578 100644 --- a/pype/tools/settings/settings/widgets/anatomy_types.py +++ b/pype/tools/settings/settings/widgets/anatomy_types.py @@ -196,6 +196,10 @@ class AnatomyWidget(QtWidgets.QWidget, SettingObject): self.root_widget.reset_to_pype_default() self.templates_widget.reset_to_pype_default() + def set_studio_default(self): + self.root_widget.set_studio_default() + self.templates_widget.set_studio_default() + def discard_changes(self): self.root_widget.discard_changes() self.templates_widget.discard_changes() @@ -596,6 +600,7 @@ class RootsWidget(QtWidgets.QWidget, SettingObject): self.singleroot_widget.discard_changes() self._is_modified = self.child_modified + self._has_studio_override = self._had_studio_override def set_as_overriden(self): self._is_overriden = True diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index aa72f1c81f..b0de7910ab 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -194,7 +194,6 @@ class SettingObject(AbstractSettingObject): not self.is_overidable and ( self.has_studio_override - or self.child_has_studio_override ) ): action = QtWidgets.QAction("Reset to pype default") @@ -203,10 +202,9 @@ class SettingObject(AbstractSettingObject): if ( not self.is_overidable - and ( - (self.is_group and not self._had_studio_override) - or self.any_parent_is_group - ) + and not self.is_overriden + and not self.any_parent_is_group + and not self._had_studio_override ): action = QtWidgets.QAction("Set sudio default") actions_mapping[action] = self._set_studio_default From 438aab08662f48f8879136a0f83635bf7be3f298 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 14 Sep 2020 18:56:16 +0200 Subject: [PATCH 599/813] added window title --- pype/tools/settings/settings/widgets/window.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pype/tools/settings/settings/widgets/window.py b/pype/tools/settings/settings/widgets/window.py index 5c7a35fa52..f83da8efe0 100644 --- a/pype/tools/settings/settings/widgets/window.py +++ b/pype/tools/settings/settings/widgets/window.py @@ -9,6 +9,7 @@ class MainWidget(QtWidgets.QWidget): def __init__(self, develop, parent=None): super(MainWidget, self).__init__(parent) self.setObjectName("MainWidget") + self.setWindowTitle("Pype Settings") self.resize(self.widget_width, self.widget_height) From 3b69f890a30b15a177d937e6db1deaa59a484e04 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 14 Sep 2020 19:00:07 +0200 Subject: [PATCH 600/813] added refresh to develop mode --- pype/tools/settings/settings/widgets/base.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/pype/tools/settings/settings/widgets/base.py b/pype/tools/settings/settings/widgets/base.py index 90e3aca677..437a74b8c1 100644 --- a/pype/tools/settings/settings/widgets/base.py +++ b/pype/tools/settings/settings/widgets/base.py @@ -27,6 +27,7 @@ from pype.settings.lib import ( from .widgets import UnsavedChangesDialog from . import lib from avalon import io +from avalon.vendor import qtawesome class SystemWidget(QtWidgets.QWidget): @@ -65,9 +66,16 @@ class SystemWidget(QtWidgets.QWidget): if self.develop_mode: save_as_default_btn = QtWidgets.QPushButton("Save as Default") - footer_layout.addWidget(save_as_default_btn, 0) save_as_default_btn.clicked.connect(self._save_as_defaults) + refresh_icon = qtawesome.icon("fa.refresh", color="white") + refresh_button = QtWidgets.QPushButton() + refresh_button.setIcon(refresh_icon) + refresh_button.clicked.connect(self._on_refresh) + + footer_layout.addWidget(save_as_default_btn, 0) + footer_layout.addWidget(refresh_button, 0) + save_btn = QtWidgets.QPushButton("Save") spacer_widget = QtWidgets.QWidget() footer_layout.addWidget(spacer_widget, 1) @@ -158,6 +166,9 @@ class SystemWidget(QtWidgets.QWidget): self._update_values() + def _on_refresh(self): + self.reset() + def _save_as_defaults(self): output = {} for item in self.input_fields: From d91c890a7d8335e245d3bf2d1f9939b1bf321e04 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 15 Sep 2020 11:23:39 +0200 Subject: [PATCH 601/813] fixed path-widget default value for dev mode --- .../settings/settings/widgets/item_types.py | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index b0de7910ab..6433956484 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -2136,13 +2136,18 @@ class PathWidget(QtWidgets.QWidget, SettingObject): @property def default_input_value(self): + if self.multipath: + value_type = list + else: + value_type = str + if self.multiplatform: return { - platform: "" + platform: value_type() for platform in self.platforms } else: - return "" + return value_type() def create_gui(self): if not self.multiplatform and not self.multipath: @@ -2204,9 +2209,19 @@ class PathWidget(QtWidgets.QWidget, SettingObject): value = parent_values.get(self.key, NOT_SET) if value is NOT_SET: - raise ValueError( - "Default value is not set. This is implementation BUG." - ) + if self.develop_mode: + value = {self.key: self.default_input_value} + self.defaults_not_set = True + if value is NOT_SET: + raise NotImplementedError(( + "{} Does not have implemented" + " attribute `default_input_value`" + ).format(self)) + + else: + raise ValueError( + "Default value is not set. This is implementation BUG." + ) self.default_value = value self._has_studio_override = False From 67a38042264fc622c9b7bf0833d549d055db3435 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 15 Sep 2020 11:45:13 +0200 Subject: [PATCH 602/813] fixed expandable widget toggle_content --- pype/tools/settings/settings/widgets/widgets.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/pype/tools/settings/settings/widgets/widgets.py b/pype/tools/settings/settings/widgets/widgets.py index bb42df6026..96f8d2ec17 100644 --- a/pype/tools/settings/settings/widgets/widgets.py +++ b/pype/tools/settings/settings/widgets/widgets.py @@ -105,7 +105,7 @@ class ExpandingWidget(QtWidgets.QWidget): self.label_widget = label_widget top_part.clicked.connect(self._top_part_clicked) - self.button_toggle.clicked.connect(self.toggle_content) + self.button_toggle.clicked.connect(self._btn_clicked) self.main_layout = QtWidgets.QVBoxLayout(self) self.main_layout.setContentsMargins(0, 0, 0, 0) @@ -123,16 +123,20 @@ class ExpandingWidget(QtWidgets.QWidget): self.main_layout.addWidget(content_widget) self.content_widget = content_widget + def _btn_clicked(self): + self.toggle_content(self.button_toggle.isChecked()) + def _top_part_clicked(self): - self.toggle_content(not self.button_toggle.isChecked()) + self.toggle_content() def toggle_content(self, *args): if self.toolbox_hidden: return + if len(args) > 0: checked = args[0] else: - checked = self.button_toggle.isChecked() + checked = not self.button_toggle.isChecked() arrow_type = QtCore.Qt.RightArrow if checked: arrow_type = QtCore.Qt.DownArrow From c42927120b1fa6a7abf1ee62624666096f905bc2 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 15 Sep 2020 11:47:07 +0200 Subject: [PATCH 603/813] changed `expandable` to `collapsable` and `expanded` to `collapsed` --- .../projects_schema/1_plugins_gui_schema.json | 80 +++++++++---------- .../system_schema/1_intents_gui_schema.json | 2 +- .../system_schema/1_tools_gui_schema.json | 2 +- .../system_schema/1_tray_items.json | 8 +- .../settings/settings/widgets/item_types.py | 21 ++--- 5 files changed, 57 insertions(+), 56 deletions(-) diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/1_plugins_gui_schema.json b/pype/tools/settings/settings/gui_schemas/projects_schema/1_plugins_gui_schema.json index c279a6b04a..6bb14463c1 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/1_plugins_gui_schema.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/1_plugins_gui_schema.json @@ -1,25 +1,25 @@ { "type": "dict", - "expandable": true, + "collapsable": true, "key": "plugins", "label": "Plugins", "children": [ { "type": "dict", - "expandable": true, + "collapsable": true, "key": "celaction", "label": "CelAction", "children": [ { "type": "dict", - "expandable": true, + "collapsable": true, "key": "publish", "label": "Publish plugins", "is_file": true, "children": [ { "type": "dict", - "expandable": true, + "collapsable": true, "checkbox_key": "enabled", "key": "ExtractCelactionDeadline", "label": "ExtractCelactionDeadline", @@ -66,20 +66,20 @@ ] }, { "type": "dict", - "expandable": true, + "collapsable": true, "key": "ftrack", "label": "Ftrack", "children": [ { "type": "dict", - "expandable": true, + "collapsable": true, "key": "publish", "label": "Publish plugins", "is_file": true, "children": [ { "type": "dict", - "expandable": true, + "collapsable": true, "checkbox_key": "enabled", "key": "IntegrateFtrackNote", "label": "IntegrateFtrackNote", @@ -106,20 +106,20 @@ ] }, { "type": "dict", - "expandable": true, + "collapsable": true, "key": "global", "label": "Global", "children": [ { "type": "dict", - "expandable": true, + "collapsable": true, "key": "publish", "label": "Publish plugins", "is_file": true, "children": [ { "type": "dict", - "expandable": true, + "collapsable": true, "checkbox_key": "enabled", "key": "IntegrateMasterVersion", "label": "IntegrateMasterVersion", @@ -133,7 +133,7 @@ ] }, { "type": "dict", - "expandable": true, + "collapsable": true, "checkbox_key": "enabled", "key": "ExtractJpegEXR", "label": "ExtractJpegEXR", @@ -163,7 +163,7 @@ ] }, { "type": "dict", - "expandable": true, + "collapsable": true, "key": "ExtractReview", "label": "ExtractReview", "checkbox_key": "enabled", @@ -181,7 +181,7 @@ ] }, { "type": "dict", - "expandable": true, + "collapsable": true, "key": "ExtractBurnin", "label": "ExtractBurnin", "checkbox_key": "enabled", @@ -193,7 +193,7 @@ "label": "Enabled" }, { "type": "dict", - "expandable": true, + "collapsable": true, "key": "options", "label": "Burnin formating options", "children": [ @@ -231,7 +231,7 @@ ] }, { "type": "dict", - "expandable": true, + "collapsable": true, "key": "IntegrateAssetNew", "label": "IntegrateAssetNew", "is_group": true, @@ -244,7 +244,7 @@ ] }, { "type": "dict", - "expandable": true, + "collapsable": true, "key": "ProcessSubmittedJobOnFarm", "label": "ProcessSubmittedJobOnFarm", "checkbox_key": "enabled", @@ -274,20 +274,20 @@ ] }, { "type": "dict", - "expandable": true, + "collapsable": true, "key": "maya", "label": "Maya", "children": [ { "type": "dict", - "expandable": true, + "collapsable": true, "key": "publish", "label": "Publish plugins", "is_file": true, "children": [ { "type": "dict", - "expandable": true, + "collapsable": true, "key": "ValidateModelName", "label": "Validate Model Name", "checkbox_key": "enabled", @@ -309,7 +309,7 @@ ] }, { "type": "dict", - "expandable": true, + "collapsable": true, "key": "ValidateAssemblyName", "label": "Validate Assembly Name", "checkbox_key": "enabled", @@ -323,7 +323,7 @@ ] }, { "type": "dict", - "expandable": true, + "collapsable": true, "key": "ValidateShaderName", "label": "ValidateShaderName", "checkbox_key": "enabled", @@ -341,7 +341,7 @@ ] }, { "type": "dict", - "expandable": true, + "collapsable": true, "key": "ValidateMeshHasOverlappingUVs", "label": "ValidateMeshHasOverlappingUVs", "checkbox_key": "enabled", @@ -364,20 +364,20 @@ ] }, { "type": "dict", - "expandable": true, + "collapsable": true, "key": "nuke", "label": "Nuke", "children": [ { "type": "dict", - "expandable": true, + "collapsable": true, "key": "create", "label": "Create plugins", "is_file": true, "children": [ { "type": "dict", - "expandable": false, + "collapsable": false, "key": "CreateWriteRender", "label": "CreateWriteRender", "is_group": true, @@ -390,7 +390,7 @@ ] }, { "type": "dict", - "expandable": false, + "collapsable": false, "key": "CreateWritePrerender", "label": "CreateWritePrerender", "is_group": true, @@ -405,14 +405,14 @@ ] }, { "type": "dict", - "expandable": true, + "collapsable": true, "key": "publish", "label": "Publish plugins", "is_file": true, "children": [ { "type": "dict", - "expandable": true, + "collapsable": true, "checkbox_key": "enabled", "key": "ExtractThumbnail", "label": "ExtractThumbnail", @@ -430,7 +430,7 @@ ] }, { "type": "dict", - "expandable": true, + "collapsable": true, "checkbox_key": "enabled", "key": "ValidateNukeWriteKnobs", "label": "ValidateNukeWriteKnobs", @@ -448,7 +448,7 @@ ] }, { "type": "dict", - "expandable": true, + "collapsable": true, "checkbox_key": "enabled", "key": "ExtractReviewDataLut", "label": "ExtractReviewDataLut", @@ -462,7 +462,7 @@ ] }, { "type": "dict", - "expandable": true, + "collapsable": true, "checkbox_key": "enabled", "key": "ExtractReviewDataMov", "label": "ExtractReviewDataMov", @@ -480,7 +480,7 @@ ] }, { "type": "dict", - "expandable": true, + "collapsable": true, "key": "ExtractSlateFrame", "label": "ExtractSlateFrame", "is_group": true, @@ -493,7 +493,7 @@ ] }, { "type": "dict", - "expandable": true, + "collapsable": true, "key": "NukeSubmitDeadline", "label": "NukeSubmitDeadline", "is_group": true, @@ -527,20 +527,20 @@ ] }, { "type": "dict", - "expandable": true, + "collapsable": true, "key": "nukestudio", "label": "NukeStudio", "children": [ { "type": "dict", - "expandable": true, + "collapsable": true, "key": "publish", "label": "Publish plugins", "is_file": true, "children": [ { "type": "dict", - "expandable": true, + "collapsable": true, "checkbox_key": "enabled", "key": "CollectInstanceVersion", "label": "Collect Instance Version", @@ -554,7 +554,7 @@ ] }, { "type": "dict", - "expandable": true, + "collapsable": true, "checkbox_key": "enabled", "key": "ExtractReviewCutUpVideo", "label": "Extract Review Cut Up Video", @@ -577,20 +577,20 @@ ] }, { "type": "dict", - "expandable": true, + "collapsable": true, "key": "resolve", "label": "DaVinci Resolve", "children": [ { "type": "dict", - "expandable": true, + "collapsable": true, "key": "create", "label": "Creator plugins", "is_file": true, "children": [ { "type": "dict", - "expandable": true, + "collapsable": true, "key": "CreateShotClip", "label": "Create Shot Clip", "is_group": true, diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/1_intents_gui_schema.json b/pype/tools/settings/settings/gui_schemas/system_schema/1_intents_gui_schema.json index a4b5e16fa1..0c252d2ca9 100644 --- a/pype/tools/settings/settings/gui_schemas/system_schema/1_intents_gui_schema.json +++ b/pype/tools/settings/settings/gui_schemas/system_schema/1_intents_gui_schema.json @@ -2,7 +2,7 @@ "key": "intent", "type": "dict", "label": "Intent Setting", - "expandable": true, + "collapsable": true, "is_group": true, "is_file": true, "children": [ diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/1_tools_gui_schema.json b/pype/tools/settings/settings/gui_schemas/system_schema/1_tools_gui_schema.json index bf35326d1c..d9540eeb3e 100644 --- a/pype/tools/settings/settings/gui_schemas/system_schema/1_tools_gui_schema.json +++ b/pype/tools/settings/settings/gui_schemas/system_schema/1_tools_gui_schema.json @@ -2,7 +2,7 @@ "key": "tools", "type": "dict", "label": "Tools", - "expandable": true, + "collapsable": true, "is_group": true, "is_file": true, "children": [ diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/1_tray_items.json b/pype/tools/settings/settings/gui_schemas/system_schema/1_tray_items.json index 7507050900..6da974a415 100644 --- a/pype/tools/settings/settings/gui_schemas/system_schema/1_tray_items.json +++ b/pype/tools/settings/settings/gui_schemas/system_schema/1_tray_items.json @@ -2,7 +2,7 @@ "key": "tray_modules", "type": "dict", "label": "Modules", - "expandable": true, + "collapsable": true, "is_group": true, "is_file": true, "children": [ @@ -69,7 +69,7 @@ "type": "dict", "key": "Rest Api", "label": "Rest Api", - "expandable": true, + "collapsable": true, "children": [ { "type": "number", @@ -92,7 +92,7 @@ "type": "dict", "key": "Timers Manager", "label": "Timers Manager", - "expandable": true, + "collapsable": true, "children": [ { "type": "number", @@ -110,7 +110,7 @@ "type": "dict", "key": "Clockify", "label": "Clockify", - "expandable": true, + "collapsable": true, "children": [ { "type": "text", diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 6433956484..729ab3a70f 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -1343,14 +1343,15 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): self.body_widget = body_widget self.label_widget = body_widget.label_widget - expandable = input_data.get("expandable", True) - if not expandable: - body_widget.hide_toolbox(hide_content=False) - else: - expanded = input_data.get("expanded", False) - if expanded: + collapsable = input_data.get("collapsable", True) + if collapsable: + collapsed = input_data.get("collapsed", True) + if not collapsed: body_widget.toggle_content() + else: + body_widget.hide_toolbox(hide_content=False) + self.body_widget = body_widget self.content_widget = content_widget self.content_layout = content_layout @@ -1590,13 +1591,13 @@ class DictWidget(QtWidgets.QWidget, SettingObject): for child_data in input_data.get("children", []): self.add_children_gui(child_data) - expandable = input_data.get("expandable", True) + collapsable = input_data.get("collapsable", True) if len(self.input_fields) == 1 and self.checkbox_widget: body_widget.hide_toolbox(hide_content=True) - elif expandable: - expanded = input_data.get("expanded", False) - if expanded: + elif collapsable: + collapsed = input_data.get("collapsed", True) + if not collapsed: body_widget.toggle_content() else: body_widget.hide_toolbox(hide_content=False) From 0a474167663ac6001ea92539cf685ceda8defcd3 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 15 Sep 2020 11:47:36 +0200 Subject: [PATCH 604/813] modifiable dict fixed states --- .../settings/settings/widgets/item_types.py | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 729ab3a70f..2b6bcfe34c 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -1165,7 +1165,6 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): self.set_default_attributes() self._parent = config_parent - self.is_single = False self.is_key_duplicated = False layout = QtWidgets.QHBoxLayout(self) @@ -1212,7 +1211,13 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): def key_value(self): return self.key_input.text() + def _is_enabled(self): + return self.key_input.isEnabled() + def is_key_invalid(self): + if not self._is_enabled(): + return False + if self.key_value() == "": return True @@ -1244,7 +1249,7 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): return self._parent.is_group def on_add_clicked(self): - if self.value_input.isEnabled(): + if self._is_enabled(): self._parent.add_row(row=self.row() + 1) else: self.set_as_empty(False) @@ -1278,15 +1283,17 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): @property def is_invalid(self): + if not self._is_enabled(): + return False return self.is_key_invalid() or self.value_input.is_invalid def update_style(self): - if self.is_key_invalid(): - state = "invalid" - elif self.is_key_modified(): - state = "modified" - else: - state = "" + state = "" + if self._is_enabled(): + if self.is_key_invalid(): + state = "invalid" + elif self.is_key_modified(): + state = "modified" self.key_input.setProperty("state", state) self.key_input.style().polish(self.key_input) From 703e17b35a2654e9696858acf6c0fd3679ef0532 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 15 Sep 2020 11:53:57 +0200 Subject: [PATCH 605/813] removed bold style on label for invalid value --- pype/tools/settings/settings/style/style.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/tools/settings/settings/style/style.css b/pype/tools/settings/settings/style/style.css index f6dd354935..ab88a2c36f 100644 --- a/pype/tools/settings/settings/style/style.css +++ b/pype/tools/settings/settings/style/style.css @@ -56,8 +56,8 @@ QLabel[state="overriden-modified"] {color: #137cbd;} QLabel[state="overriden-modified"]:hover {color: #1798e8;} QLabel[state="overriden"] {color: #ff8c1a;} QLabel[state="overriden"]:hover {color: #ffa64d;} -QLabel[state="invalid"] {color: #ad2e2e; font-weight: bold;} -QLabel[state="invalid"]:hover {color: #ad2e2e; font-weight: bold;} +QLabel[state="invalid"] {color: #ad2e2e;} +QLabel[state="invalid"]:hover {color: #ad2e2e;} QWidget[input-state="studio"] {border-color: #bfccd6;} From 2d4f983a88674a21f6e05b4398e1b5a4e8ac2c3a Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 15 Sep 2020 11:54:12 +0200 Subject: [PATCH 606/813] using only horizontal layouts --- .../system_schema/1_applications_gui_schema.json | 2 +- pype/tools/settings/settings/widgets/item_types.py | 9 +++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/1_applications_gui_schema.json b/pype/tools/settings/settings/gui_schemas/system_schema/1_applications_gui_schema.json index 5ee769eed8..48f8ecbd7c 100644 --- a/pype/tools/settings/settings/gui_schemas/system_schema/1_applications_gui_schema.json +++ b/pype/tools/settings/settings/gui_schemas/system_schema/1_applications_gui_schema.json @@ -2,7 +2,7 @@ "key": "applications", "type": "dict", "label": "Applications", - "expandable": true, + "collapsable": true, "is_group": true, "is_file": true, "children": [ diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 2b6bcfe34c..754b4512bd 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -805,9 +805,9 @@ class RawJsonWidget(QtWidgets.QWidget, InputObject): self.initial_attributes(input_data, parent, as_widget) - layout = QtWidgets.QVBoxLayout(self) + layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(0) + layout.setSpacing(5) self.text_input = RawJsonInput(self) self.text_input.setSizePolicy( @@ -2117,10 +2117,7 @@ class PathWidget(QtWidgets.QWidget, SettingObject): self.input_fields = [] - if not self.multiplatform and not self.multipath: - layout = QtWidgets.QHBoxLayout(self) - else: - layout = QtWidgets.QVBoxLayout(self) + layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(5) From 6f27fbdcbc960fe47a213b0cd1c506c06a96e7fc Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 15 Sep 2020 11:35:53 +0200 Subject: [PATCH 607/813] #180 - Enrich tasks with additional information Tasks in project config or on assets were stored only as names. This changes them into dictionaries to enhance information that could be stored and used elsewhere later. --- pype/modules/ftrack/lib/avalon_sync.py | 41 ++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/pype/modules/ftrack/lib/avalon_sync.py b/pype/modules/ftrack/lib/avalon_sync.py index 65a59452da..7dd4056524 100644 --- a/pype/modules/ftrack/lib/avalon_sync.py +++ b/pype/modules/ftrack/lib/avalon_sync.py @@ -16,6 +16,7 @@ from bson.objectid import ObjectId from bson.errors import InvalidId from pymongo import UpdateOne import ftrack_api +from pype.api import config log = Logger().get_logger(__name__) @@ -238,6 +239,27 @@ def get_hierarchical_attributes(session, entity, attr_names, attr_defaults={}): return hier_values +def get_task_short_name(task_type): + """ + Returns short name (code) for 'task_type'. Short name stored in + metadata dictionary in project.config per each 'task_type'. + Could be used in anatomy, paths etc. + If no appropriate short name is found in mapping, 'task_type' is + returned back unchanged. + + Currently stores data in: + 'pype-config/presets/ftrack/project_defaults.json' + Args: + task_type: (string) - Animation | Modeling ... + + Returns: + (string) - anim | model ... + """ + presets = config.get_presets()['ftrack']['project_defaults']\ + .get("task_short_names") + + return presets.get(task_type, task_type) + class SyncEntitiesFactory: dbcon = AvalonMongoDB() @@ -389,7 +411,9 @@ class SyncEntitiesFactory: continue elif entity_type_low == "task": - entities_dict[parent_id]["tasks"].append(entity["name"]) + # enrich task info with additional metadata + task = {"name": entity["name"], "type": entity["type"]["name"]} + entities_dict[parent_id]["tasks"].append(task) continue entity_id = entity["id"] @@ -534,8 +558,9 @@ class SyncEntitiesFactory: name = entity_dict["name"] entity_type = entity_dict["entity_type"] # Tasks must be checked too - for task_name in entity_dict["tasks"]: - passed = task_names.get(task_name) + for task in entity_dict["tasks"]: + task_name = task.get("name") + passed = task_name if passed is None: passed = check_regex( task_name, "task", schema_patterns=_schema_patterns @@ -1014,9 +1039,14 @@ class SyncEntitiesFactory: if not msg or not items: continue self.report_items["warning"][msg] = items - + tasks = [] + for tt in task_types: + tasks.append({ + "name": tt["name"], + "short_name": get_task_short_name(tt["name"]) + }) self.entities_dict[id]["final_entity"]["config"] = { - "tasks": [{"name": tt["name"]} for tt in task_types], + "tasks": tasks, "apps": proj_apps } continue @@ -1904,7 +1934,6 @@ class SyncEntitiesFactory: filter = {"_id": ObjectId(mongo_id)} change_data = from_dict_to_set(changes) mongo_changes_bulk.append(UpdateOne(filter, change_data)) - if not mongo_changes_bulk: # TODO LOG return From 1a7ba6f8d789572d755002cb8fb5362a7e9a6168 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 15 Sep 2020 12:41:59 +0200 Subject: [PATCH 608/813] added `is_input_type` attribute for non input item types --- pype/tools/settings/settings/widgets/item_types.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 754b4512bd..5f995b40b7 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -12,6 +12,7 @@ from .lib import NOT_SET, METADATA_KEY, TypeToKlass, CHILD_OFFSET class SettingObject(AbstractSettingObject): + is_input_type = True default_input_value = NOT_SET allow_actions = True default_state = "" @@ -1612,6 +1613,12 @@ class DictWidget(QtWidgets.QWidget, SettingObject): def add_children_gui(self, child_configuration): item_type = child_configuration["type"] klass = TypeToKlass.types.get(item_type) + + if not klass.is_input_type: + item = klass(child_configuration, self) + self.content_layout.addWidget(item) + return item + if self.checkbox_key and not self.checkbox_widget: key = child_configuration.get("key") if key == self.checkbox_key: @@ -1898,6 +1905,11 @@ class DictInvisible(QtWidgets.QWidget, SettingObject): item_type = child_configuration["type"] klass = TypeToKlass.types.get(item_type) + if not klass.is_input_type: + item = klass(child_configuration, self) + self.layout().addWidget(item) + return item + item = klass(child_configuration, self) self.layout().addWidget(item) From 0a9c96d27b4d755d7cb641304bad2d43ea82ba9c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 15 Sep 2020 12:42:12 +0200 Subject: [PATCH 609/813] added anatomy widgets to TypeToKlass --- pype/tools/settings/settings/widgets/anatomy_types.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pype/tools/settings/settings/widgets/anatomy_types.py b/pype/tools/settings/settings/widgets/anatomy_types.py index dd4b5dc578..785632b08d 100644 --- a/pype/tools/settings/settings/widgets/anatomy_types.py +++ b/pype/tools/settings/settings/widgets/anatomy_types.py @@ -754,3 +754,5 @@ class TemplatesWidget(QtWidgets.QWidget, SettingObject): TypeToKlass.types["anatomy"] = AnatomyWidget +TypeToKlass.types["anatomy_roots"] = AnatomyWidget +TypeToKlass.types["anatomy_templates"] = AnatomyWidget From 3bde8a77a2ac5275760fd673d8cb2dc7af21062a Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 15 Sep 2020 12:42:27 +0200 Subject: [PATCH 610/813] added examples schema --- .../gui_schemas/system_schema/1_examples.json | 234 ++++++++++++++++++ 1 file changed, 234 insertions(+) create mode 100644 pype/tools/settings/settings/gui_schemas/system_schema/1_examples.json 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 new file mode 100644 index 0000000000..a884dcb31e --- /dev/null +++ b/pype/tools/settings/settings/gui_schemas/system_schema/1_examples.json @@ -0,0 +1,234 @@ +{ + "key": "example_dict", + "label": "Examples", + "type": "dict", + "is_file": true, + "children": [ + { + "key": "dict_wrapper", + "type": "dict-invisible", + "children": [ + { + "type": "boolean", + "key": "bool", + "label": "Boolean checkbox" + }, { + "type": "label", + "label": "NOTE: This is label" + }, { + "type": "splitter" + }, { + "type": "number", + "key": "integer", + "label": "Integer", + "decimal": 0, + "minimum": 0, + "maximum": 10 + }, { + "type": "number", + "key": "float", + "label": "Float (2 decimals)", + "decimal": 2, + "minimum": -10, + "maximum": -5 + }, { + "type": "text", + "key": "singleline_text", + "label": "Singleline text" + }, { + "type": "text", + "key": "multiline_text", + "label": "Multiline text", + "multiline": true + }, { + "type": "raw-json", + "key": "raw_json", + "label": "Raw json input" + }, { + "type": "list", + "key": "list_item_of_multiline_texts", + "label": "List of multiline texts", + "object_type": "text", + "input_modifiers": { + "multiline": true + } + }, { + "type": "list", + "key": "list_item_of_floats", + "label": "List of floats", + "object_type": "number", + "input_modifiers": { + "decimal": 3, + "minimum": 1000, + "maximum": 2000 + } + }, { + "type": "dict-modifiable", + "key": "modifiable_dict_of_integers", + "label": "Modifiable dict of integers", + "object_type": "number", + "input_modifiers": { + "decimal": 0, + "minimum": 10, + "maximum": 100 + } + }, { + "type": "path-widget", + "key": "single_path_input", + "label": "Single path input", + "multiplatform": false, + "multipath": false + }, { + "type": "path-widget", + "key": "multi_path_input", + "label": "Multi path input", + "multiplatform": false, + "multipath": true + }, { + "type": "path-widget", + "key": "single_os_specific_path_input", + "label": "Single OS specific path input", + "multiplatform": true, + "multipath": false + }, { + "type": "path-widget", + "key": "multi_os_specific_path_input", + "label": "Multi OS specific path input", + "multiplatform": true, + "multipath": true + }, { + "key": "collapsable", + "type": "dict", + "label": "collapsable dictionary", + "collapsable": true, + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "_nothing", + "label": "Exmaple input" + } + ] + }, { + "key": "collapsable_expanded", + "type": "dict", + "label": "collapsable dictionary, expanded on creation", + "collapsable": true, + "collapsed": false, + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "_nothing", + "label": "Exmaple input" + } + ] + }, { + "key": "not_collapsable", + "type": "dict", + "label": "Not collapsable", + "collapsable": false, + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "_nothing", + "label": "Exmaple input" + } + ] + }, { + "key": "nested_dict_lvl1", + "type": "dict", + "label": "Nested dictionary (level 1)", + "children": [ + { + "key": "nested_dict_lvl2", + "type": "dict", + "label": "Nested dictionary (level 2)", + "is_group": true, + "children": [ + { + "key": "nested_dict_lvl3", + "type": "dict", + "label": "Nested dictionary (level 3)", + "children": [ + { + "type": "boolean", + "key": "_nothing", + "label": "Exmaple input" + } + ] + }, { + "key": "nested_dict_lvl3_2", + "type": "dict", + "label": "Nested dictionary (level 3) (2)", + "children": [ + { + "type": "text", + "key": "_nothing", + "label": "Exmaple input" + }, { + "type": "text", + "key": "_nothing2", + "label": "Exmaple input 2" + } + ] + } + ] + } + ] + }, { + "key": "form_examples", + "type": "dict", + "label": "Form examples", + "children": [ + { + "key": "inputs_without_form_example", + "type": "dict", + "label": "Inputs without form", + "children": [ + { + "type": "text", + "key": "_nothing_1", + "label": "Example label" + }, { + "type": "text", + "key": "_nothing_2", + "label": "Example label ####" + }, { + "type": "text", + "key": "_nothing_3", + "label": "Example label ########" + } + ] + }, { + "key": "inputs_with_form_example", + "type": "dict", + "label": "Inputs with form", + "children": [ + { + "type": "dict-form", + "children": [ + { + "type": "text", + "key": "_nothing_1", + "label": "Example label" + }, { + "type": "text", + "key": "_nothing_2", + "label": "Example label ####" + }, { + "type": "text", + "key": "_nothing_3", + "label": "Example label ########" + } + ] + } + ] + } + ] + } + ] + } + ] +} From 3cf1c91e2b2f4c94e9df67a99885bccfd9a44fca Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 15 Sep 2020 12:46:10 +0200 Subject: [PATCH 611/813] implemented label widget and splitter widget --- pype/tools/settings/settings/style/style.css | 4 +++ .../settings/settings/widgets/item_types.py | 34 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/pype/tools/settings/settings/style/style.css b/pype/tools/settings/settings/style/style.css index ab88a2c36f..38f69fef50 100644 --- a/pype/tools/settings/settings/style/style.css +++ b/pype/tools/settings/settings/style/style.css @@ -152,6 +152,10 @@ QPushButton[btn-type="expand-toggle"] { background: #141a1f; } +#SplitterItem { + background-color: #1d272f; +} + QTabWidget::pane { border-top-style: none; } diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 5f995b40b7..bd9341973b 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -2668,6 +2668,37 @@ class DictFormWidget(QtWidgets.QWidget, SettingObject): return values, self.is_group +class LabelWidget(QtWidgets.QWidget): + is_input_type = False + + def __init__(self, configuration, parent=None): + super(LabelWidget, self).__init__(parent) + self.setObjectName("LabelWidget") + + label = configuration["label"] + + layout = QtWidgets.QHBoxLayout(self) + layout.setContentsMargins(5, 5, 5, 5) + label_widget = QtWidgets.QLabel(label, self) + layout.addWidget(label_widget) + + +class SplitterWidget(QtWidgets.QWidget): + is_input_type = False + _height = 2 + + def __init__(self, configuration, parent=None): + super(SplitterWidget, self).__init__(parent) + + layout = QtWidgets.QHBoxLayout(self) + layout.setContentsMargins(5, 5, 5, 5) + splitter_item = QtWidgets.QWidget(self) + splitter_item.setObjectName("SplitterItem") + splitter_item.setMinimumHeight(self._height) + splitter_item.setMaximumHeight(self._height) + layout.addWidget(splitter_item) + + TypeToKlass.types["boolean"] = BooleanWidget TypeToKlass.types["number"] = NumberWidget TypeToKlass.types["text"] = TextWidget @@ -2679,3 +2710,6 @@ TypeToKlass.types["dict"] = DictWidget TypeToKlass.types["dict-invisible"] = DictInvisible TypeToKlass.types["path-widget"] = PathWidget TypeToKlass.types["dict-form"] = DictFormWidget + +TypeToKlass.types["label"] = LabelWidget +TypeToKlass.types["splitter"] = SplitterWidget From 6329836c277d6908a28d5850d7fb121c163b3ed3 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 15 Sep 2020 12:46:23 +0200 Subject: [PATCH 612/813] lib fixed is file keys --- pype/tools/settings/settings/widgets/lib.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pype/tools/settings/settings/widgets/lib.py b/pype/tools/settings/settings/widgets/lib.py index 0c3f01cef1..e225d65417 100644 --- a/pype/tools/settings/settings/widgets/lib.py +++ b/pype/tools/settings/settings/widgets/lib.py @@ -123,6 +123,11 @@ class SchemaDuplicatedKeys(Exception): def file_keys_from_schema(schema_data): output = [] + item_type = schema_data["type"] + klass = TypeToKlass.types[item_type] + if not klass.is_input_type: + return output + keys = [] key = schema_data.get("key") if key: @@ -143,6 +148,11 @@ def file_keys_from_schema(schema_data): def validate_all_has_ending_file(schema_data, is_top=True): + item_type = schema_data["type"] + klass = TypeToKlass.types[item_type] + if not klass.is_input_type: + return None + if schema_data.get("is_file"): return None From c4db7bdf620a9fc2c89b890cacfbbeae12518e4e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 15 Sep 2020 12:56:41 +0200 Subject: [PATCH 613/813] mod dict item return valid value if not set --- pype/tools/settings/settings/widgets/item_types.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index bd9341973b..2cc25df270 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -1303,9 +1303,11 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): return self._parent.input_fields.index(self) def config_value(self): - key = self.key_input.text() - value = self.value_input.item_value() - return {key: value} + if self._is_enabled(): + key = self.key_input.text() + value = self.value_input.item_value() + return {key: value} + return {} def mouseReleaseEvent(self, event): return QtWidgets.QWidget.mouseReleaseEvent(self, event) From 50a44e9cbc83817ebb869d562925580957bd3d13 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 15 Sep 2020 13:10:56 +0200 Subject: [PATCH 614/813] modifiable dict has one more addiotional method for getting all values event invalid for roots widget --- .../tools/settings/settings/widgets/item_types.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 2cc25df270..17f8514fb9 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -1302,11 +1302,14 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): def row(self): return self._parent.input_fields.index(self) + def item_value(self): + key = self.key_input.text() + value = self.value_input.item_value() + return {key: value} + def config_value(self): if self._is_enabled(): - key = self.key_input.text() - value = self.value_input.item_value() - return {key: value} + return self.item_value() return {} def mouseReleaseEvent(self, event): @@ -1468,6 +1471,12 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): self._state = state + def all_item_values(self): + output = {} + for item in self.input_fields: + output.update(item.item_value()) + return output + def item_value(self): output = {} for item in self.input_fields: From d20665a845864791f29f957533d05faa51f0ee1f Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 15 Sep 2020 13:18:36 +0200 Subject: [PATCH 615/813] anatomy type fixes --- pype/tools/settings/settings/widgets/anatomy_types.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pype/tools/settings/settings/widgets/anatomy_types.py b/pype/tools/settings/settings/widgets/anatomy_types.py index 785632b08d..6d7b3292ce 100644 --- a/pype/tools/settings/settings/widgets/anatomy_types.py +++ b/pype/tools/settings/settings/widgets/anatomy_types.py @@ -261,7 +261,7 @@ class RootsWidget(QtWidgets.QWidget, SettingObject): content_widget = QtWidgets.QWidget(body_widget) path_widget_data = { - "key": "roots", + "key": self.key, "multipath": False, "multiplatform": True } @@ -270,7 +270,7 @@ class RootsWidget(QtWidgets.QWidget, SettingObject): as_widget=True, parent_widget=content_widget ) multiroot_data = { - "key": "roots", + "key": self.key, "object_type": "path-widget", "expandable": False, "input_modifiers": { @@ -497,7 +497,7 @@ class RootsWidget(QtWidgets.QWidget, SettingObject): self.multiroot_widget.set_value(mutli_value) def _from_multi_to_single(self): - mutli_value = self.multiroot_widget.item_value() + mutli_value = self.multiroot_widget.all_item_values() for value in mutli_value.values(): single_value = value break From 72ae67af1911fdfa31c98babf05367f7843281e0 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 15 Sep 2020 13:20:39 +0200 Subject: [PATCH 616/813] fixed PathWidget --- pype/tools/settings/settings/widgets/item_types.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 17f8514fb9..7914c6f04f 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -2234,11 +2234,17 @@ class PathWidget(QtWidgets.QWidget, SettingObject): if self._as_widget: value = parent_values elif parent_values is not NOT_SET: - value = parent_values.get(self.key, NOT_SET) + if not self.multiplatform: + value = parent_values + else: + value = parent_values.get(self.key, NOT_SET) if value is NOT_SET: if self.develop_mode: - value = {self.key: self.default_input_value} + if self._as_widget or not self.multiplatform: + value = {self.key: self.default_input_value} + else: + value = self.default_input_value self.defaults_not_set = True if value is NOT_SET: raise NotImplementedError(( From acc2e3db0b34e0f40c812ce8a416833e161aae50 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 15 Sep 2020 13:27:02 +0200 Subject: [PATCH 617/813] added refresh button to projects too --- pype/tools/settings/settings/widgets/base.py | 24 ++++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/pype/tools/settings/settings/widgets/base.py b/pype/tools/settings/settings/widgets/base.py index 437a74b8c1..d9be84638e 100644 --- a/pype/tools/settings/settings/widgets/base.py +++ b/pype/tools/settings/settings/widgets/base.py @@ -111,6 +111,8 @@ class SystemWidget(QtWidgets.QWidget): input_field.hierarchical_style_update() def reset(self): + reset_default_settings() + if self.content_layout.count() != 0: for widget in self.input_fields: self.content_layout.removeWidget(widget) @@ -211,9 +213,7 @@ class SystemWidget(QtWidgets.QWidget): with open(output_path, "w") as file_stream: json.dump(new_values, file_stream, indent=4) - reset_default_settings() - - self._update_values() + self.reset() def _update_values(self): self.ignore_value_changes = True @@ -401,9 +401,16 @@ class ProjectWidget(QtWidgets.QWidget): if self.develop_mode: save_as_default_btn = QtWidgets.QPushButton("Save as Default") - footer_layout.addWidget(save_as_default_btn, 0) save_as_default_btn.clicked.connect(self._save_as_defaults) + refresh_icon = qtawesome.icon("fa.refresh", color="white") + refresh_button = QtWidgets.QPushButton() + refresh_button.setIcon(refresh_icon) + refresh_button.clicked.connect(self._on_refresh) + + footer_layout.addWidget(save_as_default_btn, 0) + footer_layout.addWidget(refresh_button, 0) + save_btn = QtWidgets.QPushButton("Save") spacer_widget = QtWidgets.QWidget() footer_layout.addWidget(spacer_widget, 1) @@ -453,6 +460,8 @@ class ProjectWidget(QtWidgets.QWidget): input_field.hierarchical_style_update() def reset(self): + reset_default_settings() + self.schema = lib.gui_schema("projects_schema", "0_project_gui_schema") self.keys = self.schema.get("keys", []) self.add_children_gui(self.schema) @@ -530,9 +539,7 @@ class ProjectWidget(QtWidgets.QWidget): with open(output_path, "w") as file_stream: json.dump(new_values, file_stream, indent=4) - reset_default_settings() - - self._update_values() + self.reset() def _save(self): has_invalid = False @@ -564,6 +571,9 @@ class ProjectWidget(QtWidgets.QWidget): else: self._save_overrides() + def _on_refresh(self): + self.reset() + def _save_overrides(self): data = {} for item in self.input_fields: From 9b3862eb74d1e1ab71e7ba2f2d1624e3879995ba Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 15 Sep 2020 14:38:21 +0200 Subject: [PATCH 618/813] refresh and save as defaults works as expected --- pype/tools/settings/settings/widgets/base.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/pype/tools/settings/settings/widgets/base.py b/pype/tools/settings/settings/widgets/base.py index d9be84638e..4769cb4e63 100644 --- a/pype/tools/settings/settings/widgets/base.py +++ b/pype/tools/settings/settings/widgets/base.py @@ -213,7 +213,10 @@ class SystemWidget(QtWidgets.QWidget): with open(output_path, "w") as file_stream: json.dump(new_values, file_stream, indent=4) - self.reset() + reset_default_settings() + + self._update_values() + self.hierarchical_style_update() def _update_values(self): self.ignore_value_changes = True @@ -460,7 +463,11 @@ class ProjectWidget(QtWidgets.QWidget): input_field.hierarchical_style_update() 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.schema = lib.gui_schema("projects_schema", "0_project_gui_schema") self.keys = self.schema.get("keys", []) @@ -539,7 +546,10 @@ class ProjectWidget(QtWidgets.QWidget): with open(output_path, "w") as file_stream: json.dump(new_values, file_stream, indent=4) - self.reset() + reset_default_settings() + + self._update_values() + self.hierarchical_style_update() def _save(self): has_invalid = False From 998c5383eea87b0eac203bd34504006e5b3508b3 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 15 Sep 2020 14:39:39 +0200 Subject: [PATCH 619/813] Refactor - added a couple of docstrings --- pype/modules/ftrack/lib/avalon_sync.py | 98 ++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/pype/modules/ftrack/lib/avalon_sync.py b/pype/modules/ftrack/lib/avalon_sync.py index 7dd4056524..e2dfdde0aa 100644 --- a/pype/modules/ftrack/lib/avalon_sync.py +++ b/pype/modules/ftrack/lib/avalon_sync.py @@ -104,6 +104,14 @@ def get_pype_attr(session, split_hierarchical=True): def from_dict_to_set(data): + """ + Converts 'data' into $set part of MongoDB update command. + Args: + data: (dictionary) - up-to-date data from Ftrack + + Returns: + (dictionary) - { "$set" : "{..}"} + """ result = {"$set": {}} dict_queue = queue.Queue() dict_queue.put((None, data)) @@ -124,6 +132,8 @@ def from_dict_to_set(data): def get_avalon_project_template(project_name): """Get avalon template + Args: + project_name: (string) Returns: dictionary with templates """ @@ -136,6 +146,16 @@ def get_avalon_project_template(project_name): def get_project_apps(in_app_list): + """ + Returns metadata information about apps in 'in_app_list' enhanced + from toml files. + Args: + in_app_list: (list) - names of applications + + Returns: + tuple (list, dictionary) - list of dictionaries about apps + dictionary of warnings + """ apps = [] # TODO report missing_toml_msg = "Missing config file for application" @@ -440,6 +460,13 @@ class SyncEntitiesFactory: @property def avalon_ents_by_id(self): + """ + Returns dictionary of avalon tracked entities (assets stored in + MongoDB) accessible by its '_id' + (mongo intenal ID - example ObjectId("5f48de5830a9467b34b69798")) + Returns: + (dictionary) - {"(_id)": whole entity asset} + """ if self._avalon_ents_by_id is None: self._avalon_ents_by_id = {} for entity in self.avalon_entities: @@ -449,6 +476,14 @@ class SyncEntitiesFactory: @property def avalon_ents_by_ftrack_id(self): + """ + Returns dictionary of Mongo ids of avalon tracked entities + (assets stored in MongoDB) accessible by its 'ftrackId' + (id from ftrack) + (example '431ee3f2-e91a-11ea-bfa4-92591a5b5e3e') + Returns: + (dictionary) - {"(ftrackId)": "_id"} + """ if self._avalon_ents_by_ftrack_id is None: self._avalon_ents_by_ftrack_id = {} for entity in self.avalon_entities: @@ -461,6 +496,13 @@ class SyncEntitiesFactory: @property def avalon_ents_by_name(self): + """ + Returns dictionary of Mongo ids of avalon tracked entities + (assets stored in MongoDB) accessible by its 'name' + (example 'Hero') + Returns: + (dictionary) - {"(name)": "_id"} + """ if self._avalon_ents_by_name is None: self._avalon_ents_by_name = {} for entity in self.avalon_entities: @@ -470,6 +512,15 @@ class SyncEntitiesFactory: @property def avalon_ents_by_parent_id(self): + """ + Returns dictionary of avalon tracked entities + (assets stored in MongoDB) accessible by its 'visualParent' + (example ObjectId("5f48de5830a9467b34b69798")) + + Fills 'self._avalon_archived_ents' for performance + Returns: + (dictionary) - {"(_id)": whole entity} + """ if self._avalon_ents_by_parent_id is None: self._avalon_ents_by_parent_id = collections.defaultdict(list) for entity in self.avalon_entities: @@ -482,6 +533,14 @@ class SyncEntitiesFactory: @property def avalon_archived_ents(self): + """ + Returns list of archived assets from DB + (their "type" == 'archived_asset') + + Fills 'self._avalon_archived_ents' for performance + Returns: + (list) of assets + """ if self._avalon_archived_ents is None: self._avalon_archived_ents = [ ent for ent in self.dbcon.find({"type": "archived_asset"}) @@ -490,6 +549,14 @@ class SyncEntitiesFactory: @property def avalon_archived_by_name(self): + """ + Returns list of archived assets from DB + (their "type" == 'archived_asset') + + Fills 'self._avalon_archived_by_name' for performance + Returns: + (dictionary of lists) of assets accessible by asset name + """ if self._avalon_archived_by_name is None: self._avalon_archived_by_name = collections.defaultdict(list) for ent in self.avalon_archived_ents: @@ -498,6 +565,14 @@ class SyncEntitiesFactory: @property def avalon_archived_by_id(self): + """ + Returns dictionary of archived assets from DB + (their "type" == 'archived_asset') + + Fills 'self._avalon_archived_by_id' for performance + Returns: + (dictionary) of assets accessible by asset mongo _id + """ if self._avalon_archived_by_id is None: self._avalon_archived_by_id = { str(ent["_id"]): ent for ent in self.avalon_archived_ents @@ -506,6 +581,15 @@ class SyncEntitiesFactory: @property def avalon_archived_by_parent_id(self): + """ + Returns dictionary of archived assets from DB per their's parent + (their "type" == 'archived_asset') + + Fills 'self._avalon_archived_by_parent_id' for performance + Returns: + (dictionary of lists) of assets accessible by asset parent + mongo _id + """ if self._avalon_archived_by_parent_id is None: self._avalon_archived_by_parent_id = collections.defaultdict(list) for entity in self.avalon_archived_ents: @@ -518,6 +602,14 @@ class SyncEntitiesFactory: @property def subsets_by_parent_id(self): + """ + Returns dictionary of subsets from Mongo ("type": "subset") + grouped by their parent. + + Fills 'self._subsets_by_parent_id' for performance + Returns: + (dictionary of lists) + """ if self._subsets_by_parent_id is None: self._subsets_by_parent_id = collections.defaultdict(list) for subset in self.dbcon.find({"type": "subset"}): @@ -539,6 +631,11 @@ class SyncEntitiesFactory: @property def all_ftrack_names(self): + """ + Returns lists of names of all entities in Ftrack + Returns: + (list) + """ return [ ent_dict["name"] for ent_dict in self.entities_dict.values() if ( ent_dict.get("name") @@ -1937,6 +2034,7 @@ class SyncEntitiesFactory: if not mongo_changes_bulk: # TODO LOG return + log.debug("mongo_changes_bulk:: {}".format(mongo_changes_bulk)) self.dbcon.bulk_write(mongo_changes_bulk) def reload_parents(self, hierarchy_changing_ids): From 452e5988f1350fe0cfde66a608dfb37335266e27 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 15 Sep 2020 14:49:47 +0200 Subject: [PATCH 620/813] fixed PathWidget for other udpates --- pype/tools/settings/settings/widgets/item_types.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 7914c6f04f..51ae0fc985 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -2276,7 +2276,10 @@ class PathWidget(QtWidgets.QWidget, SettingObject): if self._as_widget: value = parent_values elif parent_values is not NOT_SET: - value = parent_values.get(self.key, NOT_SET) + if not self.multiplatform: + value = parent_values + else: + value = parent_values.get(self.key, NOT_SET) self.studio_value = value if value is not NOT_SET: @@ -2297,11 +2300,15 @@ class PathWidget(QtWidgets.QWidget, SettingObject): self._is_modified = False self._state = None self._child_state = None + override_values = NOT_SET if self._as_widget: override_values = parent_values elif parent_values is not NOT_SET: - override_values = parent_values.get(self.key, override_values) + if not self.multiplatform: + override_values = parent_values + else: + override_values = parent_values.get(self.key, NOT_SET) self._is_overriden = override_values is not NOT_SET self._was_overriden = bool(self._is_overriden) From a135517dcfefc213473925510a60c5d71f60230f Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 15 Sep 2020 15:03:27 +0200 Subject: [PATCH 621/813] Hound --- pype/modules/ftrack/lib/avalon_sync.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pype/modules/ftrack/lib/avalon_sync.py b/pype/modules/ftrack/lib/avalon_sync.py index e2dfdde0aa..e8d5ef0093 100644 --- a/pype/modules/ftrack/lib/avalon_sync.py +++ b/pype/modules/ftrack/lib/avalon_sync.py @@ -259,6 +259,7 @@ def get_hierarchical_attributes(session, entity, attr_names, attr_defaults={}): return hier_values + def get_task_short_name(task_type): """ Returns short name (code) for 'task_type'. Short name stored in @@ -1138,10 +1139,9 @@ class SyncEntitiesFactory: self.report_items["warning"][msg] = items tasks = [] for tt in task_types: - tasks.append({ - "name": tt["name"], - "short_name": get_task_short_name(tt["name"]) - }) + tasks.append({"name": tt["name"], + "short_name": get_task_short_name(tt["name"]) + }) self.entities_dict[id]["final_entity"]["config"] = { "tasks": tasks, "apps": proj_apps From edce6a1ea9a0fe376e62b26d6f8b0070bcc715c5 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 15 Sep 2020 15:19:16 +0200 Subject: [PATCH 622/813] added possibility to hide studio overrides --- pype/tools/settings/settings/widgets/base.py | 62 ++++++++++++++++++-- 1 file changed, 57 insertions(+), 5 deletions(-) diff --git a/pype/tools/settings/settings/widgets/base.py b/pype/tools/settings/settings/widgets/base.py index 4769cb4e63..dbcc380daf 100644 --- a/pype/tools/settings/settings/widgets/base.py +++ b/pype/tools/settings/settings/widgets/base.py @@ -41,6 +41,7 @@ class SystemWidget(QtWidgets.QWidget): super(SystemWidget, self).__init__(parent) self.develop_mode = develop_mode + self._hide_studio_overrides = False self._ignore_value_changes = False self.input_fields = [] @@ -73,8 +74,25 @@ class SystemWidget(QtWidgets.QWidget): refresh_button.setIcon(refresh_icon) refresh_button.clicked.connect(self._on_refresh) + hide_studio_overrides = QtWidgets.QCheckBox() + hide_studio_overrides.setChecked(self._hide_studio_overrides) + hide_studio_overrides.stateChanged.connect( + self._on_hide_studio_overrides + ) + + hide_studio_overrides_widget = QtWidgets.QWidget() + hide_studio_overrides_layout = QtWidgets.QHBoxLayout( + hide_studio_overrides_widget + ) + _label_widget = QtWidgets.QLabel( + "Hide studio overrides", hide_studio_overrides_widget + ) + hide_studio_overrides_layout.addWidget(_label_widget) + hide_studio_overrides_layout.addWidget(hide_studio_overrides) + footer_layout.addWidget(save_as_default_btn, 0) footer_layout.addWidget(refresh_button, 0) + footer_layout.addWidget(hide_studio_overrides_widget, 0) save_btn = QtWidgets.QPushButton("Save") spacer_widget = QtWidgets.QWidget() @@ -171,6 +189,11 @@ class SystemWidget(QtWidgets.QWidget): def _on_refresh(self): self.reset() + def _on_hide_studio_overrides(self, state): + self._hide_studio_overrides = (state == QtCore.Qt.Checked) + self._update_values() + self.hierarchical_style_update() + def _save_as_defaults(self): output = {} for item in self.input_fields: @@ -227,7 +250,10 @@ class SystemWidget(QtWidgets.QWidget): for input_field in self.input_fields: input_field.update_default_values(default_values) - system_values = {"system": studio_system_settings()} + if self._hide_studio_overrides: + system_values = lib.NOT_SET + else: + system_values = {"system": studio_system_settings()} for input_field in self.input_fields: input_field.update_studio_values(system_values) @@ -377,6 +403,7 @@ class ProjectWidget(QtWidgets.QWidget): super(ProjectWidget, self).__init__(parent) self.develop_mode = develop_mode + self._hide_studio_overrides = False self.is_overidable = False self._ignore_value_changes = False @@ -411,8 +438,25 @@ class ProjectWidget(QtWidgets.QWidget): refresh_button.setIcon(refresh_icon) refresh_button.clicked.connect(self._on_refresh) + hide_studio_overrides = QtWidgets.QCheckBox() + hide_studio_overrides.setChecked(self._hide_studio_overrides) + hide_studio_overrides.stateChanged.connect( + self._on_hide_studio_overrides + ) + + hide_studio_overrides_widget = QtWidgets.QWidget() + hide_studio_overrides_layout = QtWidgets.QHBoxLayout( + hide_studio_overrides_widget + ) + _label_widget = QtWidgets.QLabel( + "Hide studio overrides", hide_studio_overrides_widget + ) + hide_studio_overrides_layout.addWidget(_label_widget) + hide_studio_overrides_layout.addWidget(hide_studio_overrides) + footer_layout.addWidget(save_as_default_btn, 0) footer_layout.addWidget(refresh_button, 0) + footer_layout.addWidget(hide_studio_overrides_widget, 0) save_btn = QtWidgets.QPushButton("Save") spacer_widget = QtWidgets.QWidget() @@ -584,6 +628,11 @@ class ProjectWidget(QtWidgets.QWidget): def _on_refresh(self): self.reset() + def _on_hide_studio_overrides(self, state): + self._hide_studio_overrides = (state == QtCore.Qt.Checked) + self._update_values() + self.hierarchical_style_update() + def _save_overrides(self): data = {} for item in self.input_fields: @@ -673,10 +722,13 @@ class ProjectWidget(QtWidgets.QWidget): for input_field in self.input_fields: input_field.update_default_values(default_values) - studio_values = {"project": { - PROJECT_SETTINGS_KEY: studio_project_settings(), - PROJECT_ANATOMY_KEY: studio_project_anatomy() - }} + if self._hide_studio_overrides: + studio_values = lib.NOT_SET + else: + studio_values = {"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 4c54afea72e2f775e781052fbb10345c35aa6090 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 15 Sep 2020 15:19:38 +0200 Subject: [PATCH 623/813] main check for develop arg changed --- pype/tools/settings/__main__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/tools/settings/__main__.py b/pype/tools/settings/__main__.py index 044c2ef495..55a38b3604 100644 --- a/pype/tools/settings/__main__.py +++ b/pype/tools/settings/__main__.py @@ -11,7 +11,7 @@ if __name__ == "__main__": app.setStyleSheet(stylesheet) app.setWindowIcon(QtGui.QIcon(settings.style.app_icon_path())) - develop = "-dev" in sys.argv + develop = "-d" in sys.argv or "--develop" in sys.argv widget = settings.MainWidget(develop) widget.show() From b1a181c6053be65605e1ca370e2679e51371539a Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Tue, 15 Sep 2020 15:36:25 +0200 Subject: [PATCH 624/813] Do not remove frames if outside of allowed frame ragne --- pype/hosts/harmony/__init__.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/pype/hosts/harmony/__init__.py b/pype/hosts/harmony/__init__.py index d4b7d91fdb..7310e91e9b 100644 --- a/pype/hosts/harmony/__init__.py +++ b/pype/hosts/harmony/__init__.py @@ -18,12 +18,7 @@ def set_scene_settings(settings): if (args[0]["frameStart"] && args[0]["frameEnd"]) { var duration = args[0]["frameEnd"] - args[0]["frameStart"] + 1 - if (frame.numberOf() > duration) - { - frame.remove( - duration, frame.numberOf() - duration - ); - } + if (frame.numberOf() < duration) { frame.insert( From 64e84474c99f8b090146cea0eac6f88f66786480 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Tue, 15 Sep 2020 17:49:52 +0200 Subject: [PATCH 625/813] psd batch publisher was trying to always publish version 1 --- pype/plugins/global/publish/validate_version.py | 2 +- .../standalonepublisher/publish/collect_context.py | 2 +- .../publish/collect_psd_instances.py | 14 ++------------ 3 files changed, 4 insertions(+), 14 deletions(-) diff --git a/pype/plugins/global/publish/validate_version.py b/pype/plugins/global/publish/validate_version.py index 9c7ce72307..6701041541 100644 --- a/pype/plugins/global/publish/validate_version.py +++ b/pype/plugins/global/publish/validate_version.py @@ -10,7 +10,7 @@ class ValidateVersion(pyblish.api.InstancePlugin): order = pyblish.api.ValidatorOrder label = "Validate Version" - hosts = ["nuke", "maya", "blender"] + hosts = ["nuke", "maya", "blender", "standalonepublisher"] def process(self, instance): version = instance.data.get("version") diff --git a/pype/plugins/standalonepublisher/publish/collect_context.py b/pype/plugins/standalonepublisher/publish/collect_context.py index a5479fdf13..9dbeec93fb 100644 --- a/pype/plugins/standalonepublisher/publish/collect_context.py +++ b/pype/plugins/standalonepublisher/publish/collect_context.py @@ -123,7 +123,7 @@ class CollectContextDataSAPublish(pyblish.api.ContextPlugin): "label": subset, "name": subset, "family": in_data["family"], - "version": in_data.get("version", 1), + # "version": in_data.get("version", 1), "frameStart": in_data.get("representations", [None])[0].get( "frameStart", None ), diff --git a/pype/plugins/standalonepublisher/publish/collect_psd_instances.py b/pype/plugins/standalonepublisher/publish/collect_psd_instances.py index 9c8e2eae83..b5db437473 100644 --- a/pype/plugins/standalonepublisher/publish/collect_psd_instances.py +++ b/pype/plugins/standalonepublisher/publish/collect_psd_instances.py @@ -9,7 +9,7 @@ class CollectPsdInstances(pyblish.api.InstancePlugin): """ label = "Collect Psd Instances" - order = pyblish.api.CollectorOrder + 0.492 + order = pyblish.api.CollectorOrder + 0.489 hosts = ["standalonepublisher"] families = ["background_batch"] @@ -34,8 +34,6 @@ class CollectPsdInstances(pyblish.api.InstancePlugin): context = instance.context asset_data = instance.data["assetEntity"] asset_name = instance.data["asset"] - anatomy_data = instance.data["anatomyData"] - for subset_name, subset_data in self.subsets.items(): instance_name = f"{asset_name}_{subset_name}" task = subset_data.get("task", "background") @@ -55,16 +53,8 @@ class CollectPsdInstances(pyblish.api.InstancePlugin): new_instance.data["label"] = f"{instance_name}" new_instance.data["subset"] = subset_name + new_instance.data["task"] = task - # fix anatomy data - anatomy_data_new = copy.deepcopy(anatomy_data) - # updating hierarchy data - anatomy_data_new.update({ - "asset": asset_data["name"], - "task": task, - "subset": subset_name - }) - new_instance.data["anatomyData"] = anatomy_data_new if subset_name in self.unchecked_by_default: new_instance.data["publish"] = False From 47ef4b64f297186c6d929a8f56ecfb93dd0f44e8 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Tue, 15 Sep 2020 17:59:53 +0200 Subject: [PATCH 626/813] bump version --- pype/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/version.py b/pype/version.py index 95a6d3a792..96fc614cb2 100644 --- a/pype/version.py +++ b/pype/version.py @@ -1 +1 @@ -__version__ = "2.12.0" +__version__ = "2.12.1" From 35a506b5f4e01f3e7dc959018489344679170bf1 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 15 Sep 2020 18:43:51 +0200 Subject: [PATCH 627/813] fixed some alignments --- pype/tools/settings/settings/widgets/item_types.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 51ae0fc985..b79e6f7756 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -608,15 +608,19 @@ class TextWidget(QtWidgets.QWidget, InputObject): self.setFocusProxy(self.text_input) + layout_kwargs = {} + if self.multiline: + layout_kwargs["alignment"] = QtCore.Qt.AlignTop + if not self._as_widget: self.key = input_data["key"] if not label_widget: label = input_data["label"] label_widget = QtWidgets.QLabel(label) - layout.addWidget(label_widget, 0) + layout.addWidget(label_widget, 0, **layout_kwargs) self.label_widget = label_widget - layout.addWidget(self.text_input, 1) + layout.addWidget(self.text_input, 1, **layout_kwargs) self.text_input.textChanged.connect(self._on_value_change) @@ -823,9 +827,9 @@ class RawJsonWidget(QtWidgets.QWidget, InputObject): if not label_widget: label = input_data["label"] label_widget = QtWidgets.QLabel(label) - layout.addWidget(label_widget, 0) + layout.addWidget(label_widget, 0, alignment=QtCore.Qt.AlignTop) self.label_widget = label_widget - layout.addWidget(self.text_input, 1) + layout.addWidget(self.text_input, 1, alignment=QtCore.Qt.AlignTop) self.text_input.textChanged.connect(self._on_value_change) From c6b7346a9127712a142d72ae6e888fd4fe6e0224 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 16 Sep 2020 11:58:20 +0200 Subject: [PATCH 628/813] list widget has move up/down btns --- .../settings/settings/widgets/item_types.py | 136 ++++++++++++++++-- 1 file changed, 123 insertions(+), 13 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index b79e6f7756..8257f79fc2 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -9,6 +9,7 @@ from .widgets import ( PathInput ) from .lib import NOT_SET, METADATA_KEY, TypeToKlass, CHILD_OFFSET +from avalon.vendor import qtawesome class SettingObject(AbstractSettingObject): @@ -898,23 +899,46 @@ class ListItem(QtWidgets.QWidget, SettingObject): layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(3) - self.add_btn = QtWidgets.QPushButton("+") - self.remove_btn = QtWidgets.QPushButton("-") + char_plus = qtawesome.charmap("fa.plus") + char_minus = qtawesome.charmap("fa.minus") + char_up = qtawesome.charmap("fa.angle-up") + char_down = qtawesome.charmap("fa.angle-down") + + self.add_btn = QtWidgets.QPushButton(char_plus) + self.remove_btn = QtWidgets.QPushButton(char_minus) + self.up_btn = QtWidgets.QPushButton(char_up) + self.down_btn = QtWidgets.QPushButton(char_down) + + font_plus_minus = qtawesome.font("fa", 10) + font_up_down = qtawesome.font("fa", 13) + + self.add_btn.setFont(font_plus_minus) + self.remove_btn.setFont(font_plus_minus) + self.up_btn.setFont(font_up_down) + self.down_btn.setFont(font_up_down) self.add_btn.setFocusPolicy(QtCore.Qt.ClickFocus) self.remove_btn.setFocusPolicy(QtCore.Qt.ClickFocus) + self.up_btn.setFocusPolicy(QtCore.Qt.ClickFocus) + self.down_btn.setFocusPolicy(QtCore.Qt.ClickFocus) self.add_btn.setFixedSize(self._btn_size, self._btn_size) self.remove_btn.setFixedSize(self._btn_size, self._btn_size) + self.up_btn.setFixedSize(self._btn_size, self._btn_size) + self.down_btn.setFixedSize(self._btn_size, self._btn_size) self.add_btn.setProperty("btn-type", "tool-item") self.remove_btn.setProperty("btn-type", "tool-item") + self.up_btn.setProperty("btn-type", "tool-item") + self.down_btn.setProperty("btn-type", "tool-item") layout.addWidget(self.add_btn, 0) layout.addWidget(self.remove_btn, 0) - self.add_btn.clicked.connect(self.on_add_clicked) - self.remove_btn.clicked.connect(self.on_remove_clicked) + self.add_btn.clicked.connect(self._on_add_clicked) + self.remove_btn.clicked.connect(self._on_remove_clicked) + self.up_btn.clicked.connect(self._on_up_clicked) + self.down_btn.clicked.connect(self._on_down_clicked) ItemKlass = TypeToKlass.types[object_type] self.value_input = ItemKlass( @@ -925,28 +949,62 @@ class ListItem(QtWidgets.QWidget, SettingObject): ) layout.addWidget(self.value_input, 1) + layout.addWidget(self.up_btn, 0) + layout.addWidget(self.down_btn, 0) + self.value_input.value_changed.connect(self._on_value_change) def set_as_empty(self, is_empty=True): self.value_input.setEnabled(not is_empty) self.remove_btn.setEnabled(not is_empty) + self.order_changed() self._on_value_change() + def order_changed(self): + row = self.row() + parent_row_count = self.parent_rows_count() + if parent_row_count == 1: + self.up_btn.setEnabled(False) + self.down_btn.setEnabled(False) + + elif row == 0: + self.up_btn.setEnabled(False) + self.down_btn.setEnabled(True) + + elif row == parent_row_count - 1: + self.up_btn.setEnabled(True) + self.down_btn.setEnabled(False) + + else: + self.up_btn.setEnabled(True) + self.down_btn.setEnabled(True) + def _on_value_change(self, item=None): self.value_changed.emit(self) def row(self): return self._parent.input_fields.index(self) - def on_add_clicked(self): + def parent_rows_count(self): + return len(self._parent.input_fields) + + def _on_add_clicked(self): if self.value_input.isEnabled(): self._parent.add_row(row=self.row() + 1) else: self.set_as_empty(False) - def on_remove_clicked(self): + def _on_remove_clicked(self): self._parent.remove_row(self) + def _on_up_clicked(self): + row = self.row() + self._parent.swap_rows(row - 1, row) + + def _on_down_clicked(self): + row = self.row() + self._parent.swap_rows(row, row + 1) + def config_value(self): if self.value_input.isEnabled(): return self.value_input.item_value() @@ -1044,21 +1102,58 @@ class ListWidget(QtWidgets.QWidget, InputObject): if self.count() == 0: self.add_row(is_empty=True) + def swap_rows(self, row_1, row_2): + if row_1 == row_2: + return + + if row_1 > row_2: + row_1, row_2 = row_2, row_1 + + field_1 = self.input_fields[row_1] + field_2 = self.input_fields[row_2] + + self.input_fields[row_1] = field_2 + self.input_fields[row_2] = field_1 + + layout_index = self.inputs_layout.indexOf(field_1) + self.inputs_layout.insertWidget(layout_index + 1, field_1) + + field_1.order_changed() + field_2.order_changed() + def add_row(self, row=None, value=None, is_empty=False): # Create new item item_widget = ListItem( self.object_type, self.input_modifiers, self, self.inputs_widget ) + if row is None: + if self.input_fields: + self.input_fields[-1].order_changed() + self.inputs_layout.addWidget(item_widget) + self.input_fields.append(item_widget) + else: + previous_field = None + if row > 0: + previous_field = self.input_fields[row - 1] + + next_field = None + max_index = self.count() + if row < max_index: + next_field = self.input_fields[row] + + self.inputs_layout.insertWidget(row, item_widget) + self.input_fields.insert(row, item_widget) + if previous_field: + previous_field.order_changed() + + if next_field: + next_field.order_changed() + if is_empty: item_widget.set_as_empty() item_widget.value_changed.connect(self._on_value_change) - if row is None: - self.inputs_layout.addWidget(item_widget) - self.input_fields.append(item_widget) - else: - self.inputs_layout.insertWidget(row, item_widget) - self.input_fields.insert(row, item_widget) + item_widget.order_changed() previous_input = None for input_field in self.input_fields: @@ -1085,11 +1180,26 @@ class ListWidget(QtWidgets.QWidget, InputObject): def remove_row(self, item_widget): item_widget.value_changed.disconnect() + row = self.input_fields.index(item_widget) + previous_field = None + next_field = None + if row > 0: + previous_field = self.input_fields[row - 1] + + if row != len(self.input_fields) - 1: + next_field = self.input_fields[row + 1] + self.inputs_layout.removeWidget(item_widget) - self.input_fields.remove(item_widget) + self.input_fields.pop(row) item_widget.setParent(None) item_widget.deleteLater() + if previous_field: + previous_field.order_changed() + + if next_field: + next_field.order_changed() + if self.count() == 0: self.add_row(is_empty=True) From ff2dfec0757aa7974de9f0c49e850eeba35a3180 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 16 Sep 2020 13:37:14 +0200 Subject: [PATCH 629/813] plus minus signe are without awesome font --- pype/tools/settings/settings/widgets/item_types.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 8257f79fc2..ef1e0ad2e1 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -899,21 +899,15 @@ class ListItem(QtWidgets.QWidget, SettingObject): layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(3) - char_plus = qtawesome.charmap("fa.plus") - char_minus = qtawesome.charmap("fa.minus") char_up = qtawesome.charmap("fa.angle-up") char_down = qtawesome.charmap("fa.angle-down") - self.add_btn = QtWidgets.QPushButton(char_plus) - self.remove_btn = QtWidgets.QPushButton(char_minus) + self.add_btn = QtWidgets.QPushButton("+") + self.remove_btn = QtWidgets.QPushButton("-") self.up_btn = QtWidgets.QPushButton(char_up) self.down_btn = QtWidgets.QPushButton(char_down) - font_plus_minus = qtawesome.font("fa", 10) font_up_down = qtawesome.font("fa", 13) - - self.add_btn.setFont(font_plus_minus) - self.remove_btn.setFont(font_plus_minus) self.up_btn.setFont(font_up_down) self.down_btn.setFont(font_up_down) From 1f0e5b65dde7bfe366003eca8e983af043fa964f Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 16 Sep 2020 13:49:28 +0200 Subject: [PATCH 630/813] labels are at the top --- pype/tools/settings/settings/widgets/item_types.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index ef1e0ad2e1..06eb82ce74 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -1060,7 +1060,7 @@ class ListWidget(QtWidgets.QWidget, InputObject): if not label_widget: label_widget = QtWidgets.QLabel(input_data["label"], self) - layout.addWidget(label_widget) + layout.addWidget(label_widget, alignment=QtCore.Qt.AlignTop) self.label_widget = label_widget @@ -2258,7 +2258,7 @@ class PathWidget(QtWidgets.QWidget, SettingObject): label = input_data["label"] label_widget = QtWidgets.QLabel(label) label_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground) - layout.addWidget(label_widget, 0) + layout.addWidget(label_widget, 0, alignment=QtCore.Qt.AlignTop) self.label_widget = label_widget self.content_widget = QtWidgets.QWidget(self) From f943b7967949398bc75f5c81d90b12692ae9881f Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 16 Sep 2020 14:23:20 +0200 Subject: [PATCH 631/813] abstract methods are implemented directly in SettingObject --- .../settings/settings/widgets/item_types.py | 163 ++++++++++++- .../settings/settings/widgets/widgets.py | 230 ------------------ 2 files changed, 161 insertions(+), 232 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 06eb82ce74..1872d72def 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -3,7 +3,6 @@ import logging import collections from Qt import QtWidgets, QtCore, QtGui from .widgets import ( - AbstractSettingObject, ExpandingWidget, NumberSpinBox, PathInput @@ -12,11 +11,31 @@ from .lib import NOT_SET, METADATA_KEY, TypeToKlass, CHILD_OFFSET from avalon.vendor import qtawesome -class SettingObject(AbstractSettingObject): +class SettingObject: is_input_type = True default_input_value = NOT_SET allow_actions = True default_state = "" + abstract_attributes = ("_parent", ) + + def __getattr__(self, name): + if name in self.abstract_attributes: + raise NotImplementedError( + "Attribute `{}` is not implemented. {}".format(name, self) + ) + return super(SettingObject, self).__getattribute__(name) + + @classmethod + def style_state(cls, is_invalid, is_overriden, is_modified): + items = [] + if is_invalid: + items.append("invalid") + else: + if is_overriden: + items.append("overriden") + if is_modified: + items.append("modified") + return "-".join(items) or cls.default_state def set_default_attributes(self): # Default input attributes @@ -245,6 +264,146 @@ class SettingObject(AbstractSettingObject): if item: return item.mouseReleaseEvent(self, event) + def _discard_changes(self): + self.ignore_value_changes = True + self.discard_changes() + self.ignore_value_changes = False + + def discard_changes(self): + raise NotImplementedError( + "{} Method `discard_changes` not implemented!".format( + repr(self) + ) + ) + + def _set_studio_default(self): + self.ignore_value_changes = True + self.set_studio_default() + self.ignore_value_changes = False + + def set_studio_default(self): + raise NotImplementedError( + "{} Method `set_studio_default` not implemented!".format( + repr(self) + ) + ) + + def _reset_to_pype_default(self): + self.ignore_value_changes = True + self.reset_to_pype_default() + self.ignore_value_changes = False + + def reset_to_pype_default(self): + raise NotImplementedError( + "{} Method `reset_to_pype_default` not implemented!".format( + repr(self) + ) + ) + + def _remove_overrides(self): + self.ignore_value_changes = True + self.remove_overrides() + self.ignore_value_changes = False + + def remove_overrides(self): + raise NotImplementedError( + "{} Method `remove_overrides` not implemented!".format( + repr(self) + ) + ) + + def _set_as_overriden(self): + self.ignore_value_changes = True + self.set_as_overriden() + self.ignore_value_changes = False + + def set_as_overriden(self): + raise NotImplementedError( + "{} Method `set_as_overriden` not implemented!".format(repr(self)) + ) + + def hierarchical_style_update(self): + raise NotImplementedError( + "{} Method `hierarchical_style_update` not implemented!".format( + repr(self) + ) + ) + + def update_default_values(self, parent_values): + raise NotImplementedError( + "{} does not have implemented `update_default_values`".format(self) + ) + + def update_studio_values(self, parent_values): + raise NotImplementedError( + "{} does not have implemented `update_studio_values`".format(self) + ) + + def apply_overrides(self, parent_values): + raise NotImplementedError( + "{} does not have implemented `apply_overrides`".format(self) + ) + + @property + def ignore_value_changes(self): + """Most of attribute changes are ignored on value change when True.""" + raise NotImplementedError( + "{} does not have implemented `ignore_value_changes`".format(self) + ) + + @ignore_value_changes.setter + def ignore_value_changes(self, value): + """Setter for global parent item to apply changes for all inputs.""" + raise NotImplementedError(( + "{} does not have implemented setter method `ignore_value_changes`" + ).format(self)) + + @property + def child_has_studio_override(self): + """Any children item is modified.""" + raise NotImplementedError( + "{} does not have implemented `child_has_studio_override`".format( + self + ) + ) + + @property + def child_modified(self): + """Any children item is modified.""" + raise NotImplementedError( + "{} does not have implemented `child_modified`".format(self) + ) + + @property + def child_overriden(self): + """Any children item is overriden.""" + raise NotImplementedError( + "{} does not have implemented `child_overriden`".format(self) + ) + + @property + def child_invalid(self): + """Any children item does not have valid value.""" + raise NotImplementedError( + "{} does not have implemented `child_invalid`".format(self) + ) + + def get_invalid(self): + """Return invalid item types all down the hierarchy.""" + raise NotImplementedError( + "{} does not have implemented `get_invalid`".format(self) + ) + + def item_value(self): + """Value of an item without key.""" + raise NotImplementedError( + "Method `item_value` not implemented!" + ) + + def studio_value(self): + """Output for saving changes or overrides.""" + return {self.key: self.item_value()} + class InputObject(SettingObject): def update_default_values(self, parent_values): diff --git a/pype/tools/settings/settings/widgets/widgets.py b/pype/tools/settings/settings/widgets/widgets.py index 96f8d2ec17..400b9371fd 100644 --- a/pype/tools/settings/settings/widgets/widgets.py +++ b/pype/tools/settings/settings/widgets/widgets.py @@ -226,233 +226,3 @@ class UnsavedChangesDialog(QtWidgets.QDialog): def on_discard_pressed(self): self.done(2) - - -class AbstractSettingObject: - abstract_attributes = ("_parent", ) - - def __getattr__(self, name): - if name in self.abstract_attributes: - raise NotImplementedError( - "Attribute `{}` is not implemented. {}".format(name, self) - ) - return super(AbstractSettingObject, self).__getattribute__(name) - - def update_default_values(self, parent_values): - raise NotImplementedError( - "{} does not have implemented `update_default_values`".format(self) - ) - - def update_studio_values(self, parent_values): - raise NotImplementedError( - "{} does not have implemented `update_studio_values`".format(self) - ) - - def apply_overrides(self, parent_values): - raise NotImplementedError( - "{} does not have implemented `apply_overrides`".format(self) - ) - - @property - def is_modified(self): - """Has object any changes that require saving.""" - raise NotImplementedError( - "{} does not have implemented `is_modified`".format(self) - ) - - @property - def is_overriden(self): - """Is object overriden so should be saved to overrides.""" - raise NotImplementedError( - "{} does not have implemented `is_overriden`".format(self) - ) - - @property - def any_parent_is_group(self): - raise NotImplementedError( - "{} does not have implemented `any_parent_is_group`".format(self) - ) - - @property - def was_overriden(self): - """Initial state after applying overrides.""" - raise NotImplementedError( - "{} does not have implemented `was_overriden`".format(self) - ) - - @property - def is_invalid(self): - """Value set in is not valid.""" - raise NotImplementedError( - "{} does not have implemented `is_invalid`".format(self) - ) - - @property - def is_group(self): - """Value set in is not valid.""" - raise NotImplementedError( - "{} does not have implemented `is_group`".format(self) - ) - - @property - def is_nullable(self): - raise NotImplementedError( - "{} does not have implemented `is_nullable`".format(self) - ) - - @property - def is_overidable(self): - """Should care about overrides.""" - raise NotImplementedError( - "{} does not have implemented `is_overidable`".format(self) - ) - - def any_parent_overriden(self): - """Any of parent object up to top hiearchy is overriden.""" - raise NotImplementedError( - "{} does not have implemented `any_parent_overriden`".format(self) - ) - - @property - def ignore_value_changes(self): - """Most of attribute changes are ignored on value change when True.""" - raise NotImplementedError( - "{} does not have implemented `ignore_value_changes`".format(self) - ) - - @ignore_value_changes.setter - def ignore_value_changes(self, value): - """Setter for global parent item to apply changes for all inputs.""" - raise NotImplementedError(( - "{} does not have implemented setter method `ignore_value_changes`" - ).format(self)) - - @property - def child_has_studio_override(self): - """Any children item is modified.""" - raise NotImplementedError( - "{} does not have implemented `child_has_studio_override`".format( - self - ) - ) - - @property - def child_modified(self): - """Any children item is modified.""" - raise NotImplementedError( - "{} does not have implemented `child_modified`".format(self) - ) - - @property - def child_overriden(self): - """Any children item is overriden.""" - raise NotImplementedError( - "{} does not have implemented `child_overriden`".format(self) - ) - - @property - def child_invalid(self): - """Any children item does not have valid value.""" - raise NotImplementedError( - "{} does not have implemented `child_invalid`".format(self) - ) - - def get_invalid(self): - """Returns invalid items all down the hierarchy.""" - raise NotImplementedError( - "{} does not have implemented `get_invalid`".format(self) - ) - - def item_value(self): - """Value of an item without key.""" - raise NotImplementedError( - "Method `item_value` not implemented!" - ) - - def studio_value(self): - """Output for saving changes or overrides.""" - return {self.key: self.item_value()} - - @classmethod - def style_state(cls, is_invalid, is_overriden, is_modified): - items = [] - if is_invalid: - items.append("invalid") - else: - if is_overriden: - items.append("overriden") - if is_modified: - items.append("modified") - return "-".join(items) or cls.default_state - - def add_children_gui(self, child_configuration, values): - raise NotImplementedError( - "{} Method `add_children_gui` is not implemented!.".format( - repr(self) - ) - ) - - def _discard_changes(self): - self.ignore_value_changes = True - self.discard_changes() - self.ignore_value_changes = False - - def discard_changes(self): - raise NotImplementedError( - "{} Method `discard_changes` not implemented!".format( - repr(self) - ) - ) - - def _set_studio_default(self): - self.ignore_value_changes = True - self.set_studio_default() - self.ignore_value_changes = False - - def set_studio_default(self): - raise NotImplementedError( - "{} Method `set_studio_default` not implemented!".format( - repr(self) - ) - ) - - def _reset_to_pype_default(self): - self.ignore_value_changes = True - self.reset_to_pype_default() - self.ignore_value_changes = False - - def reset_to_pype_default(self): - raise NotImplementedError( - "{} Method `reset_to_pype_default` not implemented!".format( - repr(self) - ) - ) - - def _remove_overrides(self): - self.ignore_value_changes = True - self.remove_overrides() - self.ignore_value_changes = False - - def remove_overrides(self): - raise NotImplementedError( - "{} Method `remove_overrides` not implemented!".format( - repr(self) - ) - ) - - def _set_as_overriden(self): - self.ignore_value_changes = True - self.set_as_overriden() - self.ignore_value_changes = False - - def set_as_overriden(self): - raise NotImplementedError( - "{} Method `set_as_overriden` not implemented!".format(repr(self)) - ) - - def hierarchical_style_update(self): - raise NotImplementedError( - "{} Method `hierarchical_style_update` not implemented!".format( - repr(self) - ) - ) From 1e2e278ede49f79f218b964e4dd694f87ca1c69a Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 16 Sep 2020 14:25:44 +0200 Subject: [PATCH 632/813] #241 - Implementation of pype-config/presets/plugins/standalonepublisher --- .../projects_schema/1_plugins_gui_schema.json | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/1_plugins_gui_schema.json b/pype/tools/settings/settings/gui_schemas/projects_schema/1_plugins_gui_schema.json index 6bb14463c1..2e64e6a40b 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/1_plugins_gui_schema.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/1_plugins_gui_schema.json @@ -614,6 +614,100 @@ ] } ] + }, + { + "type": "dict", + "collapsable": true, + "key": "standalonepublisher", + "label": "Standalone Publisher", + "children": [ + { + "type": "dict", + "collapsable": true, + "key": "publish", + "label": "Publish plugins", + "is_file": true, + "children": [ + { + "type": "dict", + "collapsable": true, + "key": "ExtractThumbnailSP", + "label": "ExtractThumbnailSP", + "is_group": true, + "children": [ + { + "type": "dict", + "collapsable": false, + "key": "ffmpeg_args", + "label": "ffmpeg_args", + "children": [ + { + "type": "list", + "object_type": "text", + "key": "input", + "label": "input" + }, + { + "type": "list", + "object_type": "text", + "key": "output", + "label": "output" + } + ] + } + ] + }, + { + "type": "dict", + "collapsable": true, + "key": "ExtractReviewSP", + "label": "ExtractReviewSP", + "is_group": true, + "children": [ + { + "type": "dict", + "collapsable": false, + "key": "outputs", + "label": "outputs", + "children": [ + { + "type": "dict", + "collapsable": false, + "key": "h264", + "label": "h264", + "children": [ + { + "type": "list", + "object_type": "text", + "key": "input", + "label": "input" + }, + { + "type": "list", + "object_type": "text", + "key": "output", + "label": "output" + }, + { + "type": "list", + "object_type": "text", + "key": "tags", + "label": "tags" + }, + { + "type": "text", + "key": "ext", + "label": "ext" + } + ] + } + ] + } + ] + } + ] + } + ] } ] } From 81df713212f3259a9b60fc76af1e179588892f46 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 16 Sep 2020 15:32:59 +0200 Subject: [PATCH 633/813] added few docstrings --- .../settings/settings/widgets/item_types.py | 117 ++++++++++++------ 1 file changed, 80 insertions(+), 37 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 1872d72def..19bfdabb9e 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -12,21 +12,19 @@ from avalon.vendor import qtawesome class SettingObject: + # `is_input_type` attribute says if has implemented item type methods is_input_type = True + # each input must have implemented default value for development + # when defaults are not filled yet default_input_value = NOT_SET + # will allow to show actions for the item type (disabled for proxies) allow_actions = True + # default state of item type default_state = "" - abstract_attributes = ("_parent", ) - - def __getattr__(self, name): - if name in self.abstract_attributes: - raise NotImplementedError( - "Attribute `{}` is not implemented. {}".format(name, self) - ) - return super(SettingObject, self).__getattribute__(name) @classmethod def style_state(cls, is_invalid, is_overriden, is_modified): + """Return stylesheet state by intered booleans.""" items = [] if is_invalid: items.append("invalid") @@ -37,7 +35,11 @@ class SettingObject: items.append("modified") return "-".join(items) or cls.default_state - def set_default_attributes(self): + def _set_default_attributes(self): + """Create and reset attributes required for all item types. + + They may not be used in the item but are required to be set. + """ # Default input attributes self._has_studio_override = False self._had_studio_override = False @@ -73,7 +75,12 @@ class SettingObject: self.defaults_not_set = False def initial_attributes(self, input_data, parent, as_widget): - self.set_default_attributes() + """Prepare attributes based on entered arguments. + + This method should be same for each item type. Few item types + may require to extend with specific attributes for their case. + """ + self._set_default_attributes() self._parent = parent self._as_widget = as_widget @@ -90,24 +97,70 @@ class SettingObject: @property def develop_mode(self): + """Tool is in develop mode or not. + + Returns: + bool + + """ return self._parent.develop_mode @property def log(self): + """Auto created logger for debugging.""" if self._log is None: self._log = logging.getLogger(self.__class__.__name__) return self._log - @property - def has_studio_override(self): - return self._has_studio_override or self._parent.has_studio_override - @property def had_studio_override(self): + """Item had studio overrides on refresh. + + Returns: + bool + + """ return self._had_studio_override + @property + def has_studio_override(self): + """Item has studio override at the moment. + + With combination of `had_studio_override` is possible to know if item + has changes (not just value change). + + Returns: + bool + + """ + return self._has_studio_override or self._parent.has_studio_override + + @property + def is_group(self): + """Item represents key that can be overriden. + + Attribute `is_group` can be set to True only once in item hierarchy. + + Returns: + bool + + """ + return self._is_group + @property def any_parent_is_group(self): + """Any parent of item is group. + + Attribute holding this information is set during creation and + stored to `_any_parent_is_group`. + + Why is this information useful: If any parent is group and + the parent is set as overriden, this item is overriden too. + + Returns: + bool + + """ if self._any_parent_is_group is None: return super(SettingObject, self).any_parent_is_group return self._any_parent_is_group @@ -130,7 +183,7 @@ class SettingObject: @property def was_overriden(self): - """Initial state after applying overrides.""" + """Item had set value of project overrides on project change.""" if self._as_widget: return self._parent.was_overriden return self._was_overriden @@ -140,13 +193,12 @@ class SettingObject: """Value set in is not valid.""" return self._is_invalid - @property - def is_group(self): - """Value set in is not valid.""" - return self._is_group - @property def is_nullable(self): + """Value of item can be set to None. + + NOT IMPLEMENTED! + """ return self._is_nullable @property @@ -155,7 +207,12 @@ class SettingObject: return self._parent.is_overidable def any_parent_overriden(self): - """Any of parent object up to top hiearchy is overriden.""" + """Any of parent objects up to top hiearchy item is overriden. + + Returns: + bool + + """ if self._parent._is_overriden: return True return self._parent.any_parent_overriden() @@ -344,20 +401,6 @@ class SettingObject: "{} does not have implemented `apply_overrides`".format(self) ) - @property - def ignore_value_changes(self): - """Most of attribute changes are ignored on value change when True.""" - raise NotImplementedError( - "{} does not have implemented `ignore_value_changes`".format(self) - ) - - @ignore_value_changes.setter - def ignore_value_changes(self, value): - """Setter for global parent item to apply changes for all inputs.""" - raise NotImplementedError(( - "{} does not have implemented setter method `ignore_value_changes`" - ).format(self)) - @property def child_has_studio_override(self): """Any children item is modified.""" @@ -1049,7 +1092,7 @@ class ListItem(QtWidgets.QWidget, SettingObject): def __init__(self, object_type, input_modifiers, config_parent, parent): super(ListItem, self).__init__(parent) - self.set_default_attributes() + self._set_default_attributes() self._parent = config_parent self._any_parent_is_group = True @@ -1430,7 +1473,7 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): def __init__(self, object_type, input_modifiers, config_parent, parent): super(ModifiableDictItem, self).__init__(parent) - self.set_default_attributes() + self._set_default_attributes() self._parent = config_parent self.is_key_duplicated = False From 1273e7c6ef9367302a77b50c2198b87b3bba84f1 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 16 Sep 2020 15:36:26 +0200 Subject: [PATCH 634/813] fixed typo --- 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 19bfdabb9e..e2d59c2e69 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -284,7 +284,7 @@ class SettingObject: and not self.any_parent_is_group and not self._had_studio_override ): - action = QtWidgets.QAction("Set sudio default") + action = QtWidgets.QAction("Set studio default") actions_mapping[action] = self._set_studio_default menu.addAction(action) From d6fbbc31e8452e12d17094418f25ac0efefde8c3 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 16 Sep 2020 16:03:11 +0200 Subject: [PATCH 635/813] feat(global): add `burnin` suffix only if more burnin profiles active --- 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 91ac877a1e88bce9acd72218f24156fa6e02d770 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Wed, 16 Sep 2020 16:05:48 +0200 Subject: [PATCH 636/813] Revert "388 Extract review a representation name with `*_burnin`" --- pype/plugins/global/publish/extract_burnin.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/pype/plugins/global/publish/extract_burnin.py b/pype/plugins/global/publish/extract_burnin.py index 6e8da1b054..4443cfe223 100644 --- a/pype/plugins/global/publish/extract_burnin.py +++ b/pype/plugins/global/publish/extract_burnin.py @@ -195,14 +195,11 @@ class ExtractBurnin(pype.api.Extractor): if "delete" in new_repre["tags"]: new_repre["tags"].remove("delete") - 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 + # 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 # Prepare paths and files for process. self.input_output_paths(new_repre, temp_data, filename_suffix) From 22132f6e4db34e3989621c8a30281d6c6dbdac66 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 16 Sep 2020 16:11:13 +0200 Subject: [PATCH 637/813] 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 9049554abf7970fa8cff8ca111096ffcb8fb4fa9 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 16 Sep 2020 16:42:43 +0200 Subject: [PATCH 638/813] #241 - Removed ExtractReviewSP element --- .../projects_schema/1_plugins_gui_schema.json | 45 +------------------ 1 file changed, 1 insertion(+), 44 deletions(-) diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/1_plugins_gui_schema.json b/pype/tools/settings/settings/gui_schemas/projects_schema/1_plugins_gui_schema.json index 2e64e6a40b..96aad3e5a9 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/1_plugins_gui_schema.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/1_plugins_gui_schema.json @@ -642,39 +642,7 @@ "label": "ffmpeg_args", "children": [ { - "type": "list", - "object_type": "text", - "key": "input", - "label": "input" - }, - { - "type": "list", - "object_type": "text", - "key": "output", - "label": "output" - } - ] - } - ] - }, - { - "type": "dict", - "collapsable": true, - "key": "ExtractReviewSP", - "label": "ExtractReviewSP", - "is_group": true, - "children": [ - { - "type": "dict", - "collapsable": false, - "key": "outputs", - "label": "outputs", - "children": [ - { - "type": "dict", - "collapsable": false, - "key": "h264", - "label": "h264", + "type": "dict-form", "children": [ { "type": "list", @@ -687,17 +655,6 @@ "object_type": "text", "key": "output", "label": "output" - }, - { - "type": "list", - "object_type": "text", - "key": "tags", - "label": "tags" - }, - { - "type": "text", - "key": "ext", - "label": "ext" } ] } From 923c00d646b91450ce25d8ff121c9dd2c61b549c Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 16 Sep 2020 17:20:22 +0200 Subject: [PATCH 639/813] #241 - Implemented pype-config/presets/plugins/nukestudio/filter.json --- .../projects_schema/1_plugins_gui_schema.json | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/1_plugins_gui_schema.json b/pype/tools/settings/settings/gui_schemas/projects_schema/1_plugins_gui_schema.json index 96aad3e5a9..0c3b2c25d2 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/1_plugins_gui_schema.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/1_plugins_gui_schema.json @@ -531,6 +531,55 @@ "key": "nukestudio", "label": "NukeStudio", "children": [ + { + "type": "dict", + "collapsable": true, + "key": "filter", + "label": "Filter", + "is_file": true, + "children": [ + { + "type": "dict", + "collapsable": true, + "checkbox_key": "enabled", + "key": "strict", + "label": "strict", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "ValidateVersion", + "label": "ValidateVersion" + }, + { + "type": "boolean", + "key": "VersionUpWorkfile", + "label": "VersionUpWorkfile" + } + ] + }, + { + "type": "dict", + "collapsable": true, + "checkbox_key": "enabled", + "key": "benevolent", + "label": "benevolent", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "ValidateVersion", + "label": "ValidateVersion" + }, + { + "type": "boolean", + "key": "VersionUpWorkfile", + "label": "VersionUpWorkfile" + } + ] + } + ] + }, { "type": "dict", "collapsable": true, From 3c0fc416d45cc990fd7eabdf5a62eaeef4fecf74 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 16 Sep 2020 17:44:22 +0200 Subject: [PATCH 640/813] removed duplicated method --- pype/tools/settings/settings/widgets/item_types.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index e2d59c2e69..24c22e754a 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -22,19 +22,6 @@ class SettingObject: # default state of item type default_state = "" - @classmethod - def style_state(cls, is_invalid, is_overriden, is_modified): - """Return stylesheet state by intered booleans.""" - items = [] - if is_invalid: - items.append("invalid") - else: - if is_overriden: - items.append("overriden") - if is_modified: - items.append("modified") - return "-".join(items) or cls.default_state - def _set_default_attributes(self): """Create and reset attributes required for all item types. From 737d02ec9ed37123a7abcecfff5b2ae27a6d3aca Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 16 Sep 2020 17:44:40 +0200 Subject: [PATCH 641/813] added docstrings to SettingObject's methods --- .../settings/settings/widgets/item_types.py | 75 ++++++++++++++++++- 1 file changed, 71 insertions(+), 4 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 24c22e754a..9388d5e124 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -103,6 +103,9 @@ class SettingObject: def had_studio_override(self): """Item had studio overrides on refresh. + Use attribute `_had_studio_override` which should be changed only + during methods `update_studio_values` and `update_default_values`. + Returns: bool @@ -114,7 +117,7 @@ class SettingObject: """Item has studio override at the moment. With combination of `had_studio_override` is possible to know if item - has changes (not just value change). + is modified (not value change). Returns: bool @@ -190,7 +193,8 @@ class SettingObject: @property def is_overidable(self): - """Should care about overrides.""" + """ care about overrides.""" + return self._parent.is_overidable def any_parent_overriden(self): @@ -200,6 +204,7 @@ class SettingObject: bool """ + if self._parent._is_overriden: return True return self._parent.any_parent_overriden() @@ -222,6 +227,7 @@ class SettingObject: def style_state( cls, has_studio_override, is_invalid, is_overriden, is_modified ): + """Return stylesheet state by intered booleans.""" items = [] if is_invalid: items.append("invalid") @@ -314,6 +320,15 @@ class SettingObject: self.ignore_value_changes = False def discard_changes(self): + """Item's implementation to discard all changes made by user. + + Reset all values to same values as had when opened GUI + or when changed project. + + Must not affect `had_studio_override` value or `was_overriden` + value. It must be marked that there are keys/values which are not in + defaults or overrides. + """ raise NotImplementedError( "{} Method `discard_changes` not implemented!".format( repr(self) @@ -326,6 +341,10 @@ class SettingObject: self.ignore_value_changes = False def set_studio_default(self): + """Item's implementation to set current values as studio's overrides. + + Mark item and it's children as they have studio overrides. + """ raise NotImplementedError( "{} Method `set_studio_default` not implemented!".format( repr(self) @@ -338,6 +357,11 @@ class SettingObject: self.ignore_value_changes = False def reset_to_pype_default(self): + """Item's implementation to remove studio overrides. + + Mark item as it does not have studio overrides unset studio + override values. + """ raise NotImplementedError( "{} Method `reset_to_pype_default` not implemented!".format( repr(self) @@ -350,6 +374,11 @@ class SettingObject: self.ignore_value_changes = False def remove_overrides(self): + """Item's implementation to remove project overrides. + + Mark item as does not have project overrides. Must not change + `was_overriden` attribute value. + """ raise NotImplementedError( "{} Method `remove_overrides` not implemented!".format( repr(self) @@ -362,11 +391,21 @@ class SettingObject: self.ignore_value_changes = False def set_as_overriden(self): + """Item's implementation to set values as overriden for project. + + Mark item and all it's children as they're overriden. Must skip + items with children items that has attributes `is_group` + and `any_parent_is_group` set to False. In that case those items + are not meant to be overridable and should trigger the method on it's + children. + + """ raise NotImplementedError( "{} Method `set_as_overriden` not implemented!".format(repr(self)) ) def hierarchical_style_update(self): + """Trigger update style method down the hierarchy.""" raise NotImplementedError( "{} Method `hierarchical_style_update` not implemented!".format( repr(self) @@ -374,23 +413,51 @@ class SettingObject: ) def update_default_values(self, parent_values): + """Fill default values on startup or on refresh. + + Default values stored in `pype` repository should update all items in + schema. Each item should take values for his key and set it's value or + pass values down to children items. + + Args: + parent_values (dict): Values of parent's item. But in case item is + used as widget, `parent_values` contain value for item. + """ raise NotImplementedError( "{} does not have implemented `update_default_values`".format(self) ) def update_studio_values(self, parent_values): + """Fill studio override values on startup or on refresh. + + Set studio value if is not set to NOT_SET, in that case studio + overrides are not set yet. + + Args: + parent_values (dict): Values of parent's item. But in case item is + used as widget, `parent_values` contain value for item. + """ raise NotImplementedError( "{} does not have implemented `update_studio_values`".format(self) ) def apply_overrides(self, parent_values): + """Fill project override values on startup, refresh or project change. + + Set project value if is not set to NOT_SET, in that case project + overrides are not set yet. + + Args: + parent_values (dict): Values of parent's item. But in case item is + used as widget, `parent_values` contain value for item. + """ raise NotImplementedError( "{} does not have implemented `apply_overrides`".format(self) ) @property def child_has_studio_override(self): - """Any children item is modified.""" + """Any children item has studio overrides.""" raise NotImplementedError( "{} does not have implemented `child_has_studio_override`".format( self @@ -406,7 +473,7 @@ class SettingObject: @property def child_overriden(self): - """Any children item is overriden.""" + """Any children item has project overrides.""" raise NotImplementedError( "{} does not have implemented `child_overriden`".format(self) ) From b74375325fc9656331287796a017068267168d65 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 16 Sep 2020 18:22:39 +0200 Subject: [PATCH 642/813] default_state removed --- pype/tools/settings/settings/widgets/item_types.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 9388d5e124..d78d335902 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -19,8 +19,6 @@ class SettingObject: default_input_value = NOT_SET # will allow to show actions for the item type (disabled for proxies) allow_actions = True - # default state of item type - default_state = "" def _set_default_attributes(self): """Create and reset attributes required for all item types. @@ -240,7 +238,7 @@ class SettingObject: if not items and has_studio_override: items.append("studio") - return "-".join(items) or cls.default_state + return "-".join(items) or "" def mouseReleaseEvent(self, event): if self.allow_actions and event.button() == QtCore.Qt.RightButton: From a8e03ee091e7a311a0a866a9c13ab7275a07b235 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 16 Sep 2020 18:25:00 +0200 Subject: [PATCH 643/813] few more docstrings --- .../settings/settings/widgets/item_types.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index d78d335902..ea32d9c79c 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -12,13 +12,18 @@ from avalon.vendor import qtawesome class SettingObject: + """Partially abstract class for Setting's item type workflow.""" # `is_input_type` attribute says if has implemented item type methods is_input_type = True - # each input must have implemented default value for development - # when defaults are not filled yet + # Each input must have implemented default value for development + # when defaults are not filled yet. default_input_value = NOT_SET - # will allow to show actions for the item type (disabled for proxies) + # 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 + # All item types must have implemented Qt signal which is emitted when + # it's or it's children value has changed, + value_changed = None def _set_default_attributes(self): """Create and reset attributes required for all item types. @@ -501,6 +506,11 @@ class SettingObject: class InputObject(SettingObject): + """Class for inputs with pre-implemented methods. + + Class is for item types not creating or using other item types, most + of methods has same code in that case. + """ def update_default_values(self, parent_values): self._state = None self._is_modified = False From 84d5580873ea0b34da3ee65fc339c858dc08d77f Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 16 Sep 2020 19:05:12 +0200 Subject: [PATCH 644/813] attribute `is_input_type` renamed to `is_item_type` and added better check --- pype/tools/settings/settings/widgets/item_types.py | 12 ++++++------ pype/tools/settings/settings/widgets/lib.py | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index e2d59c2e69..ff95ba82c3 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -12,8 +12,8 @@ from avalon.vendor import qtawesome class SettingObject: - # `is_input_type` attribute says if has implemented item type methods - is_input_type = True + # `is_item_type` attribute says if has implemented item type methods + is_item_type = True # each input must have implemented default value for development # when defaults are not filled yet default_input_value = NOT_SET @@ -1935,7 +1935,7 @@ class DictWidget(QtWidgets.QWidget, SettingObject): item_type = child_configuration["type"] klass = TypeToKlass.types.get(item_type) - if not klass.is_input_type: + if not getattr(klass, "is_item_type", False): item = klass(child_configuration, self) self.content_layout.addWidget(item) return item @@ -2226,7 +2226,7 @@ class DictInvisible(QtWidgets.QWidget, SettingObject): item_type = child_configuration["type"] klass = TypeToKlass.types.get(item_type) - if not klass.is_input_type: + if not klass.is_item_type: item = klass(child_configuration, self) self.layout().addWidget(item) return item @@ -3003,7 +3003,7 @@ class DictFormWidget(QtWidgets.QWidget, SettingObject): class LabelWidget(QtWidgets.QWidget): - is_input_type = False + is_item_type = False def __init__(self, configuration, parent=None): super(LabelWidget, self).__init__(parent) @@ -3018,7 +3018,7 @@ class LabelWidget(QtWidgets.QWidget): class SplitterWidget(QtWidgets.QWidget): - is_input_type = False + is_item_type = False _height = 2 def __init__(self, configuration, parent=None): diff --git a/pype/tools/settings/settings/widgets/lib.py b/pype/tools/settings/settings/widgets/lib.py index e225d65417..a2646ad4e6 100644 --- a/pype/tools/settings/settings/widgets/lib.py +++ b/pype/tools/settings/settings/widgets/lib.py @@ -125,7 +125,7 @@ def file_keys_from_schema(schema_data): output = [] item_type = schema_data["type"] klass = TypeToKlass.types[item_type] - if not klass.is_input_type: + if not klass.is_item_type: return output keys = [] @@ -150,7 +150,7 @@ def file_keys_from_schema(schema_data): def validate_all_has_ending_file(schema_data, is_top=True): item_type = schema_data["type"] klass = TypeToKlass.types[item_type] - if not klass.is_input_type: + if not klass.is_item_type: return None if schema_data.get("is_file"): From 733ae9d8b1130c584d74345c7631d69717e16757 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 17 Sep 2020 10:26:06 +0200 Subject: [PATCH 645/813] #241 - Reverted checkboxes back to raw-json Both keys and values for this field are editable, no valid widget for such a case --- .../projects_schema/1_plugins_gui_schema.json | 48 ++----------------- 1 file changed, 3 insertions(+), 45 deletions(-) diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/1_plugins_gui_schema.json b/pype/tools/settings/settings/gui_schemas/projects_schema/1_plugins_gui_schema.json index 0c3b2c25d2..721b0924e8 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/1_plugins_gui_schema.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/1_plugins_gui_schema.json @@ -532,53 +532,11 @@ "label": "NukeStudio", "children": [ { - "type": "dict", + "type": "raw-json", "collapsable": true, "key": "filter", - "label": "Filter", - "is_file": true, - "children": [ - { - "type": "dict", - "collapsable": true, - "checkbox_key": "enabled", - "key": "strict", - "label": "strict", - "is_group": true, - "children": [ - { - "type": "boolean", - "key": "ValidateVersion", - "label": "ValidateVersion" - }, - { - "type": "boolean", - "key": "VersionUpWorkfile", - "label": "VersionUpWorkfile" - } - ] - }, - { - "type": "dict", - "collapsable": true, - "checkbox_key": "enabled", - "key": "benevolent", - "label": "benevolent", - "is_group": true, - "children": [ - { - "type": "boolean", - "key": "ValidateVersion", - "label": "ValidateVersion" - }, - { - "type": "boolean", - "key": "VersionUpWorkfile", - "label": "VersionUpWorkfile" - } - ] - } - ] + "label": "Publish GUI Filters", + "is_file": true }, { "type": "dict", From 20230fbccd10e47abd1fcb130305ddf1b7bafea7 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 17 Sep 2020 10:28:17 +0200 Subject: [PATCH 646/813] Revert "attribute `is_input_type` renamed to `is_item_type` and added better check" This reverts commit 84d5580873ea0b34da3ee65fc339c858dc08d77f. --- pype/tools/settings/settings/widgets/item_types.py | 12 ++++++------ pype/tools/settings/settings/widgets/lib.py | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index ff95ba82c3..e2d59c2e69 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -12,8 +12,8 @@ from avalon.vendor import qtawesome class SettingObject: - # `is_item_type` attribute says if has implemented item type methods - is_item_type = True + # `is_input_type` attribute says if has implemented item type methods + is_input_type = True # each input must have implemented default value for development # when defaults are not filled yet default_input_value = NOT_SET @@ -1935,7 +1935,7 @@ class DictWidget(QtWidgets.QWidget, SettingObject): item_type = child_configuration["type"] klass = TypeToKlass.types.get(item_type) - if not getattr(klass, "is_item_type", False): + if not klass.is_input_type: item = klass(child_configuration, self) self.content_layout.addWidget(item) return item @@ -2226,7 +2226,7 @@ class DictInvisible(QtWidgets.QWidget, SettingObject): item_type = child_configuration["type"] klass = TypeToKlass.types.get(item_type) - if not klass.is_item_type: + if not klass.is_input_type: item = klass(child_configuration, self) self.layout().addWidget(item) return item @@ -3003,7 +3003,7 @@ class DictFormWidget(QtWidgets.QWidget, SettingObject): class LabelWidget(QtWidgets.QWidget): - is_item_type = False + is_input_type = False def __init__(self, configuration, parent=None): super(LabelWidget, self).__init__(parent) @@ -3018,7 +3018,7 @@ class LabelWidget(QtWidgets.QWidget): class SplitterWidget(QtWidgets.QWidget): - is_item_type = False + is_input_type = False _height = 2 def __init__(self, configuration, parent=None): diff --git a/pype/tools/settings/settings/widgets/lib.py b/pype/tools/settings/settings/widgets/lib.py index a2646ad4e6..e225d65417 100644 --- a/pype/tools/settings/settings/widgets/lib.py +++ b/pype/tools/settings/settings/widgets/lib.py @@ -125,7 +125,7 @@ def file_keys_from_schema(schema_data): output = [] item_type = schema_data["type"] klass = TypeToKlass.types[item_type] - if not klass.is_item_type: + if not klass.is_input_type: return output keys = [] @@ -150,7 +150,7 @@ def file_keys_from_schema(schema_data): def validate_all_has_ending_file(schema_data, is_top=True): item_type = schema_data["type"] klass = TypeToKlass.types[item_type] - if not klass.is_item_type: + if not klass.is_input_type: return None if schema_data.get("is_file"): From 828ebde3deea11a28251420d516962be0b0e7bfd Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 17 Sep 2020 12:25:47 +0200 Subject: [PATCH 647/813] added attribute to know if item type should be expanded in grid layout --- pype/tools/settings/settings/widgets/item_types.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index ea32d9c79c..ffbb128c3d 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: # All item types must have implemented Qt signal which is emitted when # it's or it's children value has changed, value_changed = None + # Item will expand to full width in grid layout + expand_in_grid = False def _set_default_attributes(self): """Create and reset attributes required for all item types. @@ -1693,6 +1695,7 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): # Should be used only for dictionary with one datatype as value # TODO this is actually input field (do not care if is group or not) value_changed = QtCore.Signal(object) + expand_in_grid = True def __init__( self, input_data, parent, @@ -1926,6 +1929,7 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): # Dictionaries class DictWidget(QtWidgets.QWidget, SettingObject): value_changed = QtCore.Signal(object) + expand_in_grid = True def __init__( self, input_data, parent, @@ -2256,6 +2260,7 @@ class DictInvisible(QtWidgets.QWidget, SettingObject): # TODO is not overridable by itself value_changed = QtCore.Signal(object) allow_actions = False + expand_in_grid = True def __init__( self, input_data, parent, @@ -2871,6 +2876,7 @@ class FormLabel(QtWidgets.QLabel): class DictFormWidget(QtWidgets.QWidget, SettingObject): value_changed = QtCore.Signal(object) allow_actions = False + expand_in_grid = True def __init__( self, input_data, parent, From 365c6852c0bb0d91ab3248a00d9182ea226ea610 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 17 Sep 2020 12:26:53 +0200 Subject: [PATCH 648/813] using QGridLayout in dict and dict-invisible --- pype/tools/settings/settings/widgets/item_types.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index ffbb128c3d..d745e83ffa 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -1969,7 +1969,7 @@ class DictWidget(QtWidgets.QWidget, SettingObject): content_widget = QtWidgets.QWidget(body_widget) content_widget.setObjectName("ContentWidget") content_widget.setProperty("content_state", content_state) - content_layout = QtWidgets.QVBoxLayout(content_widget) + content_layout = QtWidgets.QGridLayout(content_widget) content_layout.setContentsMargins(CHILD_OFFSET, 5, 0, bottom_margin) body_widget.set_content_widget(content_widget) @@ -2278,10 +2278,12 @@ class DictInvisible(QtWidgets.QWidget, SettingObject): self.setAttribute(QtCore.Qt.WA_TranslucentBackground) - layout = QtWidgets.QVBoxLayout(self) + layout = QtWidgets.QGridLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(5) + self.content_layout = layout + self.input_fields = [] self.key = input_data["key"] From 5f574c1643bc6db92327ffd8cb9e570ed60adbdd Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 17 Sep 2020 12:27:13 +0200 Subject: [PATCH 649/813] items are added to grid layout in right order --- .../settings/settings/widgets/item_types.py | 39 +++++++++++++++---- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index d745e83ffa..628b7262e8 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -2001,9 +2001,10 @@ class DictWidget(QtWidgets.QWidget, SettingObject): item_type = child_configuration["type"] klass = TypeToKlass.types.get(item_type) - if not klass.is_input_type: + row = self.content_layout.rowCount() + if not getattr(klass, "is_input_type", False): item = klass(child_configuration, self) - self.content_layout.addWidget(item) + self.content_layout.addWidget(item, row, 0, 1, 2) return item if self.checkbox_key and not self.checkbox_widget: @@ -2011,9 +2012,20 @@ class DictWidget(QtWidgets.QWidget, SettingObject): if key == self.checkbox_key: return self._add_checkbox_child(child_configuration) - item = klass(child_configuration, self) + label_widget = None + if not klass.expand_in_grid: + label = child_configuration.get("label") + if label is not None: + label_widget = QtWidgets.QLabel(label, self) + self.content_layout.addWidget(label_widget, row, 0, 1, 1) + + item = klass(child_configuration, self, label_widget=label_widget) item.value_changed.connect(self._on_value_change) - self.content_layout.addWidget(item) + + if label_widget: + self.content_layout.addWidget(item, row, 1, 1, 1) + else: + self.content_layout.addWidget(item, row, 0, 1, 2) self.input_fields.append(item) return item @@ -2295,16 +2307,27 @@ class DictInvisible(QtWidgets.QWidget, SettingObject): item_type = child_configuration["type"] klass = TypeToKlass.types.get(item_type) - if not klass.is_input_type: + row = self.content_layout.rowCount() + if not getattr(klass, "is_input_type", False): item = klass(child_configuration, self) - self.layout().addWidget(item) + self.content_layout.addWidget(item, row, 0, 1, 2) return item - item = klass(child_configuration, self) - self.layout().addWidget(item) + label_widget = None + if not klass.expand_in_grid: + label = child_configuration.get("label") + if label is not None: + label_widget = QtWidgets.QLabel(label, self) + self.content_layout.addWidget(label_widget, row, 0, 1, 1) + item = klass(child_configuration, self, label_widget=label_widget) item.value_changed.connect(self._on_value_change) + if label_widget: + self.content_layout.addWidget(item, row, 1, 1, 1) + else: + self.content_layout.addWidget(item, row, 0, 1, 2) + self.input_fields.append(item) return item From 3e24555f49b384cc4863db81806e4831dc39741b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 17 Sep 2020 12:27:57 +0200 Subject: [PATCH 650/813] removed dict-form from schemas --- .../projects_schema/1_plugins_gui_schema.json | 83 +++--- .../1_applications_gui_schema.json | 259 +++++++++--------- .../system_schema/1_tools_gui_schema.json | 35 +-- .../system_schema/1_tray_items.json | 91 +++--- 4 files changed, 218 insertions(+), 250 deletions(-) diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/1_plugins_gui_schema.json b/pype/tools/settings/settings/gui_schemas/projects_schema/1_plugins_gui_schema.json index 721b0924e8..b2d7914c84 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/1_plugins_gui_schema.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/1_plugins_gui_schema.json @@ -30,34 +30,29 @@ "key": "enabled", "label": "Enabled" }, { - "type": "dict-form", - "children": [ - { - "type": "text", - "key": "deadline_department", - "label": "Deadline apartment" - }, { - "type": "number", - "key": "deadline_priority", - "label": "Deadline priority" - }, { - "type": "text", - "key": "deadline_pool", - "label": "Deadline pool" - }, { - "type": "text", - "key": "deadline_pool_secondary", - "label": "Deadline pool (secondary)" - }, { - "type": "text", - "key": "deadline_group", - "label": "Deadline Group" - }, { - "type": "number", - "key": "deadline_chunk_size", - "label": "Deadline Chunk size" - } - ] + "type": "text", + "key": "deadline_department", + "label": "Deadline apartment" + }, { + "type": "number", + "key": "deadline_priority", + "label": "Deadline priority" + }, { + "type": "text", + "key": "deadline_pool", + "label": "Deadline pool" + }, { + "type": "text", + "key": "deadline_pool_secondary", + "label": "Deadline pool (secondary)" + }, { + "type": "text", + "key": "deadline_group", + "label": "Deadline Group" + }, { + "type": "number", + "key": "deadline_chunk_size", + "label": "Deadline Chunk size" } ] } @@ -531,13 +526,6 @@ "key": "nukestudio", "label": "NukeStudio", "children": [ - { - "type": "raw-json", - "collapsable": true, - "key": "filter", - "label": "Publish GUI Filters", - "is_file": true - }, { "type": "dict", "collapsable": true, @@ -649,21 +637,16 @@ "label": "ffmpeg_args", "children": [ { - "type": "dict-form", - "children": [ - { - "type": "list", - "object_type": "text", - "key": "input", - "label": "input" - }, - { - "type": "list", - "object_type": "text", - "key": "output", - "label": "output" - } - ] + "type": "list", + "object_type": "text", + "key": "input", + "label": "input" + }, + { + "type": "list", + "object_type": "text", + "key": "output", + "label": "output" } ] } diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/1_applications_gui_schema.json b/pype/tools/settings/settings/gui_schemas/system_schema/1_applications_gui_schema.json index 48f8ecbd7c..3427f98253 100644 --- a/pype/tools/settings/settings/gui_schemas/system_schema/1_applications_gui_schema.json +++ b/pype/tools/settings/settings/gui_schemas/system_schema/1_applications_gui_schema.json @@ -7,138 +7,133 @@ "is_file": true, "children": [ { - "type": "dict-form", - "children": [ - { - "type": "boolean", - "key": "blender_2.80", - "label": "Blender 2.80" - }, { - "type": "boolean", - "key": "blender_2.81", - "label": "Blender 2.81" - }, { - "type": "boolean", - "key": "blender_2.82", - "label": "Blender 2.82" - }, { - "type": "boolean", - "key": "blender_2.83", - "label": "Blender 2.83" - }, { - "type": "boolean", - "key": "celaction_local", - "label": "Celaction Local" - }, { - "type": "boolean", - "key": "celaction_remote", - "label": "Celaction Remote" - }, { - "type": "boolean", - "key": "harmony_17", - "label": "Harmony 17" - }, { - "type": "boolean", - "key": "maya_2017", - "label": "Autodest Maya 2017" - }, { - "type": "boolean", - "key": "maya_2018", - "label": "Autodest Maya 2018" - }, { - "type": "boolean", - "key": "maya_2019", - "label": "Autodest Maya 2019" - }, { - "type": "boolean", - "key": "maya_2020", - "label": "Autodest Maya 2020" - }, { - "key": "nuke_10.0", - "type": "boolean", - "label": "Nuke 10.0" - }, { - "type": "boolean", - "key": "nuke_11.2", - "label": "Nuke 11.2" - }, { - "type": "boolean", - "key": "nuke_11.3", - "label": "Nuke 11.3" - }, { - "type": "boolean", - "key": "nuke_12.0", - "label": "Nuke 12.0" - }, { - "type": "boolean", - "key": "nukex_10.0", - "label": "NukeX 10.0" - }, { - "type": "boolean", - "key": "nukex_11.2", - "label": "NukeX 11.2" - }, { - "type": "boolean", - "key": "nukex_11.3", - "label": "NukeX 11.3" - }, { - "type": "boolean", - "key": "nukex_12.0", - "label": "NukeX 12.0" - }, { - "type": "boolean", - "key": "nukestudio_10.0", - "label": "NukeStudio 10.0" - }, { - "type": "boolean", - "key": "nukestudio_11.2", - "label": "NukeStudio 11.2" - }, { - "type": "boolean", - "key": "nukestudio_11.3", - "label": "NukeStudio 11.3" - }, { - "type": "boolean", - "key": "nukestudio_12.0", - "label": "NukeStudio 12.0" - }, { - "type": "boolean", - "key": "houdini_16", - "label": "Houdini 16" - }, { - "type": "boolean", - "key": "houdini_16.5", - "label": "Houdini 16.5" - }, { - "type": "boolean", - "key": "houdini_17", - "label": "Houdini 17" - }, { - "type": "boolean", - "key": "houdini_18", - "label": "Houdini 18" - }, { - "type": "boolean", - "key": "premiere_2019", - "label": "Premiere 2019" - }, { - "type": "boolean", - "key": "premiere_2020", - "label": "Premiere 2020" - }, { - "type": "boolean", - "key": "resolve_16", - "label": "BM DaVinci Resolve 16" - }, { - "type": "boolean", - "key": "storyboardpro_7", - "label": "Storyboard Pro 7" - }, { - "type": "boolean", - "key": "unreal_4.24", - "label": "Unreal Editor 4.24" - } - ] + "type": "boolean", + "key": "blender_2.80", + "label": "Blender 2.80" + }, { + "type": "boolean", + "key": "blender_2.81", + "label": "Blender 2.81" + }, { + "type": "boolean", + "key": "blender_2.82", + "label": "Blender 2.82" + }, { + "type": "boolean", + "key": "blender_2.83", + "label": "Blender 2.83" + }, { + "type": "boolean", + "key": "celaction_local", + "label": "Celaction Local" + }, { + "type": "boolean", + "key": "celaction_remote", + "label": "Celaction Remote" + }, { + "type": "boolean", + "key": "harmony_17", + "label": "Harmony 17" + }, { + "type": "boolean", + "key": "maya_2017", + "label": "Autodest Maya 2017" + }, { + "type": "boolean", + "key": "maya_2018", + "label": "Autodest Maya 2018" + }, { + "type": "boolean", + "key": "maya_2019", + "label": "Autodest Maya 2019" + }, { + "type": "boolean", + "key": "maya_2020", + "label": "Autodest Maya 2020" + }, { + "key": "nuke_10.0", + "type": "boolean", + "label": "Nuke 10.0" + }, { + "type": "boolean", + "key": "nuke_11.2", + "label": "Nuke 11.2" + }, { + "type": "boolean", + "key": "nuke_11.3", + "label": "Nuke 11.3" + }, { + "type": "boolean", + "key": "nuke_12.0", + "label": "Nuke 12.0" + }, { + "type": "boolean", + "key": "nukex_10.0", + "label": "NukeX 10.0" + }, { + "type": "boolean", + "key": "nukex_11.2", + "label": "NukeX 11.2" + }, { + "type": "boolean", + "key": "nukex_11.3", + "label": "NukeX 11.3" + }, { + "type": "boolean", + "key": "nukex_12.0", + "label": "NukeX 12.0" + }, { + "type": "boolean", + "key": "nukestudio_10.0", + "label": "NukeStudio 10.0" + }, { + "type": "boolean", + "key": "nukestudio_11.2", + "label": "NukeStudio 11.2" + }, { + "type": "boolean", + "key": "nukestudio_11.3", + "label": "NukeStudio 11.3" + }, { + "type": "boolean", + "key": "nukestudio_12.0", + "label": "NukeStudio 12.0" + }, { + "type": "boolean", + "key": "houdini_16", + "label": "Houdini 16" + }, { + "type": "boolean", + "key": "houdini_16.5", + "label": "Houdini 16.5" + }, { + "type": "boolean", + "key": "houdini_17", + "label": "Houdini 17" + }, { + "type": "boolean", + "key": "houdini_18", + "label": "Houdini 18" + }, { + "type": "boolean", + "key": "premiere_2019", + "label": "Premiere 2019" + }, { + "type": "boolean", + "key": "premiere_2020", + "label": "Premiere 2020" + }, { + "type": "boolean", + "key": "resolve_16", + "label": "BM DaVinci Resolve 16" + }, { + "type": "boolean", + "key": "storyboardpro_7", + "label": "Storyboard Pro 7" + }, { + "type": "boolean", + "key": "unreal_4.24", + "label": "Unreal Editor 4.24" } ] } diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/1_tools_gui_schema.json b/pype/tools/settings/settings/gui_schemas/system_schema/1_tools_gui_schema.json index d9540eeb3e..08b8d13d89 100644 --- a/pype/tools/settings/settings/gui_schemas/system_schema/1_tools_gui_schema.json +++ b/pype/tools/settings/settings/gui_schemas/system_schema/1_tools_gui_schema.json @@ -7,26 +7,21 @@ "is_file": true, "children": [ { - "type": "dict-form", - "children": [ - { - "key": "mtoa_3.0.1", - "type": "boolean", - "label": "Arnold Maya 3.0.1" - }, { - "key": "mtoa_3.1.1", - "type": "boolean", - "label": "Arnold Maya 3.1.1" - }, { - "key": "mtoa_3.2.0", - "type": "boolean", - "label": "Arnold Maya 3.2.0" - }, { - "key": "yeti_2.1.2", - "type": "boolean", - "label": "Yeti 2.1.2" - } - ] + "key": "mtoa_3.0.1", + "type": "boolean", + "label": "Arnold Maya 3.0.1" + }, { + "key": "mtoa_3.1.1", + "type": "boolean", + "label": "Arnold Maya 3.1.1" + }, { + "key": "mtoa_3.2.0", + "type": "boolean", + "label": "Arnold Maya 3.2.0" + }, { + "key": "yeti_2.1.2", + "type": "boolean", + "label": "Yeti 2.1.2" } ] } diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/1_tray_items.json b/pype/tools/settings/settings/gui_schemas/system_schema/1_tray_items.json index 6da974a415..0d27ccdc4b 100644 --- a/pype/tools/settings/settings/gui_schemas/system_schema/1_tray_items.json +++ b/pype/tools/settings/settings/gui_schemas/system_schema/1_tray_items.json @@ -11,54 +11,49 @@ "type": "dict-invisible", "children": [ { - "type": "dict-form", - "children": [ - { - "type": "boolean", - "key": "User settings", - "label": "User settings" - }, { - "type": "boolean", - "key": "Ftrack", - "label": "Ftrack" - }, { - "type": "boolean", - "key": "Muster", - "label": "Muster" - }, { - "type": "boolean", - "key": "Avalon", - "label": "Avalon" - }, { - "type": "boolean", - "key": "Clockify", - "label": "Clockify" - }, { - "type": "boolean", - "key": "Standalone Publish", - "label": "Standalone Publish" - }, { - "type": "boolean", - "key": "Logging", - "label": "Logging" - }, { - "type": "boolean", - "key": "Idle Manager", - "label": "Idle Manager" - }, { - "type": "boolean", - "key": "Timers Manager", - "label": "Timers Manager" - }, { - "type": "boolean", - "key": "Rest Api", - "label": "Rest Api" - }, { - "type": "boolean", - "key": "Adobe Communicator", - "label": "Adobe Communicator" - } - ] + "type": "boolean", + "key": "User settings", + "label": "User settings" + }, { + "type": "boolean", + "key": "Ftrack", + "label": "Ftrack" + }, { + "type": "boolean", + "key": "Muster", + "label": "Muster" + }, { + "type": "boolean", + "key": "Avalon", + "label": "Avalon" + }, { + "type": "boolean", + "key": "Clockify", + "label": "Clockify" + }, { + "type": "boolean", + "key": "Standalone Publish", + "label": "Standalone Publish" + }, { + "type": "boolean", + "key": "Logging", + "label": "Logging" + }, { + "type": "boolean", + "key": "Idle Manager", + "label": "Idle Manager" + }, { + "type": "boolean", + "key": "Timers Manager", + "label": "Timers Manager" + }, { + "type": "boolean", + "key": "Rest Api", + "label": "Rest Api" + }, { + "type": "boolean", + "key": "Adobe Communicator", + "label": "Adobe Communicator" } ] }, { From 35d7f0b1d28f2c2248a49a2eff5063669ab4b44a Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 17 Sep 2020 12:30:37 +0200 Subject: [PATCH 651/813] added Gui schema README --- pype/tools/settings/settings/README.md | 261 +++++++++++++++++++++++++ 1 file changed, 261 insertions(+) create mode 100644 pype/tools/settings/settings/README.md diff --git a/pype/tools/settings/settings/README.md b/pype/tools/settings/settings/README.md new file mode 100644 index 0000000000..db444eb3bd --- /dev/null +++ b/pype/tools/settings/settings/README.md @@ -0,0 +1,261 @@ +# Creating GUI schemas + +## Basic rules +- configurations does not define GUI, but GUI defines configurations! +- output is always json (yaml is not needed for anatomy templates anymore) +- GUI schema has multiple input types, all inputs are represented by a dictionary +- each input may have "input modifiers" (keys in dictionary) that are required or optional + - only required modifier for all input items is key `"type"` which says what type of item it is +- there are special keys across all inputs + - `"is_file"` - this key is for storing pype defaults in `pype` repo + - reasons of existence: developing new schemas does not require to create defaults manually + - key is validated, must be once in hierarchy else it won't be possible to store pype defaults + - `"is_group"` - define that all values under key in hierarchy will be overriden if any value is modified, this information is also stored to overrides + - this keys is not allowed for all inputs as they may have not reason for that + - key is validated, can be only once in hierarchy but is not required +- currently there are `system configurations` and `project configurations` + +## Inner schema +- 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") + +### 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 +- will just paste schemas from other schema file in order of "children" list + +``` +{ + "type": "schema", + "children": [ + "my_schema_name", + "my_other_schema_name" + ] +} +``` + +## Basic Dictionary inputs +- these inputs wraps another inputs into {key: value} relation + +### dict-invisible +- this input gives ability to wrap another inputs but keep them in same widget without visible divider + - this is for example used as first input widget +- has required keys `"key"` and `"children"` + - "children" says what children inputs are underneath + - "key" is key under which will be stored value from it's children +- output is dictionary `{the "key": children values}` +- can't have `"is_group"` key set to True as it breaks visual override showing +``` +{ + "type": "dict-invisible", + "key": "global", + "children": [ + ...ITEMS... + ] +} +``` + +## dict +- this is another dictionary input wrapping more inputs but visually makes them different +- required keys are `"key"` under which will be stored and `"label"` which will be shown in GUI +- this input can be expandable + - that can be set with key `"expandable"` as `True`/`False` (Default: `True`) + - with key `"expanded"` as `True`/`False` can be set that is expanded when GUI is opened (Default: `False`) +- it is possible to add darker background with `"highlight_content"` (Default: `False`) + - darker background has limits of maximum applies after 3-4 nested highlighted items there is not difference in the color +``` +{ + "key": "applications", + "type": "dict", + "label": "Applications", + "expandable": true, + "highlight_content": true, + "is_group": true, + "is_file": true, + "children": [ + ...ITEMS... + ] +} +``` + +## Inputs for setting any kind of value (`Pure` inputs) +- all these input must have defined `"key"` under which will be stored and `"label"` which will be shown next to input + - unless they are used in different types of inputs (later) "as widgets" in that case `"key"` and `"label"` are not required as there is not place where to set them + +### boolean +- simple checkbox, nothing more to set +``` +{ + "type": "boolean", + "key": "my_boolean_key", + "label": "Do you want to use Pype?" +} +``` + +### number +- number input, can be used for both integer and float + - key `"decimal"` defines how many decimal places will be used, 0 is for integer input (Default: `0`) + - key `"minimum"` as minimum allowed number to enter (Default: `-99999`) + - key `"maxium"` as maximum allowed number to enter (Default: `99999`) +``` +{ + "type": "number", + "key": "fps", + "label": "Frame rate (FPS)" + "decimal": 2, + "minimum": 1, + "maximum": 300000 +} +``` + +### text +- simple text input + - key `"multiline"` allows to enter multiple lines of text (Default: `False`) + +``` +{ + "type": "text", + "key": "deadline_pool", + "label": "Deadline pool" +} +``` + +### path-input +- enhanced text input + - does not allow to enter backslash, is auto-converted to forward slash + - may be added another validations, like do not allow end path with slash +- this input is implemented to add additional features to text input +- this is meant to be used in proxy input `path-widget` + - DO NOT USE this input in schema please + +### raw-json +- a little bit enhanced text input for raw json +- has validations of json format + - empty value is invalid value, always must be at least `{}` of `[]` + +``` +{ + "type": "raw-json", + "key": "profiles", + "label": "Extract Review profiles" +} +``` + +## Inputs for setting value using Pure inputs +- these inputs also have required `"key"` and `"label"` +- they use Pure inputs "as widgets" + +### list +- output is list +- items can be added and removed +- items in list must be the same type + - type of items is defined with key `"object_type"` where Pure input name is entered (e.g. `number`) + - because Pure inputs may have modifiers (`number` input has `minimum`, `maximum` and `decimals`) you can set these in key `"input_modifiers"` + +``` +{ + "type": "list", + "object_type": "number", + "key": "exclude_ports", + "label": "Exclude ports", + "input_modifiers": { + "minimum": 1, + "maximum": 65535 + } +} +``` + +### dict-modifiable +- one of dictionary inputs, this is only used as value input +- items in this input can be removed and added same way as in `list` input +- value items in dictionary must be the same type + - type of items is defined with key `"object_type"` where Pure input name is entered (e.g. `number`) + - because Pure inputs may have modifiers (`number` input has `minimum`, `maximum` and `decimals`) you can set these in key `"input_modifiers"` +- this input can be expandable + - that can be set with key `"expandable"` as `True`/`False` (Default: `True`) + - with key `"expanded"` as `True`/`False` can be set that is expanded when GUI is opened (Default: `False`) + +``` +{ + "type": "dict-modifiable", + "object_type": "number", + "input_modifiers": { + "minimum": 0, + "maximum": 300 + }, + "is_group": true, + "key": "templates_mapping", + "label": "Muster - Templates mapping", + "is_file": true +} +``` + +### path-widget +- input for paths, use `path-input` internally +- has 2 input modifiers `"multiplatform"` and `"multipath"` + - `"multiplatform"` - adds `"windows"`, `"linux"` and `"darwin"` path inputs result is dictionary + - `"multipath"` - it is possible to enter multiple paths + - if both are enabled result is dictionary with lists + +``` +{ + "type": "path-widget", + "key": "ffmpeg_path", + "label": "FFmpeg path", + "multiplatform": true, + "multipath": true +} +``` + +## Noninteractive widgets +- have nothing to do with data + +### label +- add label with note or explanations +- it is possible to use html tags inside the label + +``` +{ + "type": "label", + "label": "RED LABEL: Normal label" +} +``` + +### splitter +- visual splitter of items (more divider than splitter) + +``` +{ + "type": "splitter" +} +``` + +## Proxy wrappers +- should wraps multiple inputs only visually +- these does not have `"key"` key and do not allow to have `"is_file"` or `"is_group"` modifiers enabled + +### dict-form +- DEPRECATED + - may be used only in `dict` and `dict-invisible` where is currently used grid layout so form is not needed + - item is kept as still may be used in specific cases +- wraps inputs into form look layout +- should be used only for Pure inputs + +``` +{ + "type": "dict-form", + "children": [ + { + "type": "text", + "key": "deadline_department", + "label": "Deadline apartment" + }, { + "type": "number", + "key": "deadline_priority", + "label": "Deadline priority" + }, { + ... + } + ] +} +``` From b676fc22b6e4991c9c744716d6c354765acfadea Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 17 Sep 2020 13:57:38 +0200 Subject: [PATCH 652/813] order_changed is triggered after new item is added, not before --- .../settings/settings/widgets/item_types.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index ea32d9c79c..9188cd5cb4 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -1384,28 +1384,31 @@ class ListWidget(QtWidgets.QWidget, InputObject): item_widget = ListItem( self.object_type, self.input_modifiers, self, self.inputs_widget ) + + previous_field = None + next_field = None + if row is None: if self.input_fields: - self.input_fields[-1].order_changed() + previous_field = self.input_fields[-1] self.inputs_layout.addWidget(item_widget) self.input_fields.append(item_widget) else: - previous_field = None if row > 0: previous_field = self.input_fields[row - 1] - next_field = None max_index = self.count() if row < max_index: next_field = self.input_fields[row] self.inputs_layout.insertWidget(row, item_widget) self.input_fields.insert(row, item_widget) - if previous_field: - previous_field.order_changed() - if next_field: - next_field.order_changed() + if previous_field: + previous_field.order_changed() + + if next_field: + next_field.order_changed() if is_empty: item_widget.set_as_empty() From 1d08c4db454edc4a4524fb571c75efbffa7b8dd6 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 17 Sep 2020 17:22:28 +0200 Subject: [PATCH 653/813] content of modifiable dictionary can be also highlighted --- .../settings/settings/widgets/item_types.py | 29 +++++++++++++++---- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 4088537a9a..72200f5a95 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -1715,21 +1715,22 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): self.key = input_data["key"] + if input_data.get("highlight_content", False): + content_state = "hightlighted" + bottom_margin = 5 + else: + content_state = "" + bottom_margin = 0 + main_layout = QtWidgets.QHBoxLayout(self) main_layout.setContentsMargins(0, 0, 0, 0) main_layout.setSpacing(0) - content_widget = QtWidgets.QWidget(self) - content_layout = QtWidgets.QVBoxLayout(content_widget) - content_layout.setContentsMargins(CHILD_OFFSET, 3, 0, 3) - if as_widget: - main_layout.addWidget(content_widget) body_widget = None else: body_widget = ExpandingWidget(input_data["label"], self) main_layout.addWidget(body_widget) - body_widget.set_content_widget(content_widget) self.body_widget = body_widget self.label_widget = body_widget.label_widget @@ -1743,6 +1744,22 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): else: body_widget.hide_toolbox(hide_content=False) + if body_widget is None: + content_parent_widget = self + else: + content_parent_widget = body_widget + + content_widget = QtWidgets.QWidget(content_parent_widget) + content_widget.setObjectName("ContentWidget") + content_widget.setProperty("content_state", content_state) + content_layout = QtWidgets.QVBoxLayout(content_widget) + content_layout.setContentsMargins(CHILD_OFFSET, 3, 0, bottom_margin) + + if body_widget is None: + main_layout.addWidget(content_widget) + else: + body_widget.set_content_widget(content_widget) + self.body_widget = body_widget self.content_widget = content_widget self.content_layout = content_layout From ef6c68e89431456ecf4430f9561dae80e64c453a Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 17 Sep 2020 17:24:39 +0200 Subject: [PATCH 654/813] align labels in dict and dict-invisible to top right --- pype/tools/settings/settings/widgets/item_types.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 72200f5a95..c8d558e044 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -2037,7 +2037,10 @@ class DictWidget(QtWidgets.QWidget, SettingObject): label = child_configuration.get("label") if label is not None: label_widget = QtWidgets.QLabel(label, self) - self.content_layout.addWidget(label_widget, row, 0, 1, 1) + self.content_layout.addWidget( + label_widget, row, 0, 1, 1, + alignment=QtCore.Qt.AlignRight | QtCore.Qt.AlignTop + ) item = klass(child_configuration, self, label_widget=label_widget) item.value_changed.connect(self._on_value_change) @@ -2338,7 +2341,10 @@ class DictInvisible(QtWidgets.QWidget, SettingObject): label = child_configuration.get("label") if label is not None: label_widget = QtWidgets.QLabel(label, self) - self.content_layout.addWidget(label_widget, row, 0, 1, 1) + self.content_layout.addWidget( + label_widget, row, 0, 1, 1, + alignment=QtCore.Qt.AlignRight | QtCore.Qt.AlignTop + ) item = klass(child_configuration, self, label_widget=label_widget) item.value_changed.connect(self._on_value_change) From 4934f65432f9e030c88b346adab7571b2b06d3a8 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 17 Sep 2020 17:26:00 +0200 Subject: [PATCH 655/813] implemented basic dict-item item type --- pype/tools/settings/settings/style/style.css | 6 + .../settings/settings/widgets/item_types.py | 116 ++++++++++++++++++ 2 files changed, 122 insertions(+) diff --git a/pype/tools/settings/settings/style/style.css b/pype/tools/settings/settings/style/style.css index 38f69fef50..221f297219 100644 --- a/pype/tools/settings/settings/style/style.css +++ b/pype/tools/settings/settings/style/style.css @@ -152,6 +152,12 @@ QPushButton[btn-type="expand-toggle"] { background: #141a1f; } +#DictItemWidgetBody{ + background: transparent; + border: 2px solid #cccccc; + border-radius: 5px; +} + #SplitterItem { background-color: #1d272f; } diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index c8d558e044..31455ecd98 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -1149,6 +1149,121 @@ class RawJsonWidget(QtWidgets.QWidget, InputObject): return self.text_input.json_value() +class DictItemWidget(QtWidgets.QWidget, SettingObject): + default_input_value = True + value_changed = QtCore.Signal(object) + + def __init__( + self, input_data, parent, + as_widget=False, label_widget=None, parent_widget=None + ): + if parent_widget is None: + parent_widget = parent + super(DictItemWidget, self).__init__(parent_widget) + + self.initial_attributes(input_data, parent, as_widget) + + if not self._as_widget: + raise TypeError("{} can be used only as widget.".format( + self.__class__.__name__ + )) + + self.input_fields = [] + + body = QtWidgets.QWidget(self) + body.setObjectName("DictItemWidgetBody") + + content_layout = QtWidgets.QGridLayout(body) + content_layout.setContentsMargins(5, 5, 5, 5) + self.content_layout = content_layout + + layout = QtWidgets.QHBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(5) + layout.addWidget(body) + + for child_configuration in input_data["children"]: + self.add_children_gui(child_configuration) + + def add_children_gui(self, child_configuration): + item_type = child_configuration["type"] + klass = TypeToKlass.types.get(item_type) + + row = self.content_layout.rowCount() + if not getattr(klass, "is_input_type", False): + item = klass(child_configuration, self) + self.content_layout.addWidget(item, row, 0, 1, 2) + return item + + label_widget = None + if not klass.expand_in_grid: + label = child_configuration.get("label") + if label is not None: + label_widget = QtWidgets.QLabel(label, self) + self.content_layout.addWidget( + label_widget, row, 0, 1, 1, + alignment=QtCore.Qt.AlignRight | QtCore.Qt.AlignTop + ) + + item = klass(child_configuration, self, label_widget=label_widget) + item.value_changed.connect(self._on_value_change) + + if label_widget: + self.content_layout.addWidget(item, row, 1, 1, 1) + else: + self.content_layout.addWidget(item, row, 0, 1, 2) + + self.input_fields.append(item) + return item + + def hierarchical_style_update(self): + print("hierarchical_style_update") + + def _on_value_change(self, item=None): + print("_on_value_change") + + def set_value(self, value): + # Ignore value change because if `self.isChecked()` has same + # value as `value` the `_on_value_change` is not triggered + self.checkbox.setChecked(value) + + def update_style(self): + if self._as_widget: + if not self.isEnabled(): + state = self.style_state(False, False, False, False) + else: + state = self.style_state( + False, + self._is_invalid, + False, + self._is_modified + ) + else: + state = self.style_state( + self.has_studio_override, + self.is_invalid, + self.is_overriden, + self.is_modified + ) + if self._state == state: + return + + if self._as_widget: + property_name = "input-state" + else: + property_name = "state" + + self.label_widget.setProperty(property_name, state) + self.label_widget.style().polish(self.label_widget) + self._state = state + + def item_value(self): + output = {} + for input_field in self.input_fields: + output.update(input_field.config_value()) + return output + + class ListItem(QtWidgets.QWidget, SettingObject): _btn_size = 20 value_changed = QtCore.Signal(object) @@ -3159,6 +3274,7 @@ TypeToKlass.types["path-input"] = PathInputWidget TypeToKlass.types["raw-json"] = RawJsonWidget TypeToKlass.types["list"] = ListWidget TypeToKlass.types["dict-modifiable"] = ModifiableDict +TypeToKlass.types["dict-item"] = DictItemWidget TypeToKlass.types["dict"] = DictWidget TypeToKlass.types["dict-invisible"] = DictInvisible TypeToKlass.types["path-widget"] = PathWidget From 5621f029d0a55f64c7aa924710ea17479009f956 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 17 Sep 2020 17:31:25 +0200 Subject: [PATCH 656/813] hode item in ListItem if is set as empty --- .../settings/settings/widgets/item_types.py | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 31455ecd98..ec75272f07 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -1307,9 +1307,6 @@ class ListItem(QtWidgets.QWidget, SettingObject): self.up_btn.setProperty("btn-type", "tool-item") self.down_btn.setProperty("btn-type", "tool-item") - layout.addWidget(self.add_btn, 0) - layout.addWidget(self.remove_btn, 0) - self.add_btn.clicked.connect(self._on_add_clicked) self.remove_btn.clicked.connect(self._on_remove_clicked) self.up_btn.clicked.connect(self._on_up_clicked) @@ -1322,7 +1319,15 @@ class ListItem(QtWidgets.QWidget, SettingObject): as_widget=True, label_widget=None ) + + self.spacer_widget = QtWidgets.QWidget(self) + self.spacer_widget.setVisible(False) + + layout.addWidget(self.add_btn, 0) + layout.addWidget(self.remove_btn, 0) + layout.addWidget(self.value_input, 1) + layout.addWidget(self.spacer_widget, 1) layout.addWidget(self.up_btn, 0) layout.addWidget(self.down_btn, 0) @@ -1330,8 +1335,11 @@ class ListItem(QtWidgets.QWidget, SettingObject): self.value_input.value_changed.connect(self._on_value_change) def set_as_empty(self, is_empty=True): - self.value_input.setEnabled(not is_empty) - self.remove_btn.setEnabled(not is_empty) + self.spacer_widget.setVisible(is_empty) + self.value_input.setVisible(not is_empty) + self.remove_btn.setVisible(not is_empty) + self.up_btn.setVisible(not is_empty) + self.down_btn.setVisible(not is_empty) self.order_changed() self._on_value_change() @@ -1364,7 +1372,7 @@ class ListItem(QtWidgets.QWidget, SettingObject): return len(self._parent.input_fields) def _on_add_clicked(self): - if self.value_input.isEnabled(): + if self.value_input.isVisible(): self._parent.add_row(row=self.row() + 1) else: self.set_as_empty(False) From cd41394f1cf9efec6799a852655f32564888e7aa Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 17 Sep 2020 17:40:26 +0200 Subject: [PATCH 657/813] keep visible minus button and added same feature to modifable dict --- .../tools/settings/settings/widgets/item_types.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index ec75272f07..293b2f5503 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -1321,6 +1321,7 @@ class ListItem(QtWidgets.QWidget, SettingObject): ) self.spacer_widget = QtWidgets.QWidget(self) + self.spacer_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground) self.spacer_widget.setVisible(False) layout.addWidget(self.add_btn, 0) @@ -1337,7 +1338,7 @@ class ListItem(QtWidgets.QWidget, SettingObject): def set_as_empty(self, is_empty=True): self.spacer_widget.setVisible(is_empty) self.value_input.setVisible(not is_empty) - self.remove_btn.setVisible(not is_empty) + self.remove_btn.setEnabled(not is_empty) self.up_btn.setVisible(not is_empty) self.down_btn.setVisible(not is_empty) self.order_changed() @@ -1692,9 +1693,14 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): self.add_btn.setProperty("btn-type", "tool-item") self.remove_btn.setProperty("btn-type", "tool-item") + self.spacer_widget = QtWidgets.QWidget(self) + self.spacer_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground) + self.spacer_widget.setVisible(False) + layout.addWidget(self.add_btn, 0) layout.addWidget(self.remove_btn, 0) layout.addWidget(self.key_input, 0) + layout.addWidget(self.spacer_widget, 1) layout.addWidget(self.value_input, 1) self.setFocusProxy(self.value_input) @@ -1713,7 +1719,7 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): return self.key_input.text() def _is_enabled(self): - return self.key_input.isEnabled() + return self.key_input.isVisible() def is_key_invalid(self): if not self._is_enabled(): @@ -1759,9 +1765,10 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): self._parent.remove_row(self) def set_as_empty(self, is_empty=True): - self.key_input.setEnabled(not is_empty) - self.value_input.setEnabled(not is_empty) + self.key_input.setVisible(not is_empty) + self.value_input.setVisible(not is_empty) self.remove_btn.setEnabled(not is_empty) + self.spacer_widget.setVisible(is_empty) self._on_value_change() @property From 04977d223e2cb301c4f6bef5b76ba39a191fe374 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 17 Sep 2020 17:45:12 +0200 Subject: [PATCH 658/813] hide up/down btns if there is only one item --- pype/tools/settings/settings/widgets/item_types.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 293b2f5503..f0a26227a1 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -1348,10 +1348,15 @@ class ListItem(QtWidgets.QWidget, SettingObject): row = self.row() parent_row_count = self.parent_rows_count() if parent_row_count == 1: - self.up_btn.setEnabled(False) - self.down_btn.setEnabled(False) + self.up_btn.setVisible(False) + self.down_btn.setVisible(False) + return - elif row == 0: + if not self.up_btn.isVisible(): + self.up_btn.setVisible(True) + self.down_btn.setVisible(True) + + if row == 0: self.up_btn.setEnabled(False) self.down_btn.setEnabled(True) From 4fa53fd2e3c996ceba410af4f7af140a8c4f1f56 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 17 Sep 2020 18:55:17 +0200 Subject: [PATCH 659/813] added few attributes to bases --- pype/tools/settings/settings/widgets/base.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pype/tools/settings/settings/widgets/base.py b/pype/tools/settings/settings/widgets/base.py index dbcc380daf..423380d54c 100644 --- a/pype/tools/settings/settings/widgets/base.py +++ b/pype/tools/settings/settings/widgets/base.py @@ -34,6 +34,8 @@ class SystemWidget(QtWidgets.QWidget): is_overidable = False has_studio_override = _has_studio_override = False is_overriden = _is_overriden = False + as_widget = _as_widget = False + any_parent_as_widget = _any_parent_as_widget = False is_group = _is_group = False any_parent_is_group = _any_parent_is_group = False @@ -396,6 +398,8 @@ class ProjectListWidget(QtWidgets.QWidget): class ProjectWidget(QtWidgets.QWidget): has_studio_override = _has_studio_override = False is_overriden = _is_overriden = False + as_widget = _as_widget = False + any_parent_as_widget = _any_parent_as_widget = False is_group = _is_group = False any_parent_is_group = _any_parent_is_group = False From c88e48f1af61d6f9c8598ffaa2c67739c8831e6d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 17 Sep 2020 18:55:52 +0200 Subject: [PATCH 660/813] removed not used method --- pype/tools/settings/settings/widgets/item_types.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index f0a26227a1..d09fd3d1ee 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -502,10 +502,6 @@ class SettingObject: "Method `item_value` not implemented!" ) - def studio_value(self): - """Output for saving changes or overrides.""" - return {self.key: self.item_value()} - class InputObject(SettingObject): """Class for inputs with pre-implemented methods. From 3544d819051465d911edeff4fa49d0f054893b92 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 17 Sep 2020 19:23:14 +0200 Subject: [PATCH 661/813] added any_parent_as_widget attribute --- .../settings/settings/widgets/item_types.py | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index d09fd3d1ee..07f57c994d 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -46,6 +46,7 @@ class SettingObject: self._as_widget = False self._is_group = False + self._any_parent_as_widget = None self._any_parent_is_group = None # Parent input @@ -81,6 +82,12 @@ class SettingObject: # TODO not implemented yet self._is_nullable = input_data.get("is_nullable", False) + any_parent_as_widget = parent.as_widget + if not any_parent_as_widget: + any_parent_as_widget = parent.any_parent_as_widget + + self._any_parent_as_widget = any_parent_as_widget + any_parent_is_group = parent.is_group if not any_parent_is_group: any_parent_is_group = parent.any_parent_is_group @@ -130,6 +137,34 @@ class SettingObject: """ return self._has_studio_override or self._parent.has_studio_override + @property + def as_widget(self): + """Item is used as widget in parent item. + + Returns: + bool + + """ + return self._as_widget + + @property + def any_parent_as_widget(self): + """Any parent of item is used as widget. + + Attribute holding this information is set during creation and + stored to `_any_parent_as_widget`. + + Why is this information useful: If any parent is used as widget then + modifications and override are not important for whole part. + + Returns: + bool + + """ + if self._any_parent_as_widget is None: + return super(SettingObject, self).any_parent_as_widget + return self._any_parent_as_widget + @property def is_group(self): """Item represents key that can be overriden. From ccfec4759f8202be4c49ff8f345d1d3757afd9f1 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 17 Sep 2020 19:24:17 +0200 Subject: [PATCH 662/813] added attribute is empty to list item --- pype/tools/settings/settings/widgets/item_types.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 07f57c994d..b013dd24f7 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -1306,6 +1306,7 @@ class ListItem(QtWidgets.QWidget, SettingObject): self._parent = config_parent self._any_parent_is_group = True + self._is_empty = False layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) @@ -1367,6 +1368,8 @@ class ListItem(QtWidgets.QWidget, SettingObject): self.value_input.value_changed.connect(self._on_value_change) def set_as_empty(self, is_empty=True): + self._is_empty = is_empty + self.spacer_widget.setVisible(is_empty) self.value_input.setVisible(not is_empty) self.remove_btn.setEnabled(not is_empty) @@ -1409,7 +1412,7 @@ class ListItem(QtWidgets.QWidget, SettingObject): return len(self._parent.input_fields) def _on_add_clicked(self): - if self.value_input.isVisible(): + if self._is_empty: self._parent.add_row(row=self.row() + 1) else: self.set_as_empty(False) @@ -1426,7 +1429,7 @@ class ListItem(QtWidgets.QWidget, SettingObject): self._parent.swap_rows(row, row + 1) def config_value(self): - if self.value_input.isEnabled(): + if not self._is_empty: return self.value_input.item_value() return NOT_SET From e0e7b29f4b2d4494ff6e3ad918d45359ae2de179 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 17 Sep 2020 19:27:18 +0200 Subject: [PATCH 663/813] added _is_empty to ModifiableDictItem too --- .../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 b013dd24f7..71d6d2370d 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -1706,6 +1706,7 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): self._set_default_attributes() self._parent = config_parent + self._is_empty = False self.is_key_duplicated = False layout = QtWidgets.QHBoxLayout(self) @@ -1757,11 +1758,8 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): def key_value(self): return self.key_input.text() - def _is_enabled(self): - return self.key_input.isVisible() - def is_key_invalid(self): - if not self._is_enabled(): + if self._is_empty: return False if self.key_value() == "": @@ -1795,15 +1793,17 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): return self._parent.is_group def on_add_clicked(self): - if self._is_enabled(): - self._parent.add_row(row=self.row() + 1) - else: + if self._is_empty: self.set_as_empty(False) + else: + self._parent.add_row(row=self.row() + 1) def on_remove_clicked(self): self._parent.remove_row(self) def set_as_empty(self, is_empty=True): + self._is_empty = is_empty + self.key_input.setVisible(not is_empty) self.value_input.setVisible(not is_empty) self.remove_btn.setEnabled(not is_empty) @@ -1830,13 +1830,13 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): @property def is_invalid(self): - if not self._is_enabled(): + if self._is_empty: return False return self.is_key_invalid() or self.value_input.is_invalid def update_style(self): state = "" - if self._is_enabled(): + if not self._is_empty: if self.is_key_invalid(): state = "invalid" elif self.is_key_modified(): @@ -1854,9 +1854,9 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): return {key: value} def config_value(self): - if self._is_enabled(): - return self.item_value() - return {} + if self._is_empty: + return {} + return self.item_value() def mouseReleaseEvent(self, event): return QtWidgets.QWidget.mouseReleaseEvent(self, event) From 180626ec2d0072779a75a9e678cba5bb388f619c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 17 Sep 2020 19:27:44 +0200 Subject: [PATCH 664/813] implemented few methods in dict-item --- .../settings/settings/widgets/item_types.py | 44 ++++--------------- 1 file changed, 9 insertions(+), 35 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 71d6d2370d..875965db32 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -1248,45 +1248,19 @@ class DictItemWidget(QtWidgets.QWidget, SettingObject): return item def hierarchical_style_update(self): - print("hierarchical_style_update") + for input_field in self.input_fields: + input_field.hierarchical_style_update() def _on_value_change(self, item=None): - print("_on_value_change") + self.value_changed.emit(self) - def set_value(self, value): - # Ignore value change because if `self.isChecked()` has same - # value as `value` the `_on_value_change` is not triggered - self.checkbox.setChecked(value) + def update_default_values(self, parent_values): + for input_field in self.input_fields: + input_field.update_default_values(parent_values) - def update_style(self): - if self._as_widget: - if not self.isEnabled(): - state = self.style_state(False, False, False, False) - else: - state = self.style_state( - False, - self._is_invalid, - False, - self._is_modified - ) - else: - state = self.style_state( - self.has_studio_override, - self.is_invalid, - self.is_overriden, - self.is_modified - ) - if self._state == state: - return - - if self._as_widget: - property_name = "input-state" - else: - property_name = "state" - - self.label_widget.setProperty(property_name, state) - self.label_widget.style().polish(self.label_widget) - self._state = state + def update_studio_values(self, parent_values): + for input_field in self.input_fields: + input_field.update_studio_values(parent_values) def item_value(self): output = {} From 127426e7864b03459c22df17eea7d6b8fb2d24f2 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 17 Sep 2020 19:31:22 +0200 Subject: [PATCH 665/813] added apply_overrides to widget item --- pype/tools/settings/settings/widgets/item_types.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 875965db32..d4faaedcf6 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -1262,6 +1262,10 @@ class DictItemWidget(QtWidgets.QWidget, SettingObject): for input_field in self.input_fields: input_field.update_studio_values(parent_values) + def apply_overrides(self, parent_values): + for input_field in self.input_fields: + input_field.apply_overrides(parent_values) + def item_value(self): output = {} for input_field in self.input_fields: From 02ba2a6f16b5304b2a9c1b724adbdf801f77ef98 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 17 Sep 2020 19:32:17 +0200 Subject: [PATCH 666/813] using more attribute as_widget and any_parent_as_widget --- .../settings/settings/widgets/item_types.py | 78 +++++++++++++++---- 1 file changed, 61 insertions(+), 17 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index d4faaedcf6..9275ccbecb 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -198,6 +198,9 @@ class SettingObject: @property def is_modified(self): """Has object any changes that require saving.""" + if self.any_parent_as_widget: + return self._is_modified + if self._is_modified or self.defaults_not_set: return True @@ -626,10 +629,11 @@ class InputObject(SettingObject): if self.ignore_value_changes: return - if self.is_overidable: - self._is_overriden = True - else: - self._has_studio_override = True + if not self.any_parent_as_widget: + if self.is_overidable: + self._is_overriden = True + else: + self._has_studio_override = True if self._is_invalid: self._is_modified = True @@ -645,12 +649,18 @@ class InputObject(SettingObject): self.value_changed.emit(self) def studio_overrides(self): - if not self.has_studio_override: + if ( + not (self.as_widget or self.any_parent_as_widget) + and not self.has_studio_override + ): return NOT_SET, False return self.config_value(), self.is_group def overrides(self): - if not self.is_overriden: + if ( + not (self.as_widget or self.any_parent_as_widget) + and not self.is_overriden + ): return NOT_SET, False return self.config_value(), self.is_group @@ -1213,6 +1223,8 @@ class DictItemWidget(QtWidgets.QWidget, SettingObject): layout.setSpacing(5) layout.addWidget(body) + self.label_widget = label_widget + for child_configuration in input_data["children"]: self.add_children_gui(child_configuration) @@ -1345,6 +1357,14 @@ class ListItem(QtWidgets.QWidget, SettingObject): self.value_input.value_changed.connect(self._on_value_change) + @property + def as_widget(self): + return self._parent.as_widget + + @property + def any_parent_as_widget(self): + return self.as_widget or self._parent.any_parent_as_widget + def set_as_empty(self, is_empty=True): self._is_empty = is_empty @@ -1684,6 +1704,13 @@ class ModifiableDictItem(QtWidgets.QWidget, SettingObject): self._set_default_attributes() self._parent = config_parent + any_parent_as_widget = config_parent.as_widget + if not any_parent_as_widget: + any_parent_as_widget = config_parent.any_parent_as_widget + + self._any_parent_as_widget = any_parent_as_widget + self._any_parent_is_group = True + self._is_empty = False self.is_key_duplicated = False @@ -2303,7 +2330,7 @@ class DictWidget(QtWidgets.QWidget, SettingObject): if self.ignore_value_changes: return - if self.is_group: + if self.is_group and not self.any_parent_as_widget: if self.is_overidable: self._is_overriden = True else: @@ -2406,7 +2433,11 @@ class DictWidget(QtWidgets.QWidget, SettingObject): return output def studio_overrides(self): - if not self.has_studio_override and not self.child_has_studio_override: + if ( + not (self.as_widget or self.any_parent_as_widget) + and not self.has_studio_override + and not self.child_has_studio_override + ): return NOT_SET, False values = {} @@ -2556,7 +2587,7 @@ class DictInvisible(QtWidgets.QWidget, SettingObject): if self.ignore_value_changes: return - if self.is_group: + if self.is_group and not self.any_parent_as_widget: if self.is_overidable: self._is_overriden = True else: @@ -2653,7 +2684,11 @@ class DictInvisible(QtWidgets.QWidget, SettingObject): self._was_overriden = bool(self._is_overriden) def studio_overrides(self): - if not self.has_studio_override and not self.child_has_studio_override: + if ( + not (self.as_widget or self.any_parent_as_widget) + and not self.has_studio_override + and not self.child_has_studio_override + ): return NOT_SET, False values = {} @@ -2916,10 +2951,11 @@ class PathWidget(QtWidgets.QWidget, SettingObject): if self.ignore_value_changes: return - if self.is_overidable: - self._is_overriden = True - else: - self._has_studio_override = True + if not self.any_parent_as_widget: + if self.is_overidable: + self._is_overriden = True + else: + self._has_studio_override = True if self._is_invalid: self._is_modified = True @@ -3046,7 +3082,11 @@ class PathWidget(QtWidgets.QWidget, SettingObject): return output def studio_overrides(self): - if not self.has_studio_override and not self.child_has_studio_override: + if ( + not (self.as_widget or self.any_parent_as_widget) + and not self.has_studio_override + and not self.child_has_studio_override + ): return NOT_SET, False value = self.item_value() @@ -3236,7 +3276,11 @@ class DictFormWidget(QtWidgets.QWidget, SettingObject): return self.item_value() def studio_overrides(self): - if not self.has_studio_override and not self.child_has_studio_override: + if ( + not (self.as_widget or self.any_parent_as_widget) + and not self.has_studio_override + and not self.child_has_studio_override + ): return NOT_SET, False values = {} @@ -3310,7 +3354,7 @@ TypeToKlass.types["dict-item"] = DictItemWidget TypeToKlass.types["dict"] = DictWidget TypeToKlass.types["dict-invisible"] = DictInvisible TypeToKlass.types["path-widget"] = PathWidget -TypeToKlass.types["dict-form"] = DictFormWidget +TypeToKlass.types["form"] = DictFormWidget TypeToKlass.types["label"] = LabelWidget TypeToKlass.types["splitter"] = SplitterWidget From 2517f13266d3ccffed41e1323c31daf6b084b8f9 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 17 Sep 2020 19:32:47 +0200 Subject: [PATCH 667/813] ExtractReview plugin converted to use new DictItemWidget --- .../projects_schema/1_plugins_gui_schema.json | 89 ++++++++++++++++++- 1 file changed, 87 insertions(+), 2 deletions(-) diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/1_plugins_gui_schema.json b/pype/tools/settings/settings/gui_schemas/projects_schema/1_plugins_gui_schema.json index b2d7914c84..f70495017e 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/1_plugins_gui_schema.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/1_plugins_gui_schema.json @@ -169,9 +169,94 @@ "key": "enabled", "label": "Enabled" }, { - "type": "raw-json", + "type": "list", "key": "profiles", - "label": "Profiles" + "label": "Profiles", + "object_type": "dict-item", + "input_modifiers": { + "children": [ + { + "key": "families", + "label": "Families", + "type": "list", + "object_type": "text" + }, { + "key": "hosts", + "label": "Hosts", + "type": "list", + "object_type": "text" + }, { + "type": "splitter" + }, { + "key": "outputs", + "label": "Output Definitions", + "type": "dict-modifiable", + "highlight_content": true, + "object_type": "dict-item", + "input_modifiers": { + "children": [ + { + "key": "ext", + "label": "Output extension", + "type": "text" + }, { + "key": "tags", + "label": "Tags", + "type": "list", + "object_type": "text" + }, { + "key": "ffmpeg_args", + "label": "FFmpeg arguments", + "type": "dict", + "highlight_content": true, + "children": [ + { + "key": "video_filters", + "label": "Video filters", + "type": "list", + "object_type": "text" + }, { + "type": "splitter" + }, { + "key": "audio_filters", + "label": "Audio filters", + "type": "list", + "object_type": "text" + }, { + "type": "splitter" + }, { + "key": "input", + "label": "Input arguments", + "type": "list", + "object_type": "text" + }, { + "type": "splitter" + }, { + "key": "output", + "label": "Output arguments", + "type": "list", + "object_type": "text" + } + ] + }, { + "key": "filter", + "label": "Additional output filtering", + "type": "dict", + "highlight_content": true, + "children": [ + { + "key": "families", + "label": "Families", + "type": "list", + "object_type": "text" + } + ] + } + ] + } + } + ] + } } ] }, { From fae0b851281b5e2fc55085c6f3cff7ee2b262c6b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 17 Sep 2020 19:34:36 +0200 Subject: [PATCH 668/813] fixed list item again --- pype/tools/settings/settings/widgets/item_types.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 9275ccbecb..ea93acddb2 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -1411,9 +1411,9 @@ class ListItem(QtWidgets.QWidget, SettingObject): def _on_add_clicked(self): if self._is_empty: - self._parent.add_row(row=self.row() + 1) - else: self.set_as_empty(False) + else: + self._parent.add_row(row=self.row() + 1) def _on_remove_clicked(self): self._parent.remove_row(self) From db928a85baf01b488ec5c2117a9f2e9055c53641 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 17 Sep 2020 19:37:28 +0200 Subject: [PATCH 669/813] make sure inputs won't affect it's potential children o remove project overrides --- pype/tools/settings/settings/widgets/item_types.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index ea93acddb2..d1fd219258 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -668,6 +668,8 @@ class InputObject(SettingObject): self.update_style() def remove_overrides(self): + self._is_overriden = False + self._is_modified = False if self.has_studio_override: self.set_value(self.studio_value) else: From bad080ee3ac877ddade28c3441699ed0807b5f19 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 18 Sep 2020 09:53:04 +0200 Subject: [PATCH 670/813] modified defaults --- .../plugins/global/publish.json | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/pype/settings/defaults/project_settings/plugins/global/publish.json b/pype/settings/defaults/project_settings/plugins/global/publish.json index b946ac4b32..0a7f6fbf3d 100644 --- a/pype/settings/defaults/project_settings/plugins/global/publish.json +++ b/pype/settings/defaults/project_settings/plugins/global/publish.json @@ -19,30 +19,30 @@ "hosts": [], "outputs": { "h264": { - "filter": { - "families": [ - "render", - "review", - "ftrack" - ] - }, "ext": "mp4", + "tags": [ + "burnin", + "ftrackreview" + ], "ffmpeg_args": { + "video_filters": [], + "audio_filters": [], "input": [ "-gamma 2.2" ], - "video_filters": [], - "audio_filters": [], "output": [ "-pix_fmt yuv420p", "-crf 18", "-intra" ] }, - "tags": [ - "burnin", - "ftrackreview" - ] + "filter": { + "families": [ + "render", + "review", + "ftrack" + ] + } } } } From dba17756003a1f554f07ce2a90b70da7d2192231 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 18 Sep 2020 11:14:38 +0200 Subject: [PATCH 671/813] ApplicationAction has pype logger --- pype/lib.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/pype/lib.py b/pype/lib.py index 601c85f521..73b47b8594 100644 --- a/pype/lib.py +++ b/pype/lib.py @@ -19,7 +19,7 @@ from abc import ABCMeta, abstractmethod from avalon import io, pipeline import six import avalon.api -from .api import config, Anatomy +from .api import config, Anatomy, Logger log = logging.getLogger(__name__) @@ -1622,7 +1622,7 @@ class ApplicationAction(avalon.api.Action): parsed application `.toml` this can launch the application. """ - + _log = None config = None group = None variant = None @@ -1632,6 +1632,12 @@ class ApplicationAction(avalon.api.Action): "AVALON_TASK" ) + @property + def log(self): + if self._log is None: + self._log = Logger().get_logger(self.__class__.__name__) + return self._log + def is_compatible(self, session): for key in self.required_session_keys: if key not in session: From b326855be0656047ac508dad3a118c36a212dbb9 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 18 Sep 2020 11:15:04 +0200 Subject: [PATCH 672/813] Application also change ftrack status and trigger ftrack timer --- pype/lib.py | 161 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 160 insertions(+), 1 deletion(-) diff --git a/pype/lib.py b/pype/lib.py index 73b47b8594..6fa204b379 100644 --- a/pype/lib.py +++ b/pype/lib.py @@ -1650,6 +1650,165 @@ class ApplicationAction(avalon.api.Action): project_name = session["AVALON_PROJECT"] asset_name = session["AVALON_ASSET"] task_name = session["AVALON_TASK"] - return launch_application( + launch_application( project_name, asset_name, task_name, self.name ) + + self._ftrack_after_launch_procedure( + project_name, asset_name, task_name + ) + + def _ftrack_after_launch_procedure( + self, project_name, asset_name, task_name + ): + # TODO move to launch hook + required_keys = ("FTRACK_SERVER", "FTRACK_API_USER", "FTRACK_API_KEY") + for key in required_keys: + if not os.environ.get(key): + self.log.debug(( + "Missing required environment \"{}\"" + " for Ftrack after launch procedure." + ).format(key)) + return + + try: + import ftrack_api + session = ftrack_api.Session(auto_connect_event_hub=True) + self.log.debug("Ftrack session created") + except Exception: + self.log.warning("Couldn't create Ftrack session") + return + + try: + entity = self._find_ftrack_task_entity( + session, project_name, asset_name, task_name + ) + self._ftrack_status_change(session, entity, project_name) + self._start_timer(session, entity, ftrack_api) + except Exception: + self.log.warning( + "Couldn't finish Ftrack procedure.", exc_info=True + ) + return + + finally: + session.close() + + def _find_ftrack_task_entity( + self, session, project_name, asset_name, task_name + ): + project_entity = session.query( + "Project where full_name is \"{}\"".format(project_name) + ).first() + if not project_entity: + self.log.warning( + "Couldn't find project \"{}\" in Ftrack.".format(project_name) + ) + return + + potential_task_entities = session.query(( + "TypedContext where parent.name is \"{}\" and project_id is \"{}\"" + ).format(asset_name, project_entity["id"])).all() + filtered_entities = [] + for _entity in potential_task_entities: + if ( + _entity.entity_type.lower() == "task" + and _entity["name"] == task_name + ): + filtered_entities.append(_entity) + + if not filtered_entities: + self.log.warning(( + "Couldn't find task \"{}\" under parent \"{}\" in Ftrack." + ).format(task_name, asset_name)) + return + + if len(filtered_entities) > 1: + self.log.warning(( + "Found more than one task \"{}\"" + " under parent \"{}\" in Ftrack." + ).format(task_name, asset_name)) + return + + return filtered_entities[0] + + def _ftrack_status_change(self, session, entity, project_name): + presets = config.get_presets(project_name)["ftrack"]["ftrack_config"] + statuses = presets.get("status_update") + if not statuses: + return + + actual_status = entity["status"]["name"].lower() + already_tested = set() + ent_path = "/".join( + [ent["name"] for ent in entity["link"]] + ) + while True: + next_status_name = None + for key, value in statuses.items(): + if key in already_tested: + continue + if actual_status in value or "_any_" in value: + if key != "_ignore_": + next_status_name = key + already_tested.add(key) + break + already_tested.add(key) + + if next_status_name is None: + break + + try: + query = "Status where name is \"{}\"".format( + next_status_name + ) + status = session.query(query).one() + + entity["status"] = status + session.commit() + self.log.debug("Changing status to \"{}\" <{}>".format( + next_status_name, ent_path + )) + break + + except Exception: + session.rollback() + msg = ( + "Status \"{}\" in presets wasn't found" + " on Ftrack entity type \"{}\"" + ).format(next_status_name, entity.entity_type) + self.log.warning(msg) + + def _start_timer(self, session, entity, _ftrack_api): + self.log.debug("Triggering timer start.") + + user_entity = session.query("User where username is \"{}\"".format( + os.environ["FTRACK_API_USER"] + )).first() + if not user_entity: + self.log.warning( + "Couldn't find user with username \"{}\" in Ftrack".format( + os.environ["FTRACK_API_USER"] + ) + ) + return + + source = { + "user": { + "id": user_entity["id"], + "username": user_entity["username"] + } + } + event_data = { + "actionIdentifier": "start.timer", + "selection": [{"entityId": entity["id"], "entityType": "task"}] + } + session.event_hub.publish( + _ftrack_api.event.base.Event( + topic="ftrack.action.launch", + data=event_data, + source=source + ), + on_error="ignore" + ) + self.log.debug("Timer start triggered successfully.") From cfcd24318fc6cd200011021205c7279223507043 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 18 Sep 2020 11:48:25 +0200 Subject: [PATCH 673/813] avoid bugs when same event is more than once stored in mongo --- pype/modules/ftrack/ftrack_server/lib.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pype/modules/ftrack/ftrack_server/lib.py b/pype/modules/ftrack/ftrack_server/lib.py index acf31ab437..ee6b1216dc 100644 --- a/pype/modules/ftrack/ftrack_server/lib.py +++ b/pype/modules/ftrack/ftrack_server/lib.py @@ -205,10 +205,16 @@ class ProcessEventHub(SocketBaseEventHub): else: try: self._handle(event) + + mongo_id = event["data"].get("_event_mongo_id") + if mongo_id is None: + continue + self.dbcon.update_one( - {"id": event["id"]}, + {"_id": mongo_id}, {"$set": {"pype_data.is_processed": True}} ) + except pymongo.errors.AutoReconnect: self.pypelog.error(( "Mongo server \"{}\" is not responding, exiting." @@ -244,6 +250,7 @@ class ProcessEventHub(SocketBaseEventHub): } try: event = ftrack_api.event.base.Event(**new_event_data) + event["data"]["_event_mongo_id"] = event_data["_id"] except Exception: self.logger.exception(L( 'Failed to convert payload into event: {0}', From 384c2382063b050f0547f0499f5647992d33f9b1 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 18 Sep 2020 12:20:27 +0200 Subject: [PATCH 674/813] ListWidget can be now used as widget in other item types --- .../tools/settings/settings/widgets/item_types.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index d1fd219258..68cebf6642 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -1479,17 +1479,17 @@ class ListWidget(QtWidgets.QWidget, InputObject): self.object_type = input_data["object_type"] self.input_modifiers = input_data.get("input_modifiers") or {} - self.key = input_data["key"] - self.input_fields = [] layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 5) layout.setSpacing(5) - if not label_widget: - label_widget = QtWidgets.QLabel(input_data["label"], self) - layout.addWidget(label_widget, alignment=QtCore.Qt.AlignTop) + if not self.as_widget: + self.key = input_data["key"] + if not label_widget: + label_widget = QtWidgets.QLabel(input_data["label"], self) + layout.addWidget(label_widget, alignment=QtCore.Qt.AlignTop) self.label_widget = label_widget @@ -1684,8 +1684,9 @@ class ListWidget(QtWidgets.QWidget, InputObject): if self._state == state: return - self.label_widget.setProperty("state", state) - self.label_widget.style().polish(self.label_widget) + if self.label_widget: + self.label_widget.setProperty("state", state) + self.label_widget.style().polish(self.label_widget) def item_value(self): output = [] From 9a5007a9aad24d788aad04bd3cf50cdc4529cf05 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 18 Sep 2020 12:21:06 +0200 Subject: [PATCH 675/813] changed `dict-form` to `form` in examples --- .../settings/settings/gui_schemas/system_schema/1_examples.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 a884dcb31e..73f72c875c 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 @@ -207,7 +207,7 @@ "label": "Inputs with form", "children": [ { - "type": "dict-form", + "type": "form", "children": [ { "type": "text", From 5b867b2def2ea19ca25d3c23f6c67c3343b70d1b Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Fri, 18 Sep 2020 12:23:24 +0200 Subject: [PATCH 676/813] change dict-form to form in examples --- .../settings/settings/gui_schemas/system_schema/1_examples.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 a884dcb31e..73f72c875c 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 @@ -207,7 +207,7 @@ "label": "Inputs with form", "children": [ { - "type": "dict-form", + "type": "form", "children": [ { "type": "text", From c5d5492c3375cc22866481beec7fc82faeffaa01 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Fri, 18 Sep 2020 12:23:38 +0200 Subject: [PATCH 677/813] start converting ftrack settings --- .../system_schema/1_tray_items.json | 272 ++++++++++-------- 1 file changed, 156 insertions(+), 116 deletions(-) diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/1_tray_items.json b/pype/tools/settings/settings/gui_schemas/system_schema/1_tray_items.json index 0d27ccdc4b..deb84673f0 100644 --- a/pype/tools/settings/settings/gui_schemas/system_schema/1_tray_items.json +++ b/pype/tools/settings/settings/gui_schemas/system_schema/1_tray_items.json @@ -1,120 +1,160 @@ { - "key": "tray_modules", - "type": "dict", - "label": "Modules", - "collapsable": true, - "is_group": true, - "is_file": true, - "children": [ - { - "key": "item_usage", - "type": "dict-invisible", - "children": [ - { - "type": "boolean", - "key": "User settings", - "label": "User settings" - }, { - "type": "boolean", - "key": "Ftrack", - "label": "Ftrack" - }, { - "type": "boolean", - "key": "Muster", - "label": "Muster" - }, { - "type": "boolean", - "key": "Avalon", - "label": "Avalon" - }, { - "type": "boolean", - "key": "Clockify", - "label": "Clockify" - }, { - "type": "boolean", - "key": "Standalone Publish", - "label": "Standalone Publish" - }, { - "type": "boolean", - "key": "Logging", - "label": "Logging" - }, { - "type": "boolean", - "key": "Idle Manager", - "label": "Idle Manager" - }, { - "type": "boolean", - "key": "Timers Manager", - "label": "Timers Manager" - }, { - "type": "boolean", - "key": "Rest Api", - "label": "Rest Api" - }, { - "type": "boolean", - "key": "Adobe Communicator", - "label": "Adobe Communicator" - } - ] + "key": "tray_modules", + "type": "dict", + "label": "Modules", + "collapsable": true, + "is_file": true, + "children": [{ + "key": "item_usage", + "type": "dict-invisible", + "children": [{ + "type": "boolean", + "key": "User settings", + "label": "User settings" + }, { + "type": "boolean", + "key": "Ftrack", + "label": "Ftrack" + }, { + "type": "boolean", + "key": "Muster", + "label": "Muster" + }, { + "type": "boolean", + "key": "Avalon", + "label": "Avalon" + }, { + "type": "boolean", + "key": "Clockify", + "label": "Clockify" + }, { + "type": "boolean", + "key": "Standalone Publish", + "label": "Standalone Publish" + }, { + "type": "boolean", + "key": "Logging", + "label": "Logging" + }, { + "type": "boolean", + "key": "Idle Manager", + "label": "Idle Manager" + }, { + "type": "boolean", + "key": "Timers Manager", + "label": "Timers Manager" + }, { + "type": "boolean", + "key": "Rest Api", + "label": "Rest Api" + }, { + "type": "boolean", + "key": "Adobe Communicator", + "label": "Adobe Communicator" + }] + }, { + "key": "attributes", + "type": "dict-invisible", + "children": [{ + "type": "dict", + "key": "Ftrack", + "label": "Ftrack", + "collapsable": true, + "checkbox_key": "enabled", + "children": [{ + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "text", + "key": "ftrack_server", + "label": "Server" + }, + { + "type": "dict", + "key": "sync_to_avalon", + "label": "Sync to avalon", + "children": [{ + "type": "list", + "key": "statuses_name_change", + "label": "Status name change", + "object_type": "text", + "input_modifiers": { + "multiline": false + } + }] + }, + { + "type": "dict-modifiable", + "key": "status_version_to_task", + "label": "Version to Task status mapping", + "object_type": "text" + } + ] + }, + { + "type": "dict", + "key": "Rest Api", + "label": "Rest Api", + "collapsable": true, + "children": [{ + "type": "number", + "key": "default_port", + "label": "Default Port", + "minimum": 1, + "maximum": 65535 }, { - "key": "attributes", - "type": "dict-invisible", - "children": [ - { - "type": "dict", - "key": "Rest Api", - "label": "Rest Api", - "collapsable": true, - "children": [ - { - "type": "number", - "key": "default_port", - "label": "Default Port", - "minimum": 1, - "maximum": 65535 - }, { - "type": "list", - "object_type": "number", - "key": "exclude_ports", - "label": "Exclude ports", - "input_modifiers": { - "minimum": 1, - "maximum": 65535 - } - } - ] - }, { - "type": "dict", - "key": "Timers Manager", - "label": "Timers Manager", - "collapsable": true, - "children": [ - { - "type": "number", - "decimal": 2, - "key": "full_time", - "label": "Max idle time" - }, { - "type": "number", - "decimal": 2, - "key": "message_time", - "label": "When dialog will show" - } - ] - }, { - "type": "dict", - "key": "Clockify", - "label": "Clockify", - "collapsable": true, - "children": [ - { - "type": "text", - "key": "workspace_name", - "label": "Workspace name" - } - ] - } - ] - } + "type": "list", + "object_type": "number", + "key": "exclude_ports", + "label": "Exclude ports", + "input_modifiers": { + "minimum": 1, + "maximum": 65535 + } + }] + }, { + "type": "dict", + "key": "Timers Manager", + "label": "Timers Manager", + "collapsable": true, + "checkbox_key": "enabled", + "children": [{ + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "number", + "decimal": 2, + "key": "full_time", + "label": "Max idle time" + }, { + "type": "number", + "decimal": 2, + "key": "message_time", + "label": "When dialog will show" + } + ] + }, { + "type": "dict", + "key": "Clockify", + "label": "Clockify", + "collapsable": true, + "checkbox_key": "enabled", + "children": [{ + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "text", + "key": "workspace_name", + "label": "Workspace name" + } + ] + } ] + }] } From 75fd8c666ee0fc7b867386486a9a9ac13aa13bd1 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 18 Sep 2020 14:09:03 +0200 Subject: [PATCH 678/813] added possibility to set placeholder for text input --- pype/tools/settings/settings/widgets/item_types.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 68cebf6642..fea78b713b 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -906,6 +906,7 @@ class TextWidget(QtWidgets.QWidget, InputObject): self.initial_attributes(input_data, parent, as_widget) self.multiline = input_data.get("multiline", False) + placeholder = input_data.get("placeholder") layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) @@ -916,6 +917,9 @@ class TextWidget(QtWidgets.QWidget, InputObject): else: self.text_input = QtWidgets.QLineEdit(self) + if placeholder: + self.text_input.setPlaceholderText(placeholder) + self.setFocusProxy(self.text_input) layout_kwargs = {} From a28e154580c4bb5eef3dcbc38c280a3bb8780c02 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 18 Sep 2020 14:09:09 +0200 Subject: [PATCH 679/813] added info to README --- pype/tools/settings/settings/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/pype/tools/settings/settings/README.md b/pype/tools/settings/settings/README.md index db444eb3bd..da8868199a 100644 --- a/pype/tools/settings/settings/README.md +++ b/pype/tools/settings/settings/README.md @@ -111,6 +111,7 @@ ### text - simple text input - key `"multiline"` allows to enter multiple lines of text (Default: `False`) + - key `"placeholder"` allows to show text inside input when is empty (Default: `None`) ``` { From 98569e4fed3469f7bb19a4b4923dfff1c2ae4add Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 18 Sep 2020 14:14:45 +0200 Subject: [PATCH 680/813] schema item does not have key `children` but `name`, schema type can represent only one schema name --- pype/tools/settings/settings/widgets/lib.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/pype/tools/settings/settings/widgets/lib.py b/pype/tools/settings/settings/widgets/lib.py index e225d65417..f54989cfd7 100644 --- a/pype/tools/settings/settings/widgets/lib.py +++ b/pype/tools/settings/settings/widgets/lib.py @@ -69,12 +69,11 @@ def _fill_inner_schemas(schema_data, schema_collection): new_children.append(new_child) continue - for schema_name in child["children"]: - new_child = _fill_inner_schemas( - schema_collection[schema_name], - schema_collection - ) - new_children.append(new_child) + new_child = _fill_inner_schemas( + schema_collection[child["name"]], + schema_collection + ) + new_children.append(new_child) schema_data["children"] = new_children return schema_data From 44dc6805ade48fd2edd94bb1db1479afdc84f0a8 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 18 Sep 2020 14:14:56 +0200 Subject: [PATCH 681/813] modified current schemas --- .../projects_schema/0_project_gui_schema.json | 4 +--- .../system_schema/0_system_gui_schema.json | 19 +++++++++++++------ 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/0_project_gui_schema.json b/pype/tools/settings/settings/gui_schemas/projects_schema/0_project_gui_schema.json index fa7c6a366d..cf95bf4c45 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/0_project_gui_schema.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/0_project_gui_schema.json @@ -22,9 +22,7 @@ "children": [ { "type": "schema", - "children": [ - "1_plugins_gui_schema" - ] + "name": "1_plugins_gui_schema" } ] } diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/0_system_gui_schema.json b/pype/tools/settings/settings/gui_schemas/system_schema/0_system_gui_schema.json index b16545111c..456af7ac0f 100644 --- a/pype/tools/settings/settings/gui_schemas/system_schema/0_system_gui_schema.json +++ b/pype/tools/settings/settings/gui_schemas/system_schema/0_system_gui_schema.json @@ -7,12 +7,16 @@ "key": "global", "children": [{ "type": "schema", - "children": [ - "1_tray_items", - "1_applications_gui_schema", - "1_tools_gui_schema", - "1_intents_gui_schema" - ] + "name": "1_tray_items" + }, { + "type": "schema", + "name": "1_applications_gui_schema" + }, { + "type": "schema", + "name": "1_tools_gui_schema" + }, { + "type": "schema", + "name": "1_intents_gui_schema" }] }, { "type": "dict-invisible", @@ -29,6 +33,9 @@ "label": "Muster - Templates mapping", "is_file": true }] + }, { + "type": "schema", + "name": "1_examples" } ] } From aa472d66de0eb674bad32cc31747a38710eeea09 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 18 Sep 2020 14:15:02 +0200 Subject: [PATCH 682/813] modified README --- pype/tools/settings/settings/README.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pype/tools/settings/settings/README.md b/pype/tools/settings/settings/README.md index db444eb3bd..adacd62772 100644 --- a/pype/tools/settings/settings/README.md +++ b/pype/tools/settings/settings/README.md @@ -27,10 +27,7 @@ ``` { "type": "schema", - "children": [ - "my_schema_name", - "my_other_schema_name" - ] + "name": "my_schema_name" } ``` From dd9809e3edd9fc9657f296748d5f2f77a8541267 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Fri, 18 Sep 2020 15:39:35 +0200 Subject: [PATCH 683/813] update modules settings, in system --- .../system_settings/global/applications.json | 28 +- .../system_settings/global/modules.json | 57 ++++ .../system_settings/global/tools.json | 4 +- .../system_settings/global/tray_modules.json | 28 -- .../system_schema/0_system_gui_schema.json | 17 +- .../system_schema/1_modules_gui_schema.json | 266 ++++++++++++++++++ .../system_schema/1_tray_items.json | 160 ----------- 7 files changed, 340 insertions(+), 220 deletions(-) create mode 100644 pype/settings/defaults/system_settings/global/modules.json delete mode 100644 pype/settings/defaults/system_settings/global/tray_modules.json create mode 100644 pype/tools/settings/settings/gui_schemas/system_schema/1_modules_gui_schema.json delete mode 100644 pype/tools/settings/settings/gui_schemas/system_schema/1_tray_items.json diff --git a/pype/settings/defaults/system_settings/global/applications.json b/pype/settings/defaults/system_settings/global/applications.json index 3a74a85468..e85e5864d9 100644 --- a/pype/settings/defaults/system_settings/global/applications.json +++ b/pype/settings/defaults/system_settings/global/applications.json @@ -1,32 +1,32 @@ { - "blender_2.80": true, - "blender_2.81": true, - "blender_2.82": true, + "blender_2.80": false, + "blender_2.81": false, + "blender_2.82": false, "blender_2.83": true, "celaction_local": true, "celaction_remote": true, "harmony_17": true, - "maya_2017": true, - "maya_2018": true, + "maya_2017": false, + "maya_2018": false, "maya_2019": true, "maya_2020": true, - "nuke_10.0": true, - "nuke_11.2": true, + "nuke_10.0": false, + "nuke_11.2": false, "nuke_11.3": true, "nuke_12.0": true, - "nukex_10.0": true, - "nukex_11.2": true, + "nukex_10.0": false, + "nukex_11.2": false, "nukex_11.3": true, "nukex_12.0": true, - "nukestudio_10.0": true, - "nukestudio_11.2": true, + "nukestudio_10.0": false, + "nukestudio_11.2": false, "nukestudio_11.3": true, "nukestudio_12.0": true, - "houdini_16": true, + "houdini_16": false, "houdini_16.5": false, - "houdini_17": true, + "houdini_17": false, "houdini_18": true, - "premiere_2019": true, + "premiere_2019": false, "premiere_2020": true, "resolve_16": true, "storyboardpro_7": true, diff --git a/pype/settings/defaults/system_settings/global/modules.json b/pype/settings/defaults/system_settings/global/modules.json new file mode 100644 index 0000000000..6400c2e3f3 --- /dev/null +++ b/pype/settings/defaults/system_settings/global/modules.json @@ -0,0 +1,57 @@ +{ + "Avalon": { + "AVALON_MONGO": "", + "AVALON_DB_DATA": "", + "AVALON_THUMBNAIL_ROOT": "" + }, + "Ftrack": { + "enabled": true, + "ftrack_server": "", + "ftrack_actions_path": [], + "ftrack_events_path": [], + "FTRACK_EVENTS_MONGO_DB": "", + "FTRACK_EVENTS_MONGO_COL": "", + "sync_to_avalon": { + "statuses_name_change": [] + }, + "status_version_to_task": {}, + "status_update": {} + }, + "Rest Api": { + "default_port": 1, + "exclude_ports": [] + }, + "Timers Manager": { + "enabled": true, + "full_time": 0.0, + "message_time": 0.0 + }, + "Clockify": { + "enabled": true, + "workspace_name": "" + }, + "Deadline": { + "enabled": true, + "DEADLINE_REST_URL": "" + }, + "Muster": { + "enabled": true, + "MUSTER_REST_URL": "", + "templates_mapping": {} + }, + "Logging": { + "enabled": true + }, + "Adobe Communicator": { + "enabled": true + }, + "User setting": { + "enabled": true + }, + "Standalone Publish": { + "enabled": true + }, + "Idle Manager": { + "enabled": true + } +} \ No newline at end of file diff --git a/pype/settings/defaults/system_settings/global/tools.json b/pype/settings/defaults/system_settings/global/tools.json index 93895c0e81..1f8c2ad1ea 100644 --- a/pype/settings/defaults/system_settings/global/tools.json +++ b/pype/settings/defaults/system_settings/global/tools.json @@ -1,6 +1,6 @@ { - "mtoa_3.0.1": true, - "mtoa_3.1.1": true, + "mtoa_3.0.1": false, + "mtoa_3.1.1": false, "mtoa_3.2.0": true, "yeti_2.1.2": true } \ No newline at end of file diff --git a/pype/settings/defaults/system_settings/global/tray_modules.json b/pype/settings/defaults/system_settings/global/tray_modules.json deleted file mode 100644 index 0ff5b15552..0000000000 --- a/pype/settings/defaults/system_settings/global/tray_modules.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "item_usage": { - "User settings": false, - "Ftrack": true, - "Muster": false, - "Avalon": true, - "Clockify": false, - "Standalone Publish": true, - "Logging": true, - "Idle Manager": true, - "Timers Manager": true, - "Rest Api": true, - "Adobe Communicator": true - }, - "attributes": { - "Rest Api": { - "default_port": 8021, - "exclude_ports": [] - }, - "Timers Manager": { - "full_time": 15.0, - "message_time": 0.5 - }, - "Clockify": { - "workspace_name": "" - } - } -} \ No newline at end of file diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/0_system_gui_schema.json b/pype/tools/settings/settings/gui_schemas/system_schema/0_system_gui_schema.json index b16545111c..a979ae1fed 100644 --- a/pype/tools/settings/settings/gui_schemas/system_schema/0_system_gui_schema.json +++ b/pype/tools/settings/settings/gui_schemas/system_schema/0_system_gui_schema.json @@ -8,27 +8,12 @@ "children": [{ "type": "schema", "children": [ - "1_tray_items", + "1_modules_gui_schema", "1_applications_gui_schema", "1_tools_gui_schema", "1_intents_gui_schema" ] }] - }, { - "type": "dict-invisible", - "key": "muster", - "children": [{ - "type": "dict-modifiable", - "object_type": "number", - "input_modifiers": { - "minimum": 0, - "maximum": 300 - }, - "is_group": true, - "key": "templates_mapping", - "label": "Muster - Templates mapping", - "is_file": true - }] } ] } diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/1_modules_gui_schema.json b/pype/tools/settings/settings/gui_schemas/system_schema/1_modules_gui_schema.json new file mode 100644 index 0000000000..feec5735f5 --- /dev/null +++ b/pype/tools/settings/settings/gui_schemas/system_schema/1_modules_gui_schema.json @@ -0,0 +1,266 @@ +{ + "key": "modules", + "type": "dict", + "label": "Modules", + "collapsable": true, + "is_file": true, + "children": [{ + "type": "dict", + "key": "Avalon", + "label": "Avalon", + "collapsable": true, + "children": [ + { + "type": "text", + "key": "AVALON_MONGO", + "label": "Avalon Mongo URL" + }, + { + "type": "text", + "key": "AVALON_DB_DATA", + "label": "Avalon Mongo Data Location" + }, + { + "type": "text", + "key": "AVALON_THUMBNAIL_ROOT", + "label": "Thumbnail Storage Location" + } + ] + },{ + "type": "dict", + "key": "Ftrack", + "label": "Ftrack", + "collapsable": true, + "checkbox_key": "enabled", + "children": [{ + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "text", + "key": "ftrack_server", + "label": "Server" + }, + { + "type": "label", + "label": "Additional Ftrack paths" + }, + { + "type": "list", + "key": "ftrack_actions_path", + "label": "Action paths", + "object_type": "text" + }, + { + "type": "list", + "key": "ftrack_events_path", + "label": "Event paths", + "object_type": "text" + }, + { + "type": "label", + "label": "Ftrack event server advanced settings" + }, + { + "type": "text", + "key": "FTRACK_EVENTS_MONGO_DB", + "label": "Event Mongo DB" + }, + { + "type": "text", + "key": "FTRACK_EVENTS_MONGO_COL", + "label": "Events Mongo Collection" + }, + { + "type": "dict", + "key": "sync_to_avalon", + "label": "Sync to avalon", + "children": [{ + "type": "list", + "key": "statuses_name_change", + "label": "Status name change", + "object_type": "text", + "input_modifiers": { + "multiline": false + } + }] + }, + { + "type": "dict-modifiable", + "key": "status_version_to_task", + "label": "Version to Task status mapping", + "object_type": "text" + }, + { + "type": "dict-modifiable", + "key": "status_update", + "label": "Status Updates", + "object_type": "list", + "input_modifiers": { + "object_type": "text" + } + } + ] + }, { + "type": "dict", + "key": "Rest Api", + "label": "Rest Api", + "collapsable": true, + "children": [{ + "type": "number", + "key": "default_port", + "label": "Default Port", + "minimum": 1, + "maximum": 65535 + }, + { + "type": "list", + "object_type": "number", + "key": "exclude_ports", + "label": "Exclude ports", + "input_modifiers": { + "minimum": 1, + "maximum": 65535 + } + } + ] + }, { + "type": "dict", + "key": "Timers Manager", + "label": "Timers Manager", + "collapsable": true, + "checkbox_key": "enabled", + "children": [{ + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "number", + "decimal": 2, + "key": "full_time", + "label": "Max idle time" + }, { + "type": "number", + "decimal": 2, + "key": "message_time", + "label": "When dialog will show" + } + ] + }, { + "type": "dict", + "key": "Clockify", + "label": "Clockify", + "collapsable": true, + "checkbox_key": "enabled", + "children": [{ + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "text", + "key": "workspace_name", + "label": "Workspace name" + } + ] + }, { + "type": "dict", + "key": "Deadline", + "label": "Deadline", + "collapsable": true, + "checkbox_key": "enabled", + "children": [{ + "type": "boolean", + "key": "enabled", + "label": "Enabled" + },{ + "type": "text", + "key": "DEADLINE_REST_URL", + "label": "Deadline Resl URL" + }] + }, { + "type": "dict", + "key": "Muster", + "label": "Muster", + "collapsable": true, + "checkbox_key": "enabled", + "children": [{ + "type": "boolean", + "key": "enabled", + "label": "Enabled" + },{ + "type": "text", + "key": "MUSTER_REST_URL", + "label": "Muster Resl URL" + },{ + "type": "dict-modifiable", + "object_type": "number", + "input_modifiers": { + "minimum": 0, + "maximum": 300 + }, + "is_group": true, + "key": "templates_mapping", + "label": "Templates mapping", + "is_file": true + }] + }, { + "type": "dict", + "key": "Logging", + "label": "Logging", + "collapsable": true, + "checkbox_key": "enabled", + "children": [{ + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }] + }, { + "type": "dict", + "key": "Adobe Communicator", + "label": "Adobe Communicator", + "collapsable": true, + "checkbox_key": "enabled", + "children": [{ + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }] + }, { + "type": "dict", + "key": "User setting", + "label": "User setting", + "collapsable": true, + "checkbox_key": "enabled", + "children": [{ + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }] + }, { + "type": "dict", + "key": "Standalone Publish", + "label": "Standalone Publish", + "collapsable": true, + "checkbox_key": "enabled", + "children": [{ + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }] + }, { + "type": "dict", + "key": "Idle Manager", + "label": "Idle Manager", + "collapsable": true, + "checkbox_key": "enabled", + "children": [{ + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }] + } + ] +} diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/1_tray_items.json b/pype/tools/settings/settings/gui_schemas/system_schema/1_tray_items.json deleted file mode 100644 index deb84673f0..0000000000 --- a/pype/tools/settings/settings/gui_schemas/system_schema/1_tray_items.json +++ /dev/null @@ -1,160 +0,0 @@ -{ - "key": "tray_modules", - "type": "dict", - "label": "Modules", - "collapsable": true, - "is_file": true, - "children": [{ - "key": "item_usage", - "type": "dict-invisible", - "children": [{ - "type": "boolean", - "key": "User settings", - "label": "User settings" - }, { - "type": "boolean", - "key": "Ftrack", - "label": "Ftrack" - }, { - "type": "boolean", - "key": "Muster", - "label": "Muster" - }, { - "type": "boolean", - "key": "Avalon", - "label": "Avalon" - }, { - "type": "boolean", - "key": "Clockify", - "label": "Clockify" - }, { - "type": "boolean", - "key": "Standalone Publish", - "label": "Standalone Publish" - }, { - "type": "boolean", - "key": "Logging", - "label": "Logging" - }, { - "type": "boolean", - "key": "Idle Manager", - "label": "Idle Manager" - }, { - "type": "boolean", - "key": "Timers Manager", - "label": "Timers Manager" - }, { - "type": "boolean", - "key": "Rest Api", - "label": "Rest Api" - }, { - "type": "boolean", - "key": "Adobe Communicator", - "label": "Adobe Communicator" - }] - }, { - "key": "attributes", - "type": "dict-invisible", - "children": [{ - "type": "dict", - "key": "Ftrack", - "label": "Ftrack", - "collapsable": true, - "checkbox_key": "enabled", - "children": [{ - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "text", - "key": "ftrack_server", - "label": "Server" - }, - { - "type": "dict", - "key": "sync_to_avalon", - "label": "Sync to avalon", - "children": [{ - "type": "list", - "key": "statuses_name_change", - "label": "Status name change", - "object_type": "text", - "input_modifiers": { - "multiline": false - } - }] - }, - { - "type": "dict-modifiable", - "key": "status_version_to_task", - "label": "Version to Task status mapping", - "object_type": "text" - } - ] - }, - { - "type": "dict", - "key": "Rest Api", - "label": "Rest Api", - "collapsable": true, - "children": [{ - "type": "number", - "key": "default_port", - "label": "Default Port", - "minimum": 1, - "maximum": 65535 - }, { - "type": "list", - "object_type": "number", - "key": "exclude_ports", - "label": "Exclude ports", - "input_modifiers": { - "minimum": 1, - "maximum": 65535 - } - }] - }, { - "type": "dict", - "key": "Timers Manager", - "label": "Timers Manager", - "collapsable": true, - "checkbox_key": "enabled", - "children": [{ - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "number", - "decimal": 2, - "key": "full_time", - "label": "Max idle time" - }, { - "type": "number", - "decimal": 2, - "key": "message_time", - "label": "When dialog will show" - } - ] - }, { - "type": "dict", - "key": "Clockify", - "label": "Clockify", - "collapsable": true, - "checkbox_key": "enabled", - "children": [{ - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "text", - "key": "workspace_name", - "label": "Workspace name" - } - ] - } - ] - }] -} From 162dc8a4bdfef487b1a804f7adb7022b07276715 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Fri, 18 Sep 2020 15:42:51 +0200 Subject: [PATCH 684/813] fill defaults --- .../system_settings/global/modules.json | 45 ++++++++++++------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/pype/settings/defaults/system_settings/global/modules.json b/pype/settings/defaults/system_settings/global/modules.json index 6400c2e3f3..3fb52fa129 100644 --- a/pype/settings/defaults/system_settings/global/modules.json +++ b/pype/settings/defaults/system_settings/global/modules.json @@ -1,30 +1,40 @@ { "Avalon": { - "AVALON_MONGO": "", - "AVALON_DB_DATA": "", - "AVALON_THUMBNAIL_ROOT": "" + "AVALON_MONGO": "mongodb://localhost:2707", + "AVALON_DB_DATA": "{PYPE_SETUP_PATH}/../mongo_db_data", + "AVALON_THUMBNAIL_ROOT": "{PYPE_SETUP_PATH}/../avalon_thumails" }, "Ftrack": { "enabled": true, - "ftrack_server": "", + "ftrack_server": "https://pype.ftrackapp.com", "ftrack_actions_path": [], "ftrack_events_path": [], - "FTRACK_EVENTS_MONGO_DB": "", - "FTRACK_EVENTS_MONGO_COL": "", + "FTRACK_EVENTS_MONGO_DB": "pype", + "FTRACK_EVENTS_MONGO_COL": "ftrack_events", "sync_to_avalon": { - "statuses_name_change": [] + "statuses_name_change": [ + "ready", + "not ready" + ] }, "status_version_to_task": {}, - "status_update": {} + "status_update": { + "Ready": [ + "Not Ready" + ], + "In Progress": [ + "_any_" + ] + } }, "Rest Api": { - "default_port": 1, + "default_port": 8021, "exclude_ports": [] }, "Timers Manager": { "enabled": true, - "full_time": 0.0, - "message_time": 0.0 + "full_time": 15.0, + "message_time": 0.5 }, "Clockify": { "enabled": true, @@ -32,12 +42,17 @@ }, "Deadline": { "enabled": true, - "DEADLINE_REST_URL": "" + "DEADLINE_REST_URL": "http://localhost:8082" }, "Muster": { - "enabled": true, + "enabled": false, "MUSTER_REST_URL": "", - "templates_mapping": {} + "templates_mapping": { + "arnold": 46, + "redshift": 55, + "renderman": 29, + "vray": 37 + } }, "Logging": { "enabled": true @@ -54,4 +69,4 @@ "Idle Manager": { "enabled": true } -} \ No newline at end of file +} From 7da75db742701e42f2c6a020413e6d1bcfc4ab07 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Fri, 18 Sep 2020 15:43:48 +0200 Subject: [PATCH 685/813] fill muster templates --- .../system_settings/global/modules.json | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/pype/settings/defaults/system_settings/global/modules.json b/pype/settings/defaults/system_settings/global/modules.json index 3fb52fa129..8a2b326d46 100644 --- a/pype/settings/defaults/system_settings/global/modules.json +++ b/pype/settings/defaults/system_settings/global/modules.json @@ -48,10 +48,23 @@ "enabled": false, "MUSTER_REST_URL": "", "templates_mapping": { - "arnold": 46, - "redshift": 55, - "renderman": 29, - "vray": 37 + "3delight": 41, + "arnold": 46, + "arnold_sf": 57, + "gelato": 30, + "harware": 3, + "krakatoa": 51, + "file_layers": 7, + "mentalray": 2, + "mentalray_sf": 6, + "redshift": 55, + "renderman": 29, + "software": 1, + "software_sf": 5, + "turtle": 10, + "vector": 4, + "vray": 37, + "ffmpeg": 48 } }, "Logging": { From 2ee94bd890d0f86052f4d6278f751383d688a82b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 18 Sep 2020 17:33:07 +0200 Subject: [PATCH 686/813] ListItem can be set as strict --- .../settings/settings/widgets/item_types.py | 76 ++++++++++--------- 1 file changed, 42 insertions(+), 34 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 68cebf6642..8c9fbd8640 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -1291,11 +1291,16 @@ class ListItem(QtWidgets.QWidget, SettingObject): _btn_size = 20 value_changed = QtCore.Signal(object) - def __init__(self, object_type, input_modifiers, config_parent, parent): + def __init__( + self, object_type, input_modifiers, config_parent, parent, + is_strict=False + ): super(ListItem, self).__init__(parent) self._set_default_attributes() + self._is_strict = is_strict + self._parent = config_parent self._any_parent_is_group = True self._is_empty = False @@ -1307,34 +1312,38 @@ class ListItem(QtWidgets.QWidget, SettingObject): char_up = qtawesome.charmap("fa.angle-up") char_down = qtawesome.charmap("fa.angle-down") - self.add_btn = QtWidgets.QPushButton("+") - self.remove_btn = QtWidgets.QPushButton("-") - self.up_btn = QtWidgets.QPushButton(char_up) - self.down_btn = QtWidgets.QPushButton(char_down) + if not self._is_strict: + self.add_btn = QtWidgets.QPushButton("+") + self.remove_btn = QtWidgets.QPushButton("-") + self.up_btn = QtWidgets.QPushButton(char_up) + self.down_btn = QtWidgets.QPushButton(char_down) - font_up_down = qtawesome.font("fa", 13) - self.up_btn.setFont(font_up_down) - self.down_btn.setFont(font_up_down) + font_up_down = qtawesome.font("fa", 13) + self.up_btn.setFont(font_up_down) + self.down_btn.setFont(font_up_down) - self.add_btn.setFocusPolicy(QtCore.Qt.ClickFocus) - self.remove_btn.setFocusPolicy(QtCore.Qt.ClickFocus) - self.up_btn.setFocusPolicy(QtCore.Qt.ClickFocus) - self.down_btn.setFocusPolicy(QtCore.Qt.ClickFocus) + self.add_btn.setFocusPolicy(QtCore.Qt.ClickFocus) + self.remove_btn.setFocusPolicy(QtCore.Qt.ClickFocus) + self.up_btn.setFocusPolicy(QtCore.Qt.ClickFocus) + self.down_btn.setFocusPolicy(QtCore.Qt.ClickFocus) - self.add_btn.setFixedSize(self._btn_size, self._btn_size) - self.remove_btn.setFixedSize(self._btn_size, self._btn_size) - self.up_btn.setFixedSize(self._btn_size, self._btn_size) - self.down_btn.setFixedSize(self._btn_size, self._btn_size) + self.add_btn.setFixedSize(self._btn_size, self._btn_size) + self.remove_btn.setFixedSize(self._btn_size, self._btn_size) + self.up_btn.setFixedSize(self._btn_size, self._btn_size) + self.down_btn.setFixedSize(self._btn_size, self._btn_size) - self.add_btn.setProperty("btn-type", "tool-item") - self.remove_btn.setProperty("btn-type", "tool-item") - self.up_btn.setProperty("btn-type", "tool-item") - self.down_btn.setProperty("btn-type", "tool-item") + self.add_btn.setProperty("btn-type", "tool-item") + self.remove_btn.setProperty("btn-type", "tool-item") + self.up_btn.setProperty("btn-type", "tool-item") + self.down_btn.setProperty("btn-type", "tool-item") - self.add_btn.clicked.connect(self._on_add_clicked) - self.remove_btn.clicked.connect(self._on_remove_clicked) - self.up_btn.clicked.connect(self._on_up_clicked) - self.down_btn.clicked.connect(self._on_down_clicked) + self.add_btn.clicked.connect(self._on_add_clicked) + self.remove_btn.clicked.connect(self._on_remove_clicked) + self.up_btn.clicked.connect(self._on_up_clicked) + self.down_btn.clicked.connect(self._on_down_clicked) + + layout.addWidget(self.add_btn, 0) + layout.addWidget(self.remove_btn, 0) ItemKlass = TypeToKlass.types[object_type] self.value_input = ItemKlass( @@ -1344,18 +1353,17 @@ class ListItem(QtWidgets.QWidget, SettingObject): label_widget=None ) - self.spacer_widget = QtWidgets.QWidget(self) - self.spacer_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground) - self.spacer_widget.setVisible(False) - - layout.addWidget(self.add_btn, 0) - layout.addWidget(self.remove_btn, 0) - layout.addWidget(self.value_input, 1) - layout.addWidget(self.spacer_widget, 1) - layout.addWidget(self.up_btn, 0) - layout.addWidget(self.down_btn, 0) + if not self._is_strict: + self.spacer_widget = QtWidgets.QWidget(self) + self.spacer_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground) + self.spacer_widget.setVisible(False) + + layout.addWidget(self.spacer_widget, 1) + + layout.addWidget(self.up_btn, 0) + layout.addWidget(self.down_btn, 0) self.value_input.value_changed.connect(self._on_value_change) From 0cccd9e3a7fe7a12866ba627f8d6770813838eca Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 18 Sep 2020 17:44:55 +0200 Subject: [PATCH 687/813] implemented strict list --- .../settings/settings/widgets/item_types.py | 148 ++++++++++++++++++ 1 file changed, 148 insertions(+) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 8c9fbd8640..7afa6fca18 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -1705,6 +1705,153 @@ class ListWidget(QtWidgets.QWidget, InputObject): return output +class ListStrictWidget(QtWidgets.QWidget, InputObject): + value_changed = QtCore.Signal(object) + _default_input_value = None + + def __init__( + self, input_data, parent, + as_widget=False, label_widget=None, parent_widget=None + ): + if parent_widget is None: + parent_widget = parent + super(ListStrictWidget, self).__init__(parent_widget) + self.setObjectName("ListStrictWidget") + + horizontal = input_data.get("horizontal", True) + + self.initial_attributes(input_data, parent, as_widget) + + self.input_fields = [] + + layout = QtWidgets.QHBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 5) + layout.setSpacing(5) + + if not self.as_widget: + self.key = input_data["key"] + if not label_widget: + label_widget = QtWidgets.QLabel(input_data["label"], self) + layout.addWidget(label_widget, alignment=QtCore.Qt.AlignTop) + + self.label_widget = label_widget + + inputs_widget = QtWidgets.QWidget(self) + inputs_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground) + layout.addWidget(inputs_widget) + + if horizontal: + inputs_layout = QtWidgets.QHBoxLayout(inputs_widget) + else: + inputs_layout = QtWidgets.QVBoxLayout(inputs_widget) + inputs_layout.setContentsMargins(0, 0, 0, 0) + inputs_layout.setSpacing(3) + + self.inputs_widget = inputs_widget + self.inputs_layout = inputs_layout + + for child_configuration in input_data["object_types"]: + object_type = child_configuration["type"] + + proxy_widget = QtWidgets.QWidget(self) + proxy_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground) + + item_widget = ListItem( + object_type, child_configuration, self, proxy_widget, + is_strict=True + ) + + self.input_fields.append(item_widget) + item_widget.value_changed.connect(self._on_value_change) + + proxy_layout = QtWidgets.QHBoxLayout(proxy_widget) + proxy_layout.setContentsMargins(0, 0, 0, 0) + proxy_layout.setSpacing(5) + + label = child_configuration.get("label") + label_widget = None + if label: + label_widget = QtWidgets.QLabel(label, self) + proxy_layout.addWidget(label_widget, 0) + + proxy_layout.addWidget(item_widget, 0) + + if not horizontal: + spacer_widget = QtWidgets.QWidget(proxy_widget) + proxy_layout.addWidget(spacer_widget, 1) + + self.inputs_layout.addWidget(proxy_widget) + + if horizontal: + spacer_widget = QtWidgets.QWidget(proxy_widget) + self.inputs_layout.addWidget(spacer_widget, 1) + + self.updateGeometry() + + @property + def default_input_value(self): + if self._default_input_value is None: + self.set_value(NOT_SET) + self._default_input_value = self.item_value() + return self._default_input_value + + def set_value(self, value): + if self._is_overriden: + method_name = "apply_overrides" + elif not self._has_studio_override: + method_name = "update_default_values" + else: + method_name = "update_studio_values" + + for idx, input_field in enumerate(self.input_fields): + if value is NOT_SET: + _value = value + else: + if idx > len(value) - 1: + _value = NOT_SET + else: + _value = value[idx] + _method = getattr(input_field, method_name) + _method(_value) + + def hierarchical_style_update(self): + for input_field in self.input_fields: + input_field.hierarchical_style_update() + self.update_style() + + def update_style(self): + if self._as_widget: + if not self.isEnabled(): + state = self.style_state(False, False, False, False) + else: + state = self.style_state( + False, + self._is_invalid, + False, + self._is_modified + ) + else: + state = self.style_state( + self.has_studio_override, + self.is_invalid, + self.is_overriden, + self.is_modified + ) + + if self._state == state: + return + + if self.label_widget: + self.label_widget.setProperty("state", state) + self.label_widget.style().polish(self.label_widget) + + def item_value(self): + output = [] + for item in self.input_fields: + output.append(item.config_value()) + return output + + class ModifiableDictItem(QtWidgets.QWidget, SettingObject): _btn_size = 20 value_changed = QtCore.Signal(object) @@ -3360,6 +3507,7 @@ TypeToKlass.types["text"] = TextWidget TypeToKlass.types["path-input"] = PathInputWidget TypeToKlass.types["raw-json"] = RawJsonWidget TypeToKlass.types["list"] = ListWidget +TypeToKlass.types["list-strict"] = ListStrictWidget TypeToKlass.types["dict-modifiable"] = ModifiableDict TypeToKlass.types["dict-item"] = DictItemWidget TypeToKlass.types["dict"] = DictWidget From a13755c341d67a91b9a70ce23440c16ce0d544d9 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 18 Sep 2020 17:46:08 +0200 Subject: [PATCH 688/813] make sure defaults_not_set is set to False if defaults are available --- pype/tools/settings/settings/widgets/item_types.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 7afa6fca18..4a9e092a99 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -547,6 +547,7 @@ class InputObject(SettingObject): Class is for item types not creating or using other item types, most of methods has same code in that case. """ + def update_default_values(self, parent_values): self._state = None self._is_modified = False @@ -559,8 +560,8 @@ class InputObject(SettingObject): if value is NOT_SET: if self.develop_mode: - value = self.default_input_value self.defaults_not_set = True + value = self.default_input_value if value is NOT_SET: raise NotImplementedError(( "{} Does not have implemented" @@ -571,6 +572,8 @@ class InputObject(SettingObject): raise ValueError( "Default value is not set. This is implementation BUG." ) + else: + self.defaults_not_set = False self.default_value = value self._has_studio_override = False @@ -3024,6 +3027,8 @@ class PathWidget(QtWidgets.QWidget, SettingObject): raise ValueError( "Default value is not set. This is implementation BUG." ) + else: + self.defaults_not_set = False self.default_value = value self._has_studio_override = False From d3e61cea7dd01d4d950c9786d9184d20a0b59485 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 18 Sep 2020 17:46:24 +0200 Subject: [PATCH 689/813] added strict list and dict-item to readme --- pype/tools/settings/settings/README.md | 79 ++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/pype/tools/settings/settings/README.md b/pype/tools/settings/settings/README.md index db444eb3bd..9d12467cc9 100644 --- a/pype/tools/settings/settings/README.md +++ b/pype/tools/settings/settings/README.md @@ -207,6 +207,85 @@ } ``` +### dict-item +- item represents dictionary with strict keys in and data types of its values +- can be used only as widget (in `list` or `dict-modifiable`) + - only key modifier is `children` which is list of it's keys +- USAGE: e.g. List of dictionaries where each dictionary have same structure. + +``` +{ + "type": "list", + "key": "profiles", + "label": "Profiles", + "object_type": "dict-item", + "input_modifiers": { + "children": [ + { + "key": "families", + "label": "Families", + "type": "list", + "object_type": "text" + }, { + "key": "hosts", + "label": "Hosts", + "type": "list", + "object_type": "text" + } + ... + ] + } +} +``` + +### list-strict +- input for strict number of items in list +- each child item can be different type with different possible modifiers +- it is possible to display them in horizontal or vertical layout + - key `"horizontal"` as `True`/`False` (Default: `True`) +- each child may have defined `"label"` which is shown next to input + - label does not reflect modifications or overrides (TODO) +- children item are defined under key `"object_types"` which is list of dictionaries + - key `"children"` is not used because is used for hierarchy validations in schema +- USAGE: For colors, transformations, etc. Custom number and different modifiers + give ability to define if color is HUE or RGB, 0-255, 0-1, 0-100 etc. + +``` +{ + "type": "list-strict", + "key": "color", + "label": "Color", + "object_types": [ + { + "label": "Red", + "type": "number", + "minimum": 0, + "maximum": 255, + "decimal": 0 + }, { + "label": "Green", + "type": "number", + "minimum": 0, + "maximum": 255, + "decimal": 0 + }, { + "label": "Blue", + "type": "number", + "minimum": 0, + "maximum": 255, + "decimal": 0 + }, { + "label": "Alpha", + "type": "number", + "minimum": 0, + "maximum": 1, + "decimal": 6 + } + ] +} +``` + + ## Noninteractive widgets - have nothing to do with data From 060aa04f93f6999ddc9146ba762d81eb9ffeb8fa Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 18 Sep 2020 17:49:16 +0200 Subject: [PATCH 690/813] added dict-item and list-strict to examples --- .../gui_schemas/system_schema/1_examples.json | 51 +++++++++++++++++++ 1 file changed, 51 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 73f72c875c..1284c9948b 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 @@ -72,6 +72,57 @@ "minimum": 10, "maximum": 100 } + }, { + "type": "list-strict", + "key": "strict_list", + "label": "StrictList (color)", + "object_types": [ + { + "label": "Red", + "type": "number", + "minimum": 0, + "maximum": 255, + "decimal": 0 + }, { + "label": "Green", + "type": "number", + "minimum": 0, + "maximum": 255, + "decimal": 0 + }, { + "label": "Blue", + "type": "number", + "minimum": 0, + "maximum": 255, + "decimal": 0 + }, { + "label": "Alpha", + "type": "number", + "minimum": 0, + "maximum": 1, + "decimal": 6 + } + ] + }, { + "type": "list", + "key": "dict_item", + "label": "DictItem in List", + "object_type": "dict-item", + "input_modifiers": { + "children": [ + { + "key": "families", + "label": "Families", + "type": "list", + "object_type": "text" + }, { + "key": "hosts", + "label": "Hosts", + "type": "list", + "object_type": "text" + } + ] + } }, { "type": "path-widget", "key": "single_path_input", From 8f967c9d6df25f85bad8ef633890688f51a54aff Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 18 Sep 2020 18:16:21 +0200 Subject: [PATCH 691/813] added more variants to list-strict --- .../gui_schemas/system_schema/1_examples.json | 91 ++++++++++++++++++- 1 file changed, 89 insertions(+), 2 deletions(-) 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 1284c9948b..0578968508 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 @@ -74,8 +74,8 @@ } }, { "type": "list-strict", - "key": "strict_list", - "label": "StrictList (color)", + "key": "strict_list_labels_horizontal", + "label": "StrictList-labels-horizontal (color)", "object_types": [ { "label": "Red", @@ -103,6 +103,93 @@ "decimal": 6 } ] + }, { + "type": "list-strict", + "key": "strict_list_labels_vertical", + "label": "StrictList-labels-vertical (color)", + "horizontal": false, + "object_types": [ + { + "label": "Red", + "type": "number", + "minimum": 0, + "maximum": 255, + "decimal": 0 + }, { + "label": "Green", + "type": "number", + "minimum": 0, + "maximum": 255, + "decimal": 0 + }, { + "label": "Blue", + "type": "number", + "minimum": 0, + "maximum": 255, + "decimal": 0 + }, { + "label": "Alpha", + "type": "number", + "minimum": 0, + "maximum": 1, + "decimal": 6 + } + ] + }, { + "type": "list-strict", + "key": "strict_list_nolabels_horizontal", + "label": "StrictList-nolabels-horizontal (color)", + "object_types": [ + { + "type": "number", + "minimum": 0, + "maximum": 255, + "decimal": 0 + }, { + "type": "number", + "minimum": 0, + "maximum": 255, + "decimal": 0 + }, { + "type": "number", + "minimum": 0, + "maximum": 255, + "decimal": 0 + }, { + "type": "number", + "minimum": 0, + "maximum": 1, + "decimal": 6 + } + ] + }, { + "type": "list-strict", + "key": "strict_list_nolabels_vertical", + "label": "StrictList-nolabels-vertical (color)", + "horizontal": false, + "object_types": [ + { + "type": "number", + "minimum": 0, + "maximum": 255, + "decimal": 0 + }, { + "type": "number", + "minimum": 0, + "maximum": 255, + "decimal": 0 + }, { + "type": "number", + "minimum": 0, + "maximum": 255, + "decimal": 0 + }, { + "type": "number", + "minimum": 0, + "maximum": 1, + "decimal": 6 + } + ] }, { "type": "list", "key": "dict_item", From 85a55b4f1b649c5d9d6bd3f27360ae2deaca6239 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 18 Sep 2020 18:16:34 +0200 Subject: [PATCH 692/813] list-strict use grid layout now --- .../settings/settings/widgets/item_types.py | 90 ++++++++++++++----- 1 file changed, 67 insertions(+), 23 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 4a9e092a99..01dab9ccc9 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -1721,8 +1721,6 @@ class ListStrictWidget(QtWidgets.QWidget, InputObject): super(ListStrictWidget, self).__init__(parent_widget) self.setObjectName("ListStrictWidget") - horizontal = input_data.get("horizontal", True) - self.initial_attributes(input_data, parent, as_widget) self.input_fields = [] @@ -1739,58 +1737,91 @@ class ListStrictWidget(QtWidgets.QWidget, InputObject): self.label_widget = label_widget + self._add_children(layout, input_data) + + def _add_children(self, layout, input_data): inputs_widget = QtWidgets.QWidget(self) inputs_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground) layout.addWidget(inputs_widget) + horizontal = input_data.get("horizontal", True) if horizontal: inputs_layout = QtWidgets.QHBoxLayout(inputs_widget) else: - inputs_layout = QtWidgets.QVBoxLayout(inputs_widget) + inputs_layout = QtWidgets.QGridLayout(inputs_widget) + inputs_layout.setContentsMargins(0, 0, 0, 0) inputs_layout.setSpacing(3) self.inputs_widget = inputs_widget self.inputs_layout = inputs_layout + children_item_mapping = [] for child_configuration in input_data["object_types"]: object_type = child_configuration["type"] - proxy_widget = QtWidgets.QWidget(self) - proxy_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground) - item_widget = ListItem( - object_type, child_configuration, self, proxy_widget, + object_type, child_configuration, self, self.inputs_widget, is_strict=True ) self.input_fields.append(item_widget) item_widget.value_changed.connect(self._on_value_change) - proxy_layout = QtWidgets.QHBoxLayout(proxy_widget) - proxy_layout.setContentsMargins(0, 0, 0, 0) - proxy_layout.setSpacing(5) - label = child_configuration.get("label") label_widget = None if label: label_widget = QtWidgets.QLabel(label, self) - proxy_layout.addWidget(label_widget, 0) - proxy_layout.addWidget(item_widget, 0) - - if not horizontal: - spacer_widget = QtWidgets.QWidget(proxy_widget) - proxy_layout.addWidget(spacer_widget, 1) - - self.inputs_layout.addWidget(proxy_widget) + children_item_mapping.append((label_widget, item_widget)) if horizontal: - spacer_widget = QtWidgets.QWidget(proxy_widget) - self.inputs_layout.addWidget(spacer_widget, 1) + self._add_children_horizontally(children_item_mapping) + else: + self._add_children_vertically(children_item_mapping) self.updateGeometry() + def _add_children_vertically(self, children_item_mapping): + any_has_label = False + for item_mapping in children_item_mapping: + if item_mapping[0]: + any_has_label = True + break + + row = self.inputs_layout.count() + if not any_has_label: + self.inputs_layout.setColumnStretch(1, 1) + for item_mapping in children_item_mapping: + item_widget = item_mapping[1] + self.inputs_layout.addWidget(item_widget, row, 0, 1, 1) + + spacer_widget = QtWidgets.QWidget(self.inputs_widget) + self.inputs_layout.addWidget(spacer_widget, row, 1, 1, 1) + row += 1 + + else: + self.inputs_layout.setColumnStretch(2, 1) + for label_widget, item_widget in children_item_mapping: + self.inputs_layout.addWidget( + label_widget, row, 0, 1, 1, + alignment=QtCore.Qt.AlignRight | QtCore.Qt.AlignTop + ) + self.inputs_layout.addWidget(item_widget, row, 1, 1, 1) + + spacer_widget = QtWidgets.QWidget(self.inputs_widget) + self.inputs_layout.addWidget(spacer_widget, row, 2, 1, 1) + row += 1 + + def _add_children_horizontally(self, children_item_mapping): + for label_widget, item_widget in children_item_mapping: + if label_widget: + self.inputs_layout.addWidget(label_widget, 0) + self.inputs_layout.addWidget(item_widget, 0) + + spacer_widget = QtWidgets.QWidget(self.inputs_widget) + self.inputs_layout.addWidget(spacer_widget, 1) + @property def default_input_value(self): if self._default_input_value is None: @@ -1840,7 +1871,7 @@ class ListStrictWidget(QtWidgets.QWidget, InputObject): self.is_overriden, self.is_modified ) - + if self._state == state: return @@ -2630,6 +2661,18 @@ class DictWidget(QtWidgets.QWidget, SettingObject): return {self.key: values}, self.is_group +class GridLabel(QtWidgets.QLabel): + def __init__(self, *args, **kwargs): + super(GridLabel, self).__init__(*args, **kwargs) + self.input_field = None + + def mouseReleaseEvent(self, event): + if self.input_field: + print("here", self.input_field) + self.input_field.mouseReleaseEvent(event) + return super(GridLabel, self).mouseReleaseEvent(event) + + class DictInvisible(QtWidgets.QWidget, SettingObject): # TODO is not overridable by itself value_changed = QtCore.Signal(object) @@ -2679,7 +2722,7 @@ class DictInvisible(QtWidgets.QWidget, SettingObject): if not klass.expand_in_grid: label = child_configuration.get("label") if label is not None: - label_widget = QtWidgets.QLabel(label, self) + label_widget = GridLabel(label, self) self.content_layout.addWidget( label_widget, row, 0, 1, 1, alignment=QtCore.Qt.AlignRight | QtCore.Qt.AlignTop @@ -2689,6 +2732,7 @@ class DictInvisible(QtWidgets.QWidget, SettingObject): item.value_changed.connect(self._on_value_change) if label_widget: + label_widget.input_field = item self.content_layout.addWidget(item, row, 1, 1, 1) else: self.content_layout.addWidget(item, row, 0, 1, 2) From dcb0db7937a94e438568308d942cbd13e76d48bc Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 18 Sep 2020 18:19:51 +0200 Subject: [PATCH 693/813] reverse testing changes --- .../tools/settings/settings/widgets/item_types.py | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 01dab9ccc9..3bbf352f8b 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -2661,18 +2661,6 @@ class DictWidget(QtWidgets.QWidget, SettingObject): return {self.key: values}, self.is_group -class GridLabel(QtWidgets.QLabel): - def __init__(self, *args, **kwargs): - super(GridLabel, self).__init__(*args, **kwargs) - self.input_field = None - - def mouseReleaseEvent(self, event): - if self.input_field: - print("here", self.input_field) - self.input_field.mouseReleaseEvent(event) - return super(GridLabel, self).mouseReleaseEvent(event) - - class DictInvisible(QtWidgets.QWidget, SettingObject): # TODO is not overridable by itself value_changed = QtCore.Signal(object) @@ -2722,7 +2710,7 @@ class DictInvisible(QtWidgets.QWidget, SettingObject): if not klass.expand_in_grid: label = child_configuration.get("label") if label is not None: - label_widget = GridLabel(label, self) + label_widget = QtWidget.QLabel(label, self) self.content_layout.addWidget( label_widget, row, 0, 1, 1, alignment=QtCore.Qt.AlignRight | QtCore.Qt.AlignTop @@ -2732,7 +2720,6 @@ class DictInvisible(QtWidgets.QWidget, SettingObject): item.value_changed.connect(self._on_value_change) if label_widget: - label_widget.input_field = item self.content_layout.addWidget(item, row, 1, 1, 1) else: self.content_layout.addWidget(item, row, 0, 1, 2) From cc8faba5bffbabec5e6f0d6f7d253ebd4a59aa8d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 18 Sep 2020 18:20:52 +0200 Subject: [PATCH 694/813] typo fix --- 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 3bbf352f8b..bf9155f765 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -2710,7 +2710,7 @@ class DictInvisible(QtWidgets.QWidget, SettingObject): if not klass.expand_in_grid: label = child_configuration.get("label") if label is not None: - label_widget = QtWidget.QLabel(label, self) + label_widget = QtWidgets.QLabel(label, self) self.content_layout.addWidget( label_widget, row, 0, 1, 1, alignment=QtCore.Qt.AlignRight | QtCore.Qt.AlignTop From bbab7178c7ca1637ccdbcf7291a9209ebcd5954b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 21 Sep 2020 11:53:27 +0200 Subject: [PATCH 695/813] DictWidget can be used as widget --- .../settings/settings/widgets/item_types.py | 43 +++++++++++++------ 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 68cebf6642..8a88fbf829 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -2132,18 +2132,26 @@ class DictWidget(QtWidgets.QWidget, SettingObject): self, input_data, parent, as_widget=False, label_widget=None, parent_widget=None ): - if as_widget: - raise TypeError("Can't use \"{}\" as widget item.".format( - self.__class__.__name__ - )) - if parent_widget is None: parent_widget = parent super(DictWidget, self).__init__(parent_widget) - self.setObjectName("DictWidget") self.initial_attributes(input_data, parent, as_widget) + self.input_fields = [] + + self.checkbox_widget = None + self.checkbox_key = input_data.get("checkbox_key") + + self.label_widget = label_widget + + if self.as_widget: + self._ui_as_widget(input_data) + else: + self._ui_as_item(input_data) + + def _ui_as_item(self, input_data): + self.key = input_data["key"] if input_data.get("highlight_content", False): content_state = "hightlighted" bottom_margin = 5 @@ -2151,10 +2159,6 @@ class DictWidget(QtWidgets.QWidget, SettingObject): content_state = "" bottom_margin = 0 - self.input_fields = [] - - self.key = input_data["key"] - main_layout = QtWidgets.QHBoxLayout(self) main_layout.setContentsMargins(0, 0, 0, 0) main_layout.setSpacing(0) @@ -2177,9 +2181,6 @@ class DictWidget(QtWidgets.QWidget, SettingObject): self.label_widget = body_widget.label_widget - self.checkbox_widget = None - self.checkbox_key = input_data.get("checkbox_key") - for child_data in input_data.get("children", []): self.add_children_gui(child_data) @@ -2194,6 +2195,22 @@ class DictWidget(QtWidgets.QWidget, SettingObject): else: body_widget.hide_toolbox(hide_content=False) + def _ui_as_widget(self, input_data): + body = QtWidgets.QWidget(self) + body.setObjectName("DictAsWidgetBody") + + content_layout = QtWidgets.QGridLayout(body) + content_layout.setContentsMargins(5, 5, 5, 5) + self.content_layout = content_layout + + layout = QtWidgets.QHBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(5) + layout.addWidget(body) + + for child_configuration in input_data["children"]: + self.add_children_gui(child_configuration) + def add_children_gui(self, child_configuration): item_type = child_configuration["type"] klass = TypeToKlass.types.get(item_type) From bd5e1ae310168996eda083fc008b190d8f671a64 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 21 Sep 2020 11:55:16 +0200 Subject: [PATCH 696/813] minor tweaks in DictWidget to be able used as widget --- .../settings/settings/widgets/item_types.py | 63 ++++++++++++------- 1 file changed, 40 insertions(+), 23 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 8a88fbf829..83805d9948 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -2297,8 +2297,12 @@ class DictWidget(QtWidgets.QWidget, SettingObject): item.set_as_overriden() def update_default_values(self, parent_values): + # Make sure this is set to False + self._state = None + self._child_state = None + value = NOT_SET - if self._as_widget: + if self.as_widget: value = parent_values elif parent_values is not NOT_SET: value = parent_values.get(self.key, NOT_SET) @@ -2307,15 +2311,21 @@ class DictWidget(QtWidgets.QWidget, SettingObject): item.update_default_values(value) def update_studio_values(self, parent_values): + # Make sure this is set to False + self._state = None + self._child_state = None value = NOT_SET - if parent_values is not NOT_SET: - value = parent_values.get(self.key, NOT_SET) + if self.as_widget: + value = parent_values + else: + if parent_values is not NOT_SET: + value = parent_values.get(self.key, NOT_SET) - self._has_studio_override = False - if self.is_group and value is not NOT_SET: - self._has_studio_override = True + self._has_studio_override = False + if self.is_group and value is not NOT_SET: + self._has_studio_override = True - self._had_studio_override = bool(self._has_studio_override) + self._had_studio_override = bool(self._has_studio_override) for item in self.input_fields: item.update_studio_values(value) @@ -2325,37 +2335,40 @@ class DictWidget(QtWidgets.QWidget, SettingObject): self._state = None self._child_state = None - metadata = {} - groups = tuple() - override_values = NOT_SET - if parent_values is not NOT_SET: - metadata = parent_values.get(METADATA_KEY) or metadata - groups = metadata.get("groups") or groups - override_values = parent_values.get(self.key, override_values) + if not self.as_widget: + metadata = {} + groups = tuple() + override_values = NOT_SET + if parent_values is not NOT_SET: + metadata = parent_values.get(METADATA_KEY) or metadata + groups = metadata.get("groups") or groups + override_values = parent_values.get(self.key, override_values) - self._is_overriden = self.key in groups + self._is_overriden = self.key in groups for item in self.input_fields: item.apply_overrides(override_values) - if not self._is_overriden: - self._is_overriden = ( - self.is_group - and self.is_overidable - and self.child_overriden - ) - self._was_overriden = bool(self._is_overriden) + if not self.as_widget: + if not self._is_overriden: + self._is_overriden = ( + self.is_group + and self.is_overidable + and self.child_overriden + ) + self._was_overriden = bool(self._is_overriden) def _on_value_change(self, item=None): if self.ignore_value_changes: return - if self.is_group and not self.any_parent_as_widget: + if self.is_group and not (self.as_widget or self.any_parent_as_widget): if self.is_overidable: self._is_overriden = True else: self._has_studio_override = True + # TODO check if this is required self.hierarchical_style_update() self.value_changed.emit(self) @@ -2368,6 +2381,10 @@ class DictWidget(QtWidgets.QWidget, SettingObject): self.update_style() def update_style(self, is_overriden=None): + # TODO add style update when used as widget + if self.as_widget: + return + child_has_studio_override = self.child_has_studio_override child_modified = self.child_modified child_invalid = self.child_invalid From cbbbefc76dd939cbd5e327086825f4eb5d508033 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 21 Sep 2020 11:56:25 +0200 Subject: [PATCH 697/813] "dict-item" is set to DictWidget for backwards compatibility --- pype/tools/settings/settings/widgets/item_types.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 83805d9948..e6fb2d32e3 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -3387,7 +3387,8 @@ TypeToKlass.types["path-input"] = PathInputWidget TypeToKlass.types["raw-json"] = RawJsonWidget TypeToKlass.types["list"] = ListWidget TypeToKlass.types["dict-modifiable"] = ModifiableDict -TypeToKlass.types["dict-item"] = DictItemWidget +# DEPRECATED - remove when removed from schemas +TypeToKlass.types["dict-item"] = DictWidget TypeToKlass.types["dict"] = DictWidget TypeToKlass.types["dict-invisible"] = DictInvisible TypeToKlass.types["path-widget"] = PathWidget From 4bd7a6c4fa2ddabf5a90e0ec39d80c970e069da8 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 21 Sep 2020 11:56:45 +0200 Subject: [PATCH 698/813] removed DictItemWidget as is not used --- .../settings/settings/widgets/item_types.py | 95 ------------------- 1 file changed, 95 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index e6fb2d32e3..3cf9cfcc74 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -1192,101 +1192,6 @@ class RawJsonWidget(QtWidgets.QWidget, InputObject): return self.text_input.json_value() -class DictItemWidget(QtWidgets.QWidget, SettingObject): - default_input_value = True - value_changed = QtCore.Signal(object) - - def __init__( - self, input_data, parent, - as_widget=False, label_widget=None, parent_widget=None - ): - if parent_widget is None: - parent_widget = parent - super(DictItemWidget, self).__init__(parent_widget) - - self.initial_attributes(input_data, parent, as_widget) - - if not self._as_widget: - raise TypeError("{} can be used only as widget.".format( - self.__class__.__name__ - )) - - self.input_fields = [] - - body = QtWidgets.QWidget(self) - body.setObjectName("DictItemWidgetBody") - - content_layout = QtWidgets.QGridLayout(body) - content_layout.setContentsMargins(5, 5, 5, 5) - self.content_layout = content_layout - - layout = QtWidgets.QHBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(5) - layout.addWidget(body) - - self.label_widget = label_widget - - for child_configuration in input_data["children"]: - self.add_children_gui(child_configuration) - - def add_children_gui(self, child_configuration): - item_type = child_configuration["type"] - klass = TypeToKlass.types.get(item_type) - - row = self.content_layout.rowCount() - if not getattr(klass, "is_input_type", False): - item = klass(child_configuration, self) - self.content_layout.addWidget(item, row, 0, 1, 2) - return item - - label_widget = None - if not klass.expand_in_grid: - label = child_configuration.get("label") - if label is not None: - label_widget = QtWidgets.QLabel(label, self) - self.content_layout.addWidget( - label_widget, row, 0, 1, 1, - alignment=QtCore.Qt.AlignRight | QtCore.Qt.AlignTop - ) - - item = klass(child_configuration, self, label_widget=label_widget) - item.value_changed.connect(self._on_value_change) - - if label_widget: - self.content_layout.addWidget(item, row, 1, 1, 1) - else: - self.content_layout.addWidget(item, row, 0, 1, 2) - - self.input_fields.append(item) - return item - - def hierarchical_style_update(self): - for input_field in self.input_fields: - input_field.hierarchical_style_update() - - def _on_value_change(self, item=None): - self.value_changed.emit(self) - - def update_default_values(self, parent_values): - for input_field in self.input_fields: - input_field.update_default_values(parent_values) - - def update_studio_values(self, parent_values): - for input_field in self.input_fields: - input_field.update_studio_values(parent_values) - - def apply_overrides(self, parent_values): - for input_field in self.input_fields: - input_field.apply_overrides(parent_values) - - def item_value(self): - output = {} - for input_field in self.input_fields: - output.update(input_field.config_value()) - return output - - class ListItem(QtWidgets.QWidget, SettingObject): _btn_size = 20 value_changed = QtCore.Signal(object) From ba8b1b8015b5821996b1b4140d6d2831c1394f03 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 21 Sep 2020 11:57:00 +0200 Subject: [PATCH 699/813] changed object name in styles --- pype/tools/settings/settings/style/style.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/tools/settings/settings/style/style.css b/pype/tools/settings/settings/style/style.css index 221f297219..3f648abef8 100644 --- a/pype/tools/settings/settings/style/style.css +++ b/pype/tools/settings/settings/style/style.css @@ -152,7 +152,7 @@ QPushButton[btn-type="expand-toggle"] { background: #141a1f; } -#DictItemWidgetBody{ +#DictAsWidgetBody{ background: transparent; border: 2px solid #cccccc; border-radius: 5px; From 14a15c9a8cbedae0641583869b552a8a84af78b1 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 21 Sep 2020 11:57:35 +0200 Subject: [PATCH 700/813] current schemas in example does not have dict-item --- .../gui_schemas/projects_schema/1_plugins_gui_schema.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/1_plugins_gui_schema.json b/pype/tools/settings/settings/gui_schemas/projects_schema/1_plugins_gui_schema.json index f70495017e..f357b51dc5 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/1_plugins_gui_schema.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/1_plugins_gui_schema.json @@ -172,7 +172,7 @@ "type": "list", "key": "profiles", "label": "Profiles", - "object_type": "dict-item", + "object_type": "dict", "input_modifiers": { "children": [ { @@ -192,7 +192,7 @@ "label": "Output Definitions", "type": "dict-modifiable", "highlight_content": true, - "object_type": "dict-item", + "object_type": "dict", "input_modifiers": { "children": [ { From 83ff6bc85bd8c0016c97fca91e8ac07169ac4a58 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 21 Sep 2020 12:03:57 +0200 Subject: [PATCH 701/813] removed dict-item from readme --- pype/tools/settings/settings/README.md | 31 -------------------------- 1 file changed, 31 deletions(-) diff --git a/pype/tools/settings/settings/README.md b/pype/tools/settings/settings/README.md index 9d12467cc9..8fa0a4616a 100644 --- a/pype/tools/settings/settings/README.md +++ b/pype/tools/settings/settings/README.md @@ -207,37 +207,6 @@ } ``` -### dict-item -- item represents dictionary with strict keys in and data types of its values -- can be used only as widget (in `list` or `dict-modifiable`) - - only key modifier is `children` which is list of it's keys -- USAGE: e.g. List of dictionaries where each dictionary have same structure. - -``` -{ - "type": "list", - "key": "profiles", - "label": "Profiles", - "object_type": "dict-item", - "input_modifiers": { - "children": [ - { - "key": "families", - "label": "Families", - "type": "list", - "object_type": "text" - }, { - "key": "hosts", - "label": "Hosts", - "type": "list", - "object_type": "text" - } - ... - ] - } -} -``` - ### list-strict - input for strict number of items in list - each child item can be different type with different possible modifiers From 7361e86cb9341d9b92e092920711091a71342463 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 21 Sep 2020 12:04:50 +0200 Subject: [PATCH 702/813] added new usage to readme --- pype/tools/settings/settings/README.md | 43 +++++++++++++++++++++----- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/pype/tools/settings/settings/README.md b/pype/tools/settings/settings/README.md index db444eb3bd..969c9dc574 100644 --- a/pype/tools/settings/settings/README.md +++ b/pype/tools/settings/settings/README.md @@ -57,13 +57,18 @@ ## dict - this is another dictionary input wrapping more inputs but visually makes them different -- required keys are `"key"` under which will be stored and `"label"` which will be shown in GUI -- this input can be expandable - - that can be set with key `"expandable"` as `True`/`False` (Default: `True`) - - with key `"expanded"` as `True`/`False` can be set that is expanded when GUI is opened (Default: `False`) -- it is possible to add darker background with `"highlight_content"` (Default: `False`) - - darker background has limits of maximum applies after 3-4 nested highlighted items there is not difference in the color +- item may be used as widget (in `list` or `dict-modifiable`) + - in that case the only key modifier is `children` which is list of it's keys + - USAGE: e.g. List of dictionaries where each dictionary have same structure. +- item options if is not used as widget + - required keys are `"key"` under which will be stored and `"label"` which will be shown in GUI + - this input can be expandable + - that can be set with key `"expandable"` as `True`/`False` (Default: `True`) + - with key `"expanded"` as `True`/`False` can be set that is expanded when GUI is opened (Default: `False`) + - it is possible to add darker background with `"highlight_content"` (Default: `False`) + - darker background has limits of maximum applies after 3-4 nested highlighted items there is not difference in the color ``` +# Example { "key": "applications", "type": "dict", @@ -76,6 +81,30 @@ ...ITEMS... ] } + +# When used as widget +{ + "type": "list", + "key": "profiles", + "label": "Profiles", + "object_type": "dict-item", + "input_modifiers": { + "children": [ + { + "key": "families", + "label": "Families", + "type": "list", + "object_type": "text" + }, { + "key": "hosts", + "label": "Hosts", + "type": "list", + "object_type": "text" + } + ... + ] + } +} ``` ## Inputs for setting any kind of value (`Pure` inputs) @@ -234,7 +263,7 @@ - should wraps multiple inputs only visually - these does not have `"key"` key and do not allow to have `"is_file"` or `"is_group"` modifiers enabled -### dict-form +### form - DEPRECATED - may be used only in `dict` and `dict-invisible` where is currently used grid layout so form is not needed - item is kept as still may be used in specific cases From ac26fb31eb873ad784202d8575c4f56e7071e913 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 21 Sep 2020 18:01:23 +0200 Subject: [PATCH 703/813] implemented label widget used in grid layout --- .../settings/settings/widgets/widgets.py | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/pype/tools/settings/settings/widgets/widgets.py b/pype/tools/settings/settings/widgets/widgets.py index 400b9371fd..2a1f5fe804 100644 --- a/pype/tools/settings/settings/widgets/widgets.py +++ b/pype/tools/settings/settings/widgets/widgets.py @@ -226,3 +226,56 @@ class UnsavedChangesDialog(QtWidgets.QDialog): def on_discard_pressed(self): self.done(2) + + +class SpacerWidget(QtWidgets.QWidget): + def __init__(self, parent=None): + super(SpacerWidget, self).__init__(parent) + self.setAttribute(QtCore.Qt.WA_TranslucentBackground) + + +class GridLabelWidget(QtWidgets.QWidget): + def __init__(self, label, parent=None): + super(GridLabelWidget, self).__init__(parent) + + self.input_field = None + + self.properties = {} + + layout = QtWidgets.QVBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(0) + + label_proxy = QtWidgets.QWidget(self) + label_proxy_layout = QtWidgets.QHBoxLayout(label_proxy) + label_proxy_layout.setContentsMargins(0, 0, 0, 0) + label_proxy_layout.setSpacing(0) + + label_widget = QtWidgets.QLabel(label, label_proxy) + spacer_widget_h = SpacerWidget(label_proxy) + label_proxy_layout.addWidget( + spacer_widget_h, 0, alignment=QtCore.Qt.AlignRight + ) + label_proxy_layout.addWidget( + label_widget, 0, alignment=QtCore.Qt.AlignRight + ) + + spacer_widget_v = SpacerWidget(self) + + layout.addWidget(label_proxy, 0) + layout.addWidget(spacer_widget_v, 1) + + self.label_widget = label_widget + + def setProperty(self, name, value): + cur_value = self.properties.get(name) + if cur_value == value: + return + + self.label_widget.setProperty(name, value) + self.label_widget.style().polish(self.label_widget) + + def mouseReleaseEvent(self, event): + if self.input_field: + return self.input_field.show_actions_menu(event) + return super(GridLabelWidget, self).mouseReleaseEvent(event) From 65e9a6a2541f3cacc3f2a98c11c749a9798d50a4 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 21 Sep 2020 18:01:54 +0200 Subject: [PATCH 704/813] `show_actions_menu` is separate method now triggered with right click on item --- .../settings/settings/widgets/item_types.py | 124 ++++++++++-------- 1 file changed, 67 insertions(+), 57 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index fea78b713b..9f7d951241 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -285,65 +285,75 @@ class SettingObject: return "-".join(items) or "" + def show_actions_menu(self, event=None): + if event and event.button() != QtCore.Qt.RightButton: + return + + if not self.allow_actions: + if event: + return self.mouseReleaseEvent(event) + return + + menu = QtWidgets.QMenu() + + actions_mapping = {} + if self.child_modified: + action = QtWidgets.QAction("Discard changes") + actions_mapping[action] = self._discard_changes + menu.addAction(action) + + if ( + self.is_overidable + and not self.is_overriden + and not self.any_parent_is_group + ): + action = QtWidgets.QAction("Set project override") + actions_mapping[action] = self._set_as_overriden + menu.addAction(action) + + if ( + not self.is_overidable + and ( + self.has_studio_override + ) + ): + action = QtWidgets.QAction("Reset to pype default") + actions_mapping[action] = self._reset_to_pype_default + menu.addAction(action) + + if ( + not self.is_overidable + and not self.is_overriden + and not self.any_parent_is_group + and not self._had_studio_override + ): + action = QtWidgets.QAction("Set studio default") + actions_mapping[action] = self._set_studio_default + menu.addAction(action) + + if ( + not self.any_parent_overriden() + and (self.is_overriden or self.child_overriden) + ): + # TODO better label + action = QtWidgets.QAction("Remove project override") + actions_mapping[action] = self._remove_overrides + menu.addAction(action) + + if not actions_mapping: + action = QtWidgets.QAction("< No action >") + actions_mapping[action] = None + menu.addAction(action) + + result = menu.exec_(QtGui.QCursor.pos()) + if result: + to_run = actions_mapping[result] + if to_run: + to_run() + def mouseReleaseEvent(self, event): if self.allow_actions and event.button() == QtCore.Qt.RightButton: - menu = QtWidgets.QMenu() - - actions_mapping = {} - if self.child_modified: - action = QtWidgets.QAction("Discard changes") - actions_mapping[action] = self._discard_changes - menu.addAction(action) - - if ( - self.is_overidable - and not self.is_overriden - and not self.any_parent_is_group - ): - action = QtWidgets.QAction("Set project override") - actions_mapping[action] = self._set_as_overriden - menu.addAction(action) - - if ( - not self.is_overidable - and ( - self.has_studio_override - ) - ): - action = QtWidgets.QAction("Reset to pype default") - actions_mapping[action] = self._reset_to_pype_default - menu.addAction(action) - - if ( - not self.is_overidable - and not self.is_overriden - and not self.any_parent_is_group - and not self._had_studio_override - ): - action = QtWidgets.QAction("Set studio default") - actions_mapping[action] = self._set_studio_default - menu.addAction(action) - - if ( - not self.any_parent_overriden() - and (self.is_overriden or self.child_overriden) - ): - # TODO better label - action = QtWidgets.QAction("Remove project override") - actions_mapping[action] = self._remove_overrides - menu.addAction(action) - - if not actions_mapping: - action = QtWidgets.QAction("< No action >") - actions_mapping[action] = None - menu.addAction(action) - - result = menu.exec_(QtGui.QCursor.pos()) - if result: - to_run = actions_mapping[result] - if to_run: - to_run() - return + return self.show_actions_menu() mro = type(self).mro() index = mro.index(self.__class__) From 0d2110a6cce03fafe1682c5189904120c7777013 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 21 Sep 2020 18:03:28 +0200 Subject: [PATCH 705/813] GridLabelWidget is used in dict and dict-invisible --- .../settings/settings/widgets/item_types.py | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 9f7d951241..213bd00a1b 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -5,7 +5,8 @@ from Qt import QtWidgets, QtCore, QtGui from .widgets import ( ExpandingWidget, NumberSpinBox, - PathInput + PathInput, + GridLabelWidget ) from .lib import NOT_SET, METADATA_KEY, TypeToKlass, CHILD_OFFSET from avalon.vendor import qtawesome @@ -2227,16 +2228,14 @@ class DictWidget(QtWidgets.QWidget, SettingObject): if not klass.expand_in_grid: label = child_configuration.get("label") if label is not None: - label_widget = QtWidgets.QLabel(label, self) - self.content_layout.addWidget( - label_widget, row, 0, 1, 1, - alignment=QtCore.Qt.AlignRight | QtCore.Qt.AlignTop - ) + label_widget = GridLabelWidget(label, self) + self.content_layout.addWidget(label_widget, row, 0, 1, 1) item = klass(child_configuration, self, label_widget=label_widget) item.value_changed.connect(self._on_value_change) if label_widget: + label_widget.input_field = item self.content_layout.addWidget(item, row, 1, 1, 1) else: self.content_layout.addWidget(item, row, 0, 1, 2) @@ -2535,16 +2534,14 @@ class DictInvisible(QtWidgets.QWidget, SettingObject): if not klass.expand_in_grid: label = child_configuration.get("label") if label is not None: - label_widget = QtWidgets.QLabel(label, self) - self.content_layout.addWidget( - label_widget, row, 0, 1, 1, - alignment=QtCore.Qt.AlignRight | QtCore.Qt.AlignTop - ) + label_widget = GridLabelWidget(label, self) + self.content_layout.addWidget(label_widget, row, 0, 1, 1) item = klass(child_configuration, self, label_widget=label_widget) item.value_changed.connect(self._on_value_change) if label_widget: + label_widget.input_field = item self.content_layout.addWidget(item, row, 1, 1, 1) else: self.content_layout.addWidget(item, row, 0, 1, 2) From 4e5226cdc581e05b41730bf4780e982b48b06f49 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 22 Sep 2020 11:42:23 +0200 Subject: [PATCH 706/813] 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 3cf6962d3c344a45a809b2ee7450fc56bf763d43 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 22 Sep 2020 14:44:00 +0200 Subject: [PATCH 707/813] 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 c2d8868ac043bf1a041510d15d6696bb9848074f Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 22 Sep 2020 15:25:46 +0100 Subject: [PATCH 708/813] 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 709/813] 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 b193cc3b519d9c399c459b96f725c9cb556a358f Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 23 Sep 2020 12:37:05 +0200 Subject: [PATCH 710/813] fix(ftrack): filter only activated instances in hierarchy assets --- .../publish/integrate_hierarchy_ftrack.py | 69 ++++++++++++++++++- 1 file changed, 67 insertions(+), 2 deletions(-) diff --git a/pype/plugins/ftrack/publish/integrate_hierarchy_ftrack.py b/pype/plugins/ftrack/publish/integrate_hierarchy_ftrack.py index cc569ce2d1..ed328ac30d 100644 --- a/pype/plugins/ftrack/publish/integrate_hierarchy_ftrack.py +++ b/pype/plugins/ftrack/publish/integrate_hierarchy_ftrack.py @@ -2,6 +2,7 @@ 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 @@ -39,10 +40,74 @@ class IntegrateHierarchyToFtrack(pyblish.api.ContextPlugin): optional = False def process(self, context): + # additional inner methods + def _get_assets(input_dict): + """ Returns only asset dictionary. + Usually the last part of deep dictionary which + is not having any children + """ + for key in input_dict.keys(): + # check if child key is available + if input_dict[key].get("childs"): + # loop deeper + return _get_assets(input_dict[key]["childs"]) + else: + # give the dictionary with assets + return input_dict + + def _set_assets(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 _set_assets(input_dict[key]["childs"]): + # if one level deeper is still children available + # then process farther + _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 + + # processing starts here + active_assets = [] self.context = context - if "hierarchyContext" not in context.data: + if "hierarchyContext" not in self.context.data: return + hierarchy_context = self.context.data["hierarchyContext"] + hierarchy_assets = _get_assets(hierarchy_context) + + # filter only the active publishing insatnces + for instance in self.context: + if instance.data.get("publish") is False: + continue + + if not instance.data.get("asset"): + continue + + 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} + + # modify the hierarchy context so there are only fitred assets + _set_assets(hierarchy_context, new_hierarchy_assets) + + 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) @@ -55,7 +120,7 @@ class IntegrateHierarchyToFtrack(pyblish.api.ContextPlugin): self.ft_project = None - input_data = context.data["hierarchyContext"] + input_data = hierarchy_context # disable termporarily ftrack project's autosyncing if auto_sync_state: From 8835617fe4cd8cf06b08f35f519ea17ad7f46a0b Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 23 Sep 2020 12:37:37 +0200 Subject: [PATCH 711/813] fix(standalonepublisher): adding commonly used extension for video --- pype/plugins/standalonepublisher/publish/collect_editorial.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/plugins/standalonepublisher/publish/collect_editorial.py b/pype/plugins/standalonepublisher/publish/collect_editorial.py index a31125d9a8..5e6fd106e4 100644 --- a/pype/plugins/standalonepublisher/publish/collect_editorial.py +++ b/pype/plugins/standalonepublisher/publish/collect_editorial.py @@ -32,7 +32,7 @@ class CollectEditorial(pyblish.api.InstancePlugin): actions = [] # presets - extensions = [".mov"] + extensions = [".mov", ".mp4"] def process(self, instance): # remove context test attribute From 7cc52fe28a332f9af3b27377e7a425d356745252 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 23 Sep 2020 12:55:02 +0200 Subject: [PATCH 712/813] fix(ftrack, global): moving asset filter to avalon hierarchy integrator --- .../publish/integrate_hierarchy_ftrack.py | 59 ----------------- .../publish/extract_hierarchy_avalon.py | 63 ++++++++++++++++++- 2 files changed, 62 insertions(+), 60 deletions(-) diff --git a/pype/plugins/ftrack/publish/integrate_hierarchy_ftrack.py b/pype/plugins/ftrack/publish/integrate_hierarchy_ftrack.py index ed328ac30d..0616382569 100644 --- a/pype/plugins/ftrack/publish/integrate_hierarchy_ftrack.py +++ b/pype/plugins/ftrack/publish/integrate_hierarchy_ftrack.py @@ -40,70 +40,11 @@ class IntegrateHierarchyToFtrack(pyblish.api.ContextPlugin): optional = False def process(self, context): - # additional inner methods - def _get_assets(input_dict): - """ Returns only asset dictionary. - Usually the last part of deep dictionary which - is not having any children - """ - for key in input_dict.keys(): - # check if child key is available - if input_dict[key].get("childs"): - # loop deeper - return _get_assets(input_dict[key]["childs"]) - else: - # give the dictionary with assets - return input_dict - - def _set_assets(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 _set_assets(input_dict[key]["childs"]): - # if one level deeper is still children available - # then process farther - _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 - - # processing starts here - active_assets = [] self.context = context if "hierarchyContext" not in self.context.data: return hierarchy_context = self.context.data["hierarchyContext"] - hierarchy_assets = _get_assets(hierarchy_context) - - # filter only the active publishing insatnces - for instance in self.context: - if instance.data.get("publish") is False: - continue - - if not instance.data.get("asset"): - continue - - 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} - - # modify the hierarchy context so there are only fitred assets - _set_assets(hierarchy_context, new_hierarchy_assets) self.log.debug( f"__ hierarchy_context: `{pformat(hierarchy_context)}`") diff --git a/pype/plugins/global/publish/extract_hierarchy_avalon.py b/pype/plugins/global/publish/extract_hierarchy_avalon.py index 1d8191f2e3..4253c35929 100644 --- a/pype/plugins/global/publish/extract_hierarchy_avalon.py +++ b/pype/plugins/global/publish/extract_hierarchy_avalon.py @@ -10,6 +10,7 @@ class ExtractHierarchyToAvalon(pyblish.api.ContextPlugin): families = ["clip", "shot"] def process(self, context): + # processing starts here if "hierarchyContext" not in context.data: self.log.info("skipping IntegrateHierarchyToAvalon") return @@ -17,7 +18,29 @@ class ExtractHierarchyToAvalon(pyblish.api.ContextPlugin): if not io.Session: io.install() - input_data = context.data["hierarchyContext"] + 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: + continue + + if not instance.data.get("asset"): + continue + + 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} + + # modify the hierarchy context so there are only fitred assets + self._set_assets(hierarchy_context, new_hierarchy_assets) + + input_data = context.data["hierarchyContext"] = hierarchy_context + self.project = None self.import_to_avalon(input_data) @@ -149,3 +172,41 @@ class ExtractHierarchyToAvalon(pyblish.api.ContextPlugin): entity_id = io.insert_one(item).inserted_id return io.find_one({"_id": entity_id}) + + def _get_assets(self, input_dict): + """ Returns only asset dictionary. + Usually the last part of deep dictionary which + is not having any children + """ + for key in input_dict.keys(): + # check if child key is available + if input_dict[key].get("childs"): + # loop deeper + return self._get_assets(input_dict[key]["childs"]) + else: + # give the dictionary with assets + return input_dict + + 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 f7104a1f7ecb9dd509478c638eaafaeade854f08 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 23 Sep 2020 16:49:28 +0200 Subject: [PATCH 713/813] first commit recreated entity before any modification are applied --- pype/modules/ftrack/lib/avalon_sync.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pype/modules/ftrack/lib/avalon_sync.py b/pype/modules/ftrack/lib/avalon_sync.py index 65a59452da..03124ab10d 100644 --- a/pype/modules/ftrack/lib/avalon_sync.py +++ b/pype/modules/ftrack/lib/avalon_sync.py @@ -2144,6 +2144,7 @@ class SyncEntitiesFactory: "name": _name, "parent": parent_entity }) + self.session.commit() final_entity = {} for k, v in av_entity.items(): From bff280b867b70418acf70a90cc053d50ef81d6f5 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 23 Sep 2020 16:49:50 +0200 Subject: [PATCH 714/813] fixed way how to access to attribute `self._avalon_ents` --- pype/modules/ftrack/events/event_sync_to_avalon.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pype/modules/ftrack/events/event_sync_to_avalon.py b/pype/modules/ftrack/events/event_sync_to_avalon.py index 314871f5b3..7f33d7b660 100644 --- a/pype/modules/ftrack/events/event_sync_to_avalon.py +++ b/pype/modules/ftrack/events/event_sync_to_avalon.py @@ -878,8 +878,9 @@ class SyncToAvalonEvent(BaseEvent): self.process_session.commit() found_idx = None - for idx, _entity in enumerate(self._avalon_ents): - if _entity["_id"] == avalon_entity["_id"]: + proj_doc, asset_docs = self._avalon_ents + for idx, asset_doc in enumerate(asset_docs): + if asset_doc["_id"] == avalon_entity["_id"]: found_idx = idx break @@ -894,7 +895,8 @@ class SyncToAvalonEvent(BaseEvent): new_entity_id ) # Update cached entities - self._avalon_ents[found_idx] = avalon_entity + asset_docs[found_idx] = avalon_entity + self._avalon_ents = proj_doc, asset_docs if self._avalon_ents_by_id is not None: mongo_id = avalon_entity["_id"] From dca974af2b288b25abd5e6639ab3a555d6920711 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 23 Sep 2020 16:49:58 +0200 Subject: [PATCH 715/813] added debugging logs --- .../ftrack/events/event_sync_to_avalon.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/pype/modules/ftrack/events/event_sync_to_avalon.py b/pype/modules/ftrack/events/event_sync_to_avalon.py index 7f33d7b660..d0c9ea2e96 100644 --- a/pype/modules/ftrack/events/event_sync_to_avalon.py +++ b/pype/modules/ftrack/events/event_sync_to_avalon.py @@ -717,6 +717,9 @@ class SyncToAvalonEvent(BaseEvent): if not self.ftrack_removed: return ent_infos = self.ftrack_removed + self.log.debug( + "Processing removed entities: {}".format(str(ent_infos)) + ) removable_ids = [] recreate_ents = [] removed_names = [] @@ -1260,6 +1263,10 @@ class SyncToAvalonEvent(BaseEvent): if not ent_infos: return + self.log.debug( + "Processing renamed entities: {}".format(str(ent_infos)) + ) + renamed_tasks = {} not_found = {} changeable_queue = queue.Queue() @@ -1455,6 +1462,10 @@ class SyncToAvalonEvent(BaseEvent): if not ent_infos: return + self.log.debug( + "Processing added entities: {}".format(str(ent_infos)) + ) + cust_attrs, hier_attrs = self.avalon_cust_attrs entity_type_conf_ids = {} # Skip if already exit in avalon db or tasks entities @@ -1731,6 +1742,10 @@ class SyncToAvalonEvent(BaseEvent): if not self.ftrack_moved: return + self.log.debug( + "Processing moved entities: {}".format(str(self.ftrack_moved)) + ) + ftrack_moved = {k: v for k, v in sorted( self.ftrack_moved.items(), key=(lambda line: len( @@ -1861,6 +1876,10 @@ class SyncToAvalonEvent(BaseEvent): if not self.ftrack_updated: return + self.log.debug( + "Processing updated entities: {}".format(str(self.ftrack_updated)) + ) + ent_infos = self.ftrack_updated ftrack_mongo_mapping = {} not_found_ids = [] From ce29b1e8623e3dba1436db4fc96d3d3d1ea9b86e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 24 Sep 2020 10:38:18 +0200 Subject: [PATCH 716/813] append in terminal model adds all items at once instead of one by one --- pype/tools/pyblish_pype/model.py | 66 +++++++++++++++++--------------- 1 file changed, 35 insertions(+), 31 deletions(-) diff --git a/pype/tools/pyblish_pype/model.py b/pype/tools/pyblish_pype/model.py index fdcdffd33f..05699043d8 100644 --- a/pype/tools/pyblish_pype/model.py +++ b/pype/tools/pyblish_pype/model.py @@ -1145,48 +1145,52 @@ class TerminalModel(QtGui.QStandardItemModel): return prepared_records - def append(self, record_item): - record_type = record_item["type"] + def append(self, record_items): + all_record_items = [] + for record_item in record_items: + record_type = record_item["type"] - terminal_item_type = None - if record_type == "record": - for level, _type in self.level_to_record: - if level > record_item["levelno"]: - break - terminal_item_type = _type + terminal_item_type = None + if record_type == "record": + for level, _type in self.level_to_record: + if level > record_item["levelno"]: + break + terminal_item_type = _type - else: - terminal_item_type = record_type + else: + terminal_item_type = record_type - icon_color = self.item_icon_colors.get(terminal_item_type) - icon_name = self.item_icon_name.get(record_type) + icon_color = self.item_icon_colors.get(terminal_item_type) + icon_name = self.item_icon_name.get(record_type) - top_item_icon = None - if icon_color and icon_name: - top_item_icon = QAwesomeIconFactory.icon(icon_name, icon_color) + top_item_icon = None + if icon_color and icon_name: + top_item_icon = QAwesomeIconFactory.icon(icon_name, icon_color) - label = record_item["label"].split("\n")[0] + label = record_item["label"].split("\n")[0] - top_item = QtGui.QStandardItem() - top_item.setData(TerminalLabelType, Roles.TypeRole) - top_item.setData(terminal_item_type, Roles.TerminalItemTypeRole) - top_item.setData(label, QtCore.Qt.DisplayRole) - top_item.setFlags( - QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled - ) + top_item = QtGui.QStandardItem() + all_record_items.append(top_item) - if top_item_icon: - top_item.setData(top_item_icon, QtCore.Qt.DecorationRole) + detail_item = TerminalDetailItem(record_item) + top_item.appendRow(detail_item) - self.appendRow(top_item) + top_item.setData(TerminalLabelType, Roles.TypeRole) + top_item.setData(terminal_item_type, Roles.TerminalItemTypeRole) + top_item.setData(label, QtCore.Qt.DisplayRole) + top_item.setFlags( + QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled + ) - detail_item = TerminalDetailItem(record_item) - detail_item.setData(TerminalDetailType, Roles.TypeRole) - top_item.appendRow(detail_item) + if top_item_icon: + top_item.setData(top_item_icon, QtCore.Qt.DecorationRole) + + detail_item.setData(TerminalDetailType, Roles.TypeRole) + + self.invisibleRootItem().appendRows(all_record_items) def update_with_result(self, result): - for record in result["records"]: - self.append(record) + self.append(result["records"]) class TerminalProxy(QtCore.QSortFilterProxyModel): From 4e193159816a87c37339a742b0bc42e6324f574f Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 24 Sep 2020 10:38:31 +0200 Subject: [PATCH 717/813] info messages are added same way as records --- pype/tools/pyblish_pype/window.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/tools/pyblish_pype/window.py b/pype/tools/pyblish_pype/window.py index 76f31e2442..2a037ba4bc 100644 --- a/pype/tools/pyblish_pype/window.py +++ b/pype/tools/pyblish_pype/window.py @@ -1087,10 +1087,10 @@ class Window(QtWidgets.QDialog): info.setText(message) # Include message in terminal - self.terminal_model.append({ + self.terminal_model.append([{ "label": message, "type": "info" - }) + }]) self.animation_info_msg.stop() self.animation_info_msg.start() From 2e8f5ee55cf460e118d16647af34918f43123751 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 24 Sep 2020 12:47:08 +0200 Subject: [PATCH 718/813] 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 719/813] 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 997b11399ee783d759c38e438c8695208e42d58d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 24 Sep 2020 16:25:41 +0200 Subject: [PATCH 720/813] implemented `_add_intent_to_context` for adding intent to context data --- pype/tools/pyblish_pype/window.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/pype/tools/pyblish_pype/window.py b/pype/tools/pyblish_pype/window.py index 2a037ba4bc..e4593d85ad 100644 --- a/pype/tools/pyblish_pype/window.py +++ b/pype/tools/pyblish_pype/window.py @@ -563,6 +563,20 @@ class Window(QtWidgets.QDialog): ): instance_item.setData(enable_value, Roles.IsEnabledRole) + def _add_intent_to_context(self): + if ( + self.intent_model.has_items + and "intent" not in self.controller.context.data + ): + idx = self.intent_model.index(self.intent_box.currentIndex(), 0) + intent_value = self.intent_model.data(idx, Roles.IntentItemValue) + intent_label = self.intent_model.data(idx, QtCore.Qt.DisplayRole) + + self.controller.context.data["intent"] = { + "value": intent_value, + "label": intent_label + } + def on_instance_toggle(self, index, state=None): """An item is requesting to be toggled""" if not index.data(Roles.IsOptionalRole): From dc4220e4745dbcaae8c9112c67b9e95bd739ffb1 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 24 Sep 2020 16:26:07 +0200 Subject: [PATCH 721/813] removed `on_intent_changed` since it's not helpful --- pype/tools/pyblish_pype/window.py | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/pype/tools/pyblish_pype/window.py b/pype/tools/pyblish_pype/window.py index e4593d85ad..5b4e806271 100644 --- a/pype/tools/pyblish_pype/window.py +++ b/pype/tools/pyblish_pype/window.py @@ -225,7 +225,6 @@ class Window(QtWidgets.QDialog): intent_model = model.IntentModel() intent_box.setModel(intent_model) - intent_box.currentIndexChanged.connect(self.on_intent_changed) comment_intent_widget = QtWidgets.QWidget() comment_intent_layout = QtWidgets.QHBoxLayout(comment_intent_widget) @@ -666,18 +665,6 @@ class Window(QtWidgets.QDialog): """The user has typed a comment.""" self.controller.context.data["comment"] = self.comment_box.text() - def on_intent_changed(self): - idx = self.intent_model.index(self.intent_box.currentIndex(), 0) - intent_value = self.intent_model.data(idx, Roles.IntentItemValue) - intent_label = self.intent_model.data(idx, QtCore.Qt.DisplayRole) - - # TODO move to play - if self.controller.context: - self.controller.context.data["intent"] = { - "value": intent_value, - "label": intent_label - } - def on_about_to_process(self, plugin, instance): """Reflect currently running pair in GUI""" if instance is None: @@ -768,8 +755,6 @@ class Window(QtWidgets.QDialog): self.comment_box.setText(comment or None) self.comment_box.setEnabled(True) - if self.intent_model.has_items: - self.on_intent_changed() self.intent_box.setEnabled(True) # Refresh tab From 16e52ccff289b28875c20f24022e444280c87174 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 24 Sep 2020 16:26:25 +0200 Subject: [PATCH 722/813] `_add_intent_to_context` is executed on play or validate button --- pype/tools/pyblish_pype/window.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pype/tools/pyblish_pype/window.py b/pype/tools/pyblish_pype/window.py index 5b4e806271..b7aada5ff7 100644 --- a/pype/tools/pyblish_pype/window.py +++ b/pype/tools/pyblish_pype/window.py @@ -631,12 +631,16 @@ class Window(QtWidgets.QDialog): self.comment_box.setEnabled(False) self.intent_box.setEnabled(False) + self._add_intent_to_context() + self.validate() def on_play_clicked(self): self.comment_box.setEnabled(False) self.intent_box.setEnabled(False) + self._add_intent_to_context() + self.publish() def on_reset_clicked(self): From 8627a9613e72ef2fb501cebfa22fc4b08d370f66 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 24 Sep 2020 16:26:45 +0200 Subject: [PATCH 723/813] extract burnin checks if intent value is valid before adding label --- pype/plugins/global/publish/extract_burnin.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pype/plugins/global/publish/extract_burnin.py b/pype/plugins/global/publish/extract_burnin.py index 6e8da1b054..b81cfbc050 100644 --- a/pype/plugins/global/publish/extract_burnin.py +++ b/pype/plugins/global/publish/extract_burnin.py @@ -316,7 +316,9 @@ class ExtractBurnin(pype.api.Extractor): intent_label = context.data.get("intent") if intent_label and isinstance(intent_label, dict): - intent_label = intent_label.get("label") + value = intent_label.get("value") + if value: + intent_label = intent_label.get("label") if intent_label: burnin_data["intent"] = intent_label From 7104ac3fbce20b3bb2391e1db781d60b074d85e1 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 24 Sep 2020 17:30:08 +0200 Subject: [PATCH 724/813] 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 725/813] 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 fec340f5ba5bea5864a95e65ec2c525bcf4c16d0 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 25 Sep 2020 11:20:32 +0200 Subject: [PATCH 726/813] #180 - Changed tasks to dictionaries Changed both for config ("tasks":{TYPE: {"short_name":""}}) and assets ("tasks": {"TASK_NAME": {"type":config.tasks.TYPE}}) --- pype/modules/ftrack/lib/avalon_sync.py | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/pype/modules/ftrack/lib/avalon_sync.py b/pype/modules/ftrack/lib/avalon_sync.py index e8d5ef0093..68b54f9456 100644 --- a/pype/modules/ftrack/lib/avalon_sync.py +++ b/pype/modules/ftrack/lib/avalon_sync.py @@ -24,9 +24,9 @@ log = Logger().get_logger(__name__) # Current schemas for avalon types EntitySchemas = { - "project": "avalon-core:project-2.0", + "project": "avalon-core:project-2.1", "asset": "avalon-core:asset-3.0", - "config": "avalon-core:config-1.0" + "config": "avalon-core:config-1.1" } # Group name of custom attributes @@ -123,7 +123,8 @@ def from_dict_to_set(data): if _key is not None: new_key = "{}.{}".format(_key, key) - if not isinstance(value, dict): + if not isinstance(value, dict) or \ + (isinstance(value, dict) and not bool(value)): # empty dic result["$set"][new_key] = value continue dict_queue.put((new_key, value)) @@ -421,7 +422,7 @@ class SyncEntitiesFactory: "custom_attributes": {}, "hier_attrs": {}, "avalon_attrs": {}, - "tasks": [] + "tasks": {} }) for entity in all_project_entities: @@ -433,8 +434,8 @@ class SyncEntitiesFactory: elif entity_type_low == "task": # enrich task info with additional metadata - task = {"name": entity["name"], "type": entity["type"]["name"]} - entities_dict[parent_id]["tasks"].append(task) + task = {"type": entity["type"]["name"]} + entities_dict[parent_id]["tasks"][entity["name"]] = task continue entity_id = entity["id"] @@ -656,8 +657,8 @@ class SyncEntitiesFactory: name = entity_dict["name"] entity_type = entity_dict["entity_type"] # Tasks must be checked too - for task in entity_dict["tasks"]: - task_name = task.get("name") + for task in entity_dict["tasks"].items(): + task_name, task = task passed = task_name if passed is None: passed = check_regex( @@ -1137,11 +1138,11 @@ class SyncEntitiesFactory: if not msg or not items: continue self.report_items["warning"][msg] = items - tasks = [] + tasks = {} for tt in task_types: - tasks.append({"name": tt["name"], + tasks[tt["name"]] = { "short_name": get_task_short_name(tt["name"]) - }) + } self.entities_dict[id]["final_entity"]["config"] = { "tasks": tasks, "apps": proj_apps @@ -1156,7 +1157,7 @@ class SyncEntitiesFactory: data["parents"] = parents data["hierarchy"] = hierarchy - data["tasks"] = self.entities_dict[id].pop("tasks", []) + data["tasks"] = self.entities_dict[id].pop("tasks", {}) self.entities_dict[id]["final_entity"]["data"] = data self.entities_dict[id]["final_entity"]["type"] = "asset" From f8e558ca42d9a7e758535d7b7a2162788a68801f Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 25 Sep 2020 12:24:02 +0200 Subject: [PATCH 727/813] #180 - Changed tasks to dictionaries Modifications based on change --- pype/hosts/nukestudio/tags.py | 4 ++-- .../clockify/launcher_actions/ClockifySync.py | 2 +- .../global/publish/extract_hierarchy_avalon.py | 12 +++++++----- pype/plugins/maya/publish/collect_yeti_cache.py | 2 +- pype/plugins/nukestudio/publish/collect_shots.py | 2 +- 5 files changed, 12 insertions(+), 10 deletions(-) diff --git a/pype/hosts/nukestudio/tags.py b/pype/hosts/nukestudio/tags.py index c2b1d0d728..36edb16da6 100644 --- a/pype/hosts/nukestudio/tags.py +++ b/pype/hosts/nukestudio/tags.py @@ -71,8 +71,8 @@ def add_tags_from_presets(): # Get project task types. tasks = io.find_one({"type": "project"})["config"]["tasks"] nks_pres_tags["[Tasks]"] = {} - for task in tasks: - nks_pres_tags["[Tasks]"][task["name"]] = { + for task_name, _ in tasks.items(): + nks_pres_tags["[Tasks]"][task_name] = { "editable": "1", "note": "", "icon": { diff --git a/pype/modules/clockify/launcher_actions/ClockifySync.py b/pype/modules/clockify/launcher_actions/ClockifySync.py index a77c038076..422a346023 100644 --- a/pype/modules/clockify/launcher_actions/ClockifySync.py +++ b/pype/modules/clockify/launcher_actions/ClockifySync.py @@ -30,7 +30,7 @@ class ClockifySync(api.Action): projects_info = {} for project in projects_to_sync: - task_types = [task['name'] for task in project['config']['tasks']] + task_types = project['config']['tasks'].keys() projects_info[project['name']] = task_types clockify_projects = self.clockapi.get_projects() diff --git a/pype/plugins/global/publish/extract_hierarchy_avalon.py b/pype/plugins/global/publish/extract_hierarchy_avalon.py index 1d8191f2e3..b43678ff6c 100644 --- a/pype/plugins/global/publish/extract_hierarchy_avalon.py +++ b/pype/plugins/global/publish/extract_hierarchy_avalon.py @@ -38,7 +38,7 @@ class ExtractHierarchyToAvalon(pyblish.api.ContextPlugin): data["inputs"] = entity_data.get("inputs", []) # Tasks. - tasks = entity_data.get("tasks", []) + tasks = entity_data.get("tasks", {}) if tasks is not None or len(tasks) > 0: data["tasks"] = tasks parents = [] @@ -78,11 +78,13 @@ class ExtractHierarchyToAvalon(pyblish.api.ContextPlugin): if entity: # Do not override data, only update cur_entity_data = entity.get("data") or {} - new_tasks = data.pop("tasks", []) + new_tasks = data.pop("tasks", {}) if "tasks" in cur_entity_data and new_tasks: - for task_name in new_tasks: - if task_name not in cur_entity_data["tasks"]: - cur_entity_data["tasks"].append(task_name) + for task_name in new_tasks.keys(): + if task_name \ + not in cur_entity_data["tasks"].keys(): + cur_entity_data["tasks"][task_name] = \ + new_tasks[task_name] cur_entity_data.update(data) data = cur_entity_data else: diff --git a/pype/plugins/maya/publish/collect_yeti_cache.py b/pype/plugins/maya/publish/collect_yeti_cache.py index 4af3e1ea18..e24517951b 100644 --- a/pype/plugins/maya/publish/collect_yeti_cache.py +++ b/pype/plugins/maya/publish/collect_yeti_cache.py @@ -30,7 +30,7 @@ class CollectYetiCache(pyblish.api.InstancePlugin): label = "Collect Yeti Cache" families = ["yetiRig", "yeticache"] hosts = ["maya"] - tasks = ["animation", "fx"] + tasks = {"animation": {"type": "Animation"}, "fx": {"type": "FX"}} def process(self, instance): diff --git a/pype/plugins/nukestudio/publish/collect_shots.py b/pype/plugins/nukestudio/publish/collect_shots.py index 455e25bf82..42b1ea160d 100644 --- a/pype/plugins/nukestudio/publish/collect_shots.py +++ b/pype/plugins/nukestudio/publish/collect_shots.py @@ -43,7 +43,7 @@ class CollectShots(api.InstancePlugin): "{} - {} - tasks:{} - assetbuilds:{}".format( data["asset"], data["subset"], - data["tasks"], + data["tasks"].keys(), [x["name"] for x in data.get("assetbuilds", [])] ) ) From c615e7972803300d761e90ee81bf4a5b211e0783 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 25 Sep 2020 12:46:05 +0200 Subject: [PATCH 728/813] #180 - Changed tasks to dictionaries Modifications based on change --- pype/modules/ftrack/lib/avalon_sync.py | 2 +- schema/config-1.1.json | 83 +++++++++++++++++++++++++ schema/inventory-1.1.json | 10 +++ schema/project-2.1.json | 86 ++++++++++++++++++++++++++ 4 files changed, 180 insertions(+), 1 deletion(-) create mode 100644 schema/config-1.1.json create mode 100644 schema/inventory-1.1.json create mode 100644 schema/project-2.1.json diff --git a/pype/modules/ftrack/lib/avalon_sync.py b/pype/modules/ftrack/lib/avalon_sync.py index 68b54f9456..5a5d489714 100644 --- a/pype/modules/ftrack/lib/avalon_sync.py +++ b/pype/modules/ftrack/lib/avalon_sync.py @@ -51,7 +51,7 @@ def check_regex(name, entity_type, in_schema=None, schema_patterns=None): if in_schema: schema_name = in_schema elif entity_type == "project": - schema_name = "project-2.0" + schema_name = "project-2.1" elif entity_type == "task": schema_name = "task" diff --git a/schema/config-1.1.json b/schema/config-1.1.json new file mode 100644 index 0000000000..5f4fe4b2fb --- /dev/null +++ b/schema/config-1.1.json @@ -0,0 +1,83 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + + "title": "avalon-core:config-1.1", + "description": "A project configuration.", + + "type": "object", + + "additionalProperties": false, + "required": [ + "template", + "tasks", + "apps" + ], + + "properties": { + "schema": { + "description": "Schema identifier for payload", + "type": "string" + }, + "template": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^.*$": { + "type": "string" + } + } + }, + "tasks": { + "type": "object", + "properties": { + "short_name": {"type": "string"}, + "icon": {"type": "string"}, + "group": {"type": "string"}, + "label": {"type": "string"} + }, + "required": ["short_name"] + }, + "apps": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": {"type": "string"}, + "icon": {"type": "string"}, + "group": {"type": "string"}, + "label": {"type": "string"} + }, + "required": ["name"] + } + }, + "families": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": {"type": "string"}, + "icon": {"type": "string"}, + "label": {"type": "string"}, + "hideFilter": {"type": "boolean"} + }, + "required": ["name"] + } + }, + "groups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": {"type": "string"}, + "icon": {"type": "string"}, + "color": {"type": "string"}, + "order": {"type": ["integer", "number"]} + }, + "required": ["name"] + } + }, + "copy": { + "type": "object" + } + } +} diff --git a/schema/inventory-1.1.json b/schema/inventory-1.1.json new file mode 100644 index 0000000000..f46df6973d --- /dev/null +++ b/schema/inventory-1.1.json @@ -0,0 +1,10 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + + "title": "avalon-core:config-1.1", + "description": "A project configuration.", + + "type": "object", + + "additionalProperties": true +} diff --git a/schema/project-2.1.json b/schema/project-2.1.json new file mode 100644 index 0000000000..22327b2f06 --- /dev/null +++ b/schema/project-2.1.json @@ -0,0 +1,86 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + + "title": "avalon-core:project-2.1", + "description": "A unit of data", + + "type": "object", + + "additionalProperties": true, + + "required": [ + "schema", + "type", + "name", + "data", + "config" + ], + + "properties": { + "schema": { + "description": "Schema identifier for payload", + "type": "string", + "enum": ["avalon-core:project-2.1"], + "example": "avalon-core:project-2.1" + }, + "type": { + "description": "The type of document", + "type": "string", + "enum": ["project"], + "example": "project" + }, + "parent": { + "description": "Unique identifier to parent document", + "example": "592c33475f8c1b064c4d1696" + }, + "name": { + "description": "Name of directory", + "type": "string", + "pattern": "^[a-zA-Z0-9_.]*$", + "example": "hulk" + }, + "data": { + "description": "Document metadata", + "type": "object", + "example": { + "fps": 24, + "width": 1920, + "height": 1080 + } + }, + "config": { + "type": "object", + "description": "Document metadata", + "example": { + "schema": "avalon-core:config-1.1", + "apps": [ + { + "name": "maya2016", + "label": "Autodesk Maya 2016" + }, + { + "name": "nuke10", + "label": "The Foundry Nuke 10.0" + } + ], + "tasks": { + "Model": {"short_name": "mdl"}, + "Render": {"short_name": "rnd"}, + "Animate": {"short_name": "anim"}, + "Rig": {"short_name": "rig"}, + "Lookdev": {"short_name": "look"}, + "Layout": {"short_name": "lay"} + }, + "template": { + "work": + "{root}/{project}/{silo}/{asset}/work/{task}/{app}", + "publish": + "{root}/{project}/{silo}/{asset}/publish/{subset}/v{version:0>3}/{subset}.{representation}" + } + }, + "$ref": "config-1.1.json" + } + }, + + "definitions": {} +} From 62888dc38793d74a0c3ce8a39b9b478178e07c5e Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 25 Sep 2020 12:54:57 +0200 Subject: [PATCH 729/813] Hound --- 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 5a5d489714..40b14a02a8 100644 --- a/pype/modules/ftrack/lib/avalon_sync.py +++ b/pype/modules/ftrack/lib/avalon_sync.py @@ -1141,8 +1141,8 @@ class SyncEntitiesFactory: tasks = {} for tt in task_types: tasks[tt["name"]] = { - "short_name": get_task_short_name(tt["name"]) - } + "short_name": get_task_short_name(tt["name"]) + } self.entities_dict[id]["final_entity"]["config"] = { "tasks": tasks, "apps": proj_apps From 76a241afe9001f7fc816f741d56c377abc95f8ce Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Fri, 25 Sep 2020 13:24:30 +0200 Subject: [PATCH 730/813] 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 731/813] 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 0ac12df93decc0b618f2088d9e520083bcb362d1 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 25 Sep 2020 15:46:13 +0200 Subject: [PATCH 732/813] feat(nuke): loader plugin for alembic camera --- pype/plugins/nuke/load/load_camera_abc.py | 64 +++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 pype/plugins/nuke/load/load_camera_abc.py diff --git a/pype/plugins/nuke/load/load_camera_abc.py b/pype/plugins/nuke/load/load_camera_abc.py new file mode 100644 index 0000000000..51810c45f9 --- /dev/null +++ b/pype/plugins/nuke/load/load_camera_abc.py @@ -0,0 +1,64 @@ +from avalon import api +import nuke +from pprint import pformat + +class AlembicCameraLoader(api.Loader): + """ + This will load alembic camera into script. + """ + + families = ["camera"] + representations = ["abc"] + + label = "Load Alembic Camera" + icon = "camera" + color = "orange" + + def load(self, context, name, namespace, data): + + # import dependencies + from avalon.nuke import containerise + + # get main variables + version = context['version'] + version_data = version.get("data", {}) + vname = version.get("name", None) + first = version_data.get("frameStart", None) + last = version_data.get("frameEnd", None) + fps = version_data.get("fps") or nuke.root()["fps"].getValue() + namespace = namespace or context['asset']['name'] + object_name = "{}_{}".format(name, namespace) + + # prepare data for imprinting + # add additional metadata from the version to imprint to Avalon knob + add_keys = ["frameStart", "frameEnd", "source", "author", "fps"] + + data_imprint = {"frameStart": first, + "frameEnd": last, + "version": vname, + "objectName": object_name} + + for k in add_keys: + data_imprint.update({k: version_data[k]}) + + # getting file path + file = self.fname.replace("\\", "/") + + camera_node = nuke.createNode( + "Camera2", + "file {} read_from_file True".format(file), + inpanel=False + ) + camera_node.forceValidate() + # camera_node["read_from_file"].setValue(True) + # camera_node["file"].setValue(file) + camera_node["frame_rate"].setValue(float(fps)) + camera_node["tile_color"].setValue(int("0x3469ffff", 16)) + + return containerise( + node=camera_node, + name=name, + namespace=namespace, + context=context, + loader=self.__class__.__name__, + data=data_imprint) From 64052aa44e8c159a2429d842e88d2fa3220fc124 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 25 Sep 2020 16:08:34 +0200 Subject: [PATCH 733/813] 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 734/813] 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 735/813] 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 736/813] 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 737/813] 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 738/813] 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 984f815f1d9d63f9d78ba75c257c4d430cf3d5dc Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 25 Sep 2020 16:12:46 +0200 Subject: [PATCH 739/813] feat(nuke): remove placeholder file --- pype/plugins/nuke/create/create_camera | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 pype/plugins/nuke/create/create_camera diff --git a/pype/plugins/nuke/create/create_camera b/pype/plugins/nuke/create/create_camera deleted file mode 100644 index 0d542b8ad7..0000000000 --- a/pype/plugins/nuke/create/create_camera +++ /dev/null @@ -1,3 +0,0 @@ -# create vanilla camera if no camera is selected -# if camera is selected then it will convert it into containerized object -# it is major versioned in publish From 2df5999ceb09e037e0617cd92309646b9674563c Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 25 Sep 2020 16:13:03 +0200 Subject: [PATCH 740/813] feat(nuke): create camera wip --- pype/plugins/nuke/create/create_camera.py | 52 +++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 pype/plugins/nuke/create/create_camera.py diff --git a/pype/plugins/nuke/create/create_camera.py b/pype/plugins/nuke/create/create_camera.py new file mode 100644 index 0000000000..594c736c82 --- /dev/null +++ b/pype/plugins/nuke/create/create_camera.py @@ -0,0 +1,52 @@ +import avalon.nuke +from avalon.nuke import lib as anlib +import nuke + + +class CreateCamera(avalon.nuke.Creator): + """Add Publishable Backdrop""" + + name = "camera" + label = "Create 3d Camera" + family = "camera" + icon = "camera" + defaults = ["Main"] + + def __init__(self, *args, **kwargs): + super(CreateCamera, self).__init__(*args, **kwargs) + self.nodes = nuke.selectedNodes() + self.node_color = "0xdfea5dff" + return + + def process(self): + nodes = list() + if (self.options or {}).get("useSelection"): + nodes = self.nodes + + if len(nodes) >= 1: + anlib.select_nodes(nodes) + # camera_node = autoBackdrop() + # camera_node["name"].setValue("{}_BDN".format(self.name)) + # camera_node["tile_color"].setValue(int(self.node_color, 16)) + # camera_node["note_font_size"].setValue(24) + # camera_node["label"].setValue("[{}]".format(self.name)) + # # add avalon knobs + # instance = anlib.imprint(camera_node, self.data) + # + # return instance + else: + msg = str("Please select nodes you " + "wish to add to a container") + self.log.error(msg) + nuke.message(msg) + return + else: + camera_node = autoBackdrop() + camera_node["name"].setValue("{}_BDN".format(self.name)) + camera_node["tile_color"].setValue(int(self.node_color, 16)) + camera_node["note_font_size"].setValue(24) + camera_node["label"].setValue("[{}]".format(self.name)) + # add avalon knobs + instance = anlib.imprint(camera_node, self.data) + + return instance From 81694986a272d9a91ceb1fceeaa139d0473867eb Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 25 Sep 2020 16:29:36 +0200 Subject: [PATCH 741/813] feat(nuke): 3d camera creator --- pype/plugins/nuke/create/create_camera.py | 25 ++++++++--------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/pype/plugins/nuke/create/create_camera.py b/pype/plugins/nuke/create/create_camera.py index 594c736c82..a3b78b0d0e 100644 --- a/pype/plugins/nuke/create/create_camera.py +++ b/pype/plugins/nuke/create/create_camera.py @@ -15,7 +15,7 @@ class CreateCamera(avalon.nuke.Creator): def __init__(self, *args, **kwargs): super(CreateCamera, self).__init__(*args, **kwargs) self.nodes = nuke.selectedNodes() - self.node_color = "0xdfea5dff" + self.node_color = "0xff9100ff" return def process(self): @@ -24,16 +24,13 @@ class CreateCamera(avalon.nuke.Creator): nodes = self.nodes if len(nodes) >= 1: - anlib.select_nodes(nodes) - # camera_node = autoBackdrop() - # camera_node["name"].setValue("{}_BDN".format(self.name)) - # camera_node["tile_color"].setValue(int(self.node_color, 16)) - # camera_node["note_font_size"].setValue(24) - # camera_node["label"].setValue("[{}]".format(self.name)) - # # add avalon knobs - # instance = anlib.imprint(camera_node, self.data) - # - # return instance + for n in nodes: + data = self.data.copy() + subset = self.family + n["name"].value().capitalize() + data["subset"] = subset + n["tile_color"].setValue(int(self.node_color, 16)) + # add avalon knobs + anlib.imprint(n, data) else: msg = str("Please select nodes you " "wish to add to a container") @@ -41,12 +38,8 @@ class CreateCamera(avalon.nuke.Creator): nuke.message(msg) return else: - camera_node = autoBackdrop() - camera_node["name"].setValue("{}_BDN".format(self.name)) + camera_node = nuke.createNode("Camera2") camera_node["tile_color"].setValue(int(self.node_color, 16)) - camera_node["note_font_size"].setValue(24) - camera_node["label"].setValue("[{}]".format(self.name)) # add avalon knobs instance = anlib.imprint(camera_node, self.data) - return instance From 6d3cd04e459b253732932dfd90ee4bebf066b327 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Fri, 25 Sep 2020 16:35:17 +0200 Subject: [PATCH 742/813] 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 7c71db88fc278bffe1c8da1da3788b3e4b22c000 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 25 Sep 2020 17:14:17 +0200 Subject: [PATCH 743/813] feat(nuke): create camera plugin final --- pype/plugins/nuke/create/create_camera.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/pype/plugins/nuke/create/create_camera.py b/pype/plugins/nuke/create/create_camera.py index a3b78b0d0e..4c668925ad 100644 --- a/pype/plugins/nuke/create/create_camera.py +++ b/pype/plugins/nuke/create/create_camera.py @@ -24,13 +24,20 @@ class CreateCamera(avalon.nuke.Creator): nodes = self.nodes if len(nodes) >= 1: + # loop selected nodes for n in nodes: data = self.data.copy() - subset = self.family + n["name"].value().capitalize() - data["subset"] = subset + if len(nodes) > 1: + # rename subset name only if more + # then one node are selected + subset = self.family + n["name"].value().capitalize() + data["subset"] = subset + + # change node color n["tile_color"].setValue(int(self.node_color, 16)) # add avalon knobs anlib.imprint(n, data) + return True else: msg = str("Please select nodes you " "wish to add to a container") @@ -38,6 +45,7 @@ class CreateCamera(avalon.nuke.Creator): nuke.message(msg) return else: + # if selected is off then create one node camera_node = nuke.createNode("Camera2") camera_node["tile_color"].setValue(int(self.node_color, 16)) # add avalon knobs From 89eb5e1347592a9b42e93a59d08fc7c1788cbf7d Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 29 Sep 2020 12:26:37 +0200 Subject: [PATCH 744/813] Added back print of comments Revert unwanted merge --- pype/hosts/nukestudio/tags.py | 4 ++-- pype/plugins/nukestudio/publish/collect_shots.py | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/pype/hosts/nukestudio/tags.py b/pype/hosts/nukestudio/tags.py index 36edb16da6..c8af0cabc1 100644 --- a/pype/hosts/nukestudio/tags.py +++ b/pype/hosts/nukestudio/tags.py @@ -71,8 +71,8 @@ def add_tags_from_presets(): # Get project task types. tasks = io.find_one({"type": "project"})["config"]["tasks"] nks_pres_tags["[Tasks]"] = {} - for task_name, _ in tasks.items(): - nks_pres_tags["[Tasks]"][task_name] = { + for task_type in tasks.keys(): + nks_pres_tags["[Tasks]"][task_type] = { "editable": "1", "note": "", "icon": { diff --git a/pype/plugins/nukestudio/publish/collect_shots.py b/pype/plugins/nukestudio/publish/collect_shots.py index 42b1ea160d..a33e1fad49 100644 --- a/pype/plugins/nukestudio/publish/collect_shots.py +++ b/pype/plugins/nukestudio/publish/collect_shots.py @@ -40,11 +40,12 @@ class CollectShots(api.InstancePlugin): data["name"] = data["subset"] + "_" + data["asset"] data["label"] = ( - "{} - {} - tasks:{} - assetbuilds:{}".format( + "{} - {} - tasks:{} - assetbuilds:{} - comments:{}".format( data["asset"], data["subset"], data["tasks"].keys(), - [x["name"] for x in data.get("assetbuilds", [])] + [x["name"] for x in data.get("assetbuilds", [])], + len(data["comments"]) ) ) From 62ac602985d73000e77233520f122c7ddd964f8a Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Tue, 29 Sep 2020 13:32:59 +0200 Subject: [PATCH 745/813] add look assigner to pype menu even if scriptsmenu is N/A --- pype/hosts/maya/menu.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pype/hosts/maya/menu.py b/pype/hosts/maya/menu.py index 70ad8d31ca..98406719c7 100644 --- a/pype/hosts/maya/menu.py +++ b/pype/hosts/maya/menu.py @@ -32,6 +32,15 @@ def deferred(): command=lambda *args: BuildWorkfile().process() ) + def add_look_assigner_item(): + import mayalookassigner + cmds.menuItem(divider=True, parent=pipeline._menu) + cmds.menuItem( + "Maya Look assigner", + parent=pipeline._menu, + command=lambda *args: mayalookassigner.show() + ) + log.info("Attempting to install scripts menu..") try: @@ -43,6 +52,7 @@ def deferred(): "'scriptsmenu' module seems unavailable." ) add_build_workfiles_item() + add_look_assigner_item() return # load configuration of custom menu From 9be103974ec433d2c725174225bd630f3a4270e2 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 30 Sep 2020 10:27:26 +0200 Subject: [PATCH 746/813] feat(nuke): exctractor plugin for camera abs (presets can do fbx) --- .../plugins/nuke/publish/collect_instances.py | 1 - pype/plugins/nuke/publish/extract_camera.py | 185 ++++++++++++++++++ 2 files changed, 185 insertions(+), 1 deletion(-) create mode 100644 pype/plugins/nuke/publish/extract_camera.py diff --git a/pype/plugins/nuke/publish/collect_instances.py b/pype/plugins/nuke/publish/collect_instances.py index 9085e12bd8..d2031266bd 100644 --- a/pype/plugins/nuke/publish/collect_instances.py +++ b/pype/plugins/nuke/publish/collect_instances.py @@ -60,7 +60,6 @@ class CollectNukeInstances(pyblish.api.ContextPlugin): families.append(family) - # except disabled nodes but exclude backdrops in test if ("nukenodes" not in family) and (node["disable"].value()): continue diff --git a/pype/plugins/nuke/publish/extract_camera.py b/pype/plugins/nuke/publish/extract_camera.py new file mode 100644 index 0000000000..9a1efba1df --- /dev/null +++ b/pype/plugins/nuke/publish/extract_camera.py @@ -0,0 +1,185 @@ +import nuke +import os +import math +import pyblish.api +import pype.api +from avalon.nuke import lib as anlib +from pprint import pformat + + +class ExtractCamera(pype.api.Extractor): + """ 3D camera exctractor + """ + label = 'Exctract Camera' + order = pyblish.api.ExtractorOrder + families = ["camera"] + hosts = ["nuke"] + + # presets + write_geo_knobs = [ + ("file_type", "abc"), + ("storageFormat", "Ogawa"), + ("writeGeometries", False), + ("writePointClouds", False), + ("writeAxes", False) + ] + + def process(self, instance): + handle_start = instance.context.data["handleStart"] + handle_end = instance.context.data["handleEnd"] + first_frame = int(nuke.root()["first_frame"].getValue()) + last_frame = int(nuke.root()["last_frame"].getValue()) + step = 1 + output_range = str(nuke.FrameRange(first_frame, last_frame, step)) + + self.log.info("instance.data: `{}`".format( + pformat(instance.data))) + + rm_nodes = list() + self.log.info("Crating additional nodes") + subset = instance.data["subset"] + staging_dir = self.staging_dir(instance) + + # get extension form preset + extension = next((k[1] for k in self.write_geo_knobs + if k[0] == "file_type"), None) + if not extension: + raise RuntimeError( + "Bad config for extension in presets. " + "Talk to your supervisor or pipeline admin") + + # create file name and path + filename = subset + ".{}".format(extension) + file_path = os.path.join(staging_dir, filename).replace("\\", "/") + + with anlib.maintained_selection(): + # bake camera with axeses onto word coordinate XYZ + rm_n = bakeCameraWithAxeses( + nuke.toNode(instance.data["name"]), output_range) + rm_nodes.append(rm_n) + + # create scene node + rm_n = nuke.createNode("Scene") + rm_nodes.append(rm_n) + + # create write geo node + wg_n = nuke.createNode("WriteGeo") + wg_n["file"].setValue(file_path) + # add path to write to + for k, v in self.write_geo_knobs: + wg_n[k].setValue(v) + rm_nodes.append(wg_n) + + # write out camera + nuke.execute( + wg_n, + int(first_frame), + int(last_frame) + ) + # erase additional nodes + for n in rm_nodes: + nuke.delete(n) + + self.log.info(file_path) + + # create representation data + if "representations" not in instance.data: + instance.data["representations"] = [] + + representation = { + 'name': extension, + 'ext': extension, + 'files': filename, + "stagingDir": staging_dir, + "frameStart": first_frame, + "frameEnd": last_frame + } + instance.data["representations"].append(representation) + + instance.data.update({ + "path": file_path, + "outputDir": staging_dir, + "ext": extension, + "handleStart": handle_start, + "handleEnd": handle_end, + "frameStart": first_frame + handle_start, + "frameEnd": last_frame - handle_end, + "frameStartHandle": first_frame, + "frameEndHandle": last_frame, + }) + + self.log.info("Extracted instance '{0}' to: {1}".format( + instance.name, file_path)) + + +def bakeCameraWithAxeses(camera_node, output_range): + """ Baking all perent hiearchy of axeses into camera + with transposition onto word XYZ coordinance + """ + bakeFocal = False + bakeHaperture = False + bakeVaperture = False + + camera_matrix = camera_node['world_matrix'] + + new_cam_n = nuke.createNode("Camera2") + new_cam_n.setInput(0, None) + new_cam_n['rotate'].setAnimated() + new_cam_n['translate'].setAnimated() + + old_focal = camera_node['focal'] + if old_focal.isAnimated() and not (old_focal.animation(0).constant()): + new_cam_n['focal'].setAnimated() + bakeFocal = True + else: + new_cam_n['focal'].setValue(old_focal.value()) + + old_haperture = camera_node['haperture'] + if old_haperture.isAnimated() and not ( + old_haperture.animation(0).constant()): + new_cam_n['haperture'].setAnimated() + bakeHaperture = True + else: + new_cam_n['haperture'].setValue(old_haperture.value()) + + old_vaperture = camera_node['vaperture'] + if old_vaperture.isAnimated() and not ( + old_vaperture.animation(0).constant()): + new_cam_n['vaperture'].setAnimated() + bakeVaperture = True + else: + new_cam_n['vaperture'].setValue(old_vaperture.value()) + + new_cam_n['win_translate'].setValue(camera_node['win_translate'].value()) + new_cam_n['win_scale'].setValue(camera_node['win_scale'].value()) + + for x in nuke.FrameRange(output_range): + math_matrix = nuke.math.Matrix4() + for y in range(camera_matrix.height()): + for z in range(camera_matrix.width()): + matrix_pointer = z + (y * camera_matrix.width()) + math_matrix[matrix_pointer] = camera_matrix.getValueAt( + x, (y + (z * camera_matrix.width()))) + + rot_matrix = nuke.math.Matrix4(math_matrix) + rot_matrix.rotationOnly() + rot = rot_matrix.rotationsZXY() + + new_cam_n['rotate'].setValueAt(math.degrees(rot[0]), x, 0) + new_cam_n['rotate'].setValueAt(math.degrees(rot[1]), x, 1) + new_cam_n['rotate'].setValueAt(math.degrees(rot[2]), x, 2) + new_cam_n['translate'].setValueAt( + camera_matrix.getValueAt(x, 3), x, 0) + new_cam_n['translate'].setValueAt( + camera_matrix.getValueAt(x, 7), x, 1) + new_cam_n['translate'].setValueAt( + camera_matrix.getValueAt(x, 11), x, 2) + + if bakeFocal: + new_cam_n['focal'].setValueAt(old_focal.getValueAt(x), x) + if bakeHaperture: + new_cam_n['haperture'].setValueAt(old_haperture.getValueAt(x), x) + if bakeVaperture: + new_cam_n['vaperture'].setValueAt(old_vaperture.getValueAt(x), x) + + return new_cam_n From dce6ab49422e681b1f742624785289c5bae3a0cc Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 30 Sep 2020 10:28:38 +0200 Subject: [PATCH 747/813] fix(nuke): loader was too strict for properties frame start/and maya is not adding them to version and it would be crashing --- pype/plugins/nuke/load/load_camera_abc.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pype/plugins/nuke/load/load_camera_abc.py b/pype/plugins/nuke/load/load_camera_abc.py index 51810c45f9..95ebb65005 100644 --- a/pype/plugins/nuke/load/load_camera_abc.py +++ b/pype/plugins/nuke/load/load_camera_abc.py @@ -31,7 +31,7 @@ class AlembicCameraLoader(api.Loader): # prepare data for imprinting # add additional metadata from the version to imprint to Avalon knob - add_keys = ["frameStart", "frameEnd", "source", "author", "fps"] + add_keys = ["source", "author", "fps"] data_imprint = {"frameStart": first, "frameEnd": last, @@ -55,6 +55,8 @@ class AlembicCameraLoader(api.Loader): camera_node["frame_rate"].setValue(float(fps)) camera_node["tile_color"].setValue(int("0x3469ffff", 16)) + camera_node["reload"].execute() + return containerise( node=camera_node, name=name, From bfb906f37ad1efcfc3d72768a12fb2841a9b583b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 30 Sep 2020 11:06:06 +0200 Subject: [PATCH 748/813] 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 749/813] 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 750/813] 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 751/813] 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 752/813] 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 753/813] 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 754/813] 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 755/813] 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 756/813] 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 6951e046de4a531a635a80ca8909f601cac076c9 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 30 Sep 2020 15:13:19 +0200 Subject: [PATCH 757/813] 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 758/813] 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 759/813] 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 760/813] 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 761/813] 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 762/813] 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 763/813] 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 764/813] 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 765/813] 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 766/813] 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 767/813] 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 768/813] 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 2a14a586c867ee3253ecb159e13a3be3cfe343b8 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 1 Oct 2020 17:05:34 +0200 Subject: [PATCH 769/813] 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 770/813] 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 771/813] 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 88bbe2d156f8fd542afc0e04bad554660aae57a3 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 1 Oct 2020 18:34:32 +0200 Subject: [PATCH 772/813] 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 773/813] 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 2e9be7332681097fa05998dbd6d41567c0d96d48 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 1 Oct 2020 19:20:00 +0200 Subject: [PATCH 774/813] Removed unload + reload to keep changes to untouched shaders --- pype/plugins/maya/load/load_look.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/pype/plugins/maya/load/load_look.py b/pype/plugins/maya/load/load_look.py index cb5b6fa2e8..c5b58c9bd5 100644 --- a/pype/plugins/maya/load/load_look.py +++ b/pype/plugins/maya/load/load_look.py @@ -117,10 +117,7 @@ class LookLoader(pype.hosts.maya.plugin.ReferenceLoader): # highlight failed edits to user if failed_edits: # clean references - removes failed reference edits - cmds.file(unloadReference=reference_node) cmds.file(cr=reference_node) # cleanReference - # reload reference, now it shouldn't fail - self._load_reference(file_type, node, path, reference_node) # reapply shading groups from json representation on orig nodes pype.hosts.maya.lib.apply_shaders(relationships, From d27be1914da9a52074d8fdb1348c493de6dfd501 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 2 Oct 2020 10:06:02 +0200 Subject: [PATCH 775/813] 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 a3f7fa777bbc2a86c32659836deaad8af4bbee26 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Fri, 2 Oct 2020 10:09:42 +0200 Subject: [PATCH 776/813] make asset dependencies configurable --- .../maya/publish/submit_maya_deadline.py | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/pype/plugins/maya/publish/submit_maya_deadline.py b/pype/plugins/maya/publish/submit_maya_deadline.py index e4048592a7..7509b5875a 100644 --- a/pype/plugins/maya/publish/submit_maya_deadline.py +++ b/pype/plugins/maya/publish/submit_maya_deadline.py @@ -262,6 +262,7 @@ class MayaSubmitDeadline(pyblish.api.InstancePlugin): use_published = True tile_assembler_plugin = "PypeTileAssembler" + asset_dependencies = False def process(self, instance): """Plugin entry point.""" @@ -417,9 +418,10 @@ class MayaSubmitDeadline(pyblish.api.InstancePlugin): # Adding file dependencies. dependencies = instance.context.data["fileDependencies"] dependencies.append(filepath) - for dependency in dependencies: - key = "AssetDependency" + str(dependencies.index(dependency)) - payload_skeleton["JobInfo"][key] = dependency + if self.assembly_files: + for dependency in dependencies: + key = "AssetDependency" + str(dependencies.index(dependency)) + payload_skeleton["JobInfo"][key] = dependency # Handle environments ----------------------------------------------- # We need those to pass them to pype for it to set correct context @@ -731,10 +733,14 @@ class MayaSubmitDeadline(pyblish.api.InstancePlugin): def _get_maya_payload(self, data): payload = copy.deepcopy(payload_skeleton) - job_info_ext = { - # Asset dependency to wait for at least the scene file to sync. - "AssetDependency0": data["filepath"], - } + if not self.asset_dependencies: + job_info_ext = {} + + else: + job_info_ext = { + # Asset dependency to wait for at least the scene file to sync. + "AssetDependency0": data["filepath"], + } plugin_info = { "SceneFile": data["filepath"], From ef240a387944c72c8ccad0890e9e0eb3c2e9ba3a Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 2 Oct 2020 11:04:29 +0200 Subject: [PATCH 777/813] Fix schemas for correct creation from empty DB --- schema/config-1.1.json | 22 +++++++++++++--------- schema/inventory-1.1.json | 2 +- schema/project-2.1.json | 6 +++--- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/schema/config-1.1.json b/schema/config-1.1.json index 5f4fe4b2fb..ea5ab0ff27 100644 --- a/schema/config-1.1.json +++ b/schema/config-1.1.json @@ -1,14 +1,13 @@ { "$schema": "http://json-schema.org/draft-04/schema#", - "title": "avalon-core:config-1.1", + "title": "pype:config-1.1", "description": "A project configuration.", "type": "object", "additionalProperties": false, "required": [ - "template", "tasks", "apps" ], @@ -29,13 +28,18 @@ }, "tasks": { "type": "object", - "properties": { - "short_name": {"type": "string"}, - "icon": {"type": "string"}, - "group": {"type": "string"}, - "label": {"type": "string"} - }, - "required": ["short_name"] + "items": { + "type": "object", + "properties": { + "name": {"type": "string"}, + "icon": {"type": "string"}, + "group": {"type": "string"}, + "label": {"type": "string"} + }, + "required": [ + "short_name" + ] + } }, "apps": { "type": "array", diff --git a/schema/inventory-1.1.json b/schema/inventory-1.1.json index f46df6973d..1b572b7d23 100644 --- a/schema/inventory-1.1.json +++ b/schema/inventory-1.1.json @@ -1,7 +1,7 @@ { "$schema": "http://json-schema.org/draft-04/schema#", - "title": "avalon-core:config-1.1", + "title": "pype:config-1.1", "description": "A project configuration.", "type": "object", diff --git a/schema/project-2.1.json b/schema/project-2.1.json index 22327b2f06..40e3bdb638 100644 --- a/schema/project-2.1.json +++ b/schema/project-2.1.json @@ -1,7 +1,7 @@ { "$schema": "http://json-schema.org/draft-04/schema#", - "title": "avalon-core:project-2.1", + "title": "pype:project-2.1", "description": "A unit of data", "type": "object", @@ -20,7 +20,7 @@ "schema": { "description": "Schema identifier for payload", "type": "string", - "enum": ["avalon-core:project-2.1"], + "enum": ["avalon-core:project-2.1", "pype:project-2.1"], "example": "avalon-core:project-2.1" }, "type": { @@ -52,7 +52,7 @@ "type": "object", "description": "Document metadata", "example": { - "schema": "avalon-core:config-1.1", + "schema": "pype:config-1.1", "apps": [ { "name": "maya2016", From 48cc3cab3d17a06272fe9073258ae322cfc32c1c Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Fri, 2 Oct 2020 14:14:45 +0200 Subject: [PATCH 778/813] remove obsolete task variable --- pype/plugins/maya/publish/collect_yeti_cache.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pype/plugins/maya/publish/collect_yeti_cache.py b/pype/plugins/maya/publish/collect_yeti_cache.py index e24517951b..26c3f601f6 100644 --- a/pype/plugins/maya/publish/collect_yeti_cache.py +++ b/pype/plugins/maya/publish/collect_yeti_cache.py @@ -30,7 +30,6 @@ class CollectYetiCache(pyblish.api.InstancePlugin): label = "Collect Yeti Cache" families = ["yetiRig", "yeticache"] hosts = ["maya"] - tasks = {"animation": {"type": "Animation"}, "fx": {"type": "FX"}} def process(self, instance): From 48b6278fa8ff85702a3157a069325ebd58e4cee0 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Fri, 2 Oct 2020 14:20:02 +0200 Subject: [PATCH 779/813] return lost changes ;) --- pype/plugins/nukestudio/publish/collect_shots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/plugins/nukestudio/publish/collect_shots.py b/pype/plugins/nukestudio/publish/collect_shots.py index a33e1fad49..03fc7ab282 100644 --- a/pype/plugins/nukestudio/publish/collect_shots.py +++ b/pype/plugins/nukestudio/publish/collect_shots.py @@ -45,7 +45,7 @@ class CollectShots(api.InstancePlugin): data["subset"], data["tasks"].keys(), [x["name"] for x in data.get("assetbuilds", [])], - len(data["comments"]) + len(data.get("comments", [])) ) ) From d965a2a2a21c50303bb9f794572bc9d3ee393c5f Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Fri, 2 Oct 2020 14:34:09 +0200 Subject: [PATCH 780/813] rename look assigner --- pype/hosts/maya/menu.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/hosts/maya/menu.py b/pype/hosts/maya/menu.py index 98406719c7..6d610b2645 100644 --- a/pype/hosts/maya/menu.py +++ b/pype/hosts/maya/menu.py @@ -36,7 +36,7 @@ def deferred(): import mayalookassigner cmds.menuItem(divider=True, parent=pipeline._menu) cmds.menuItem( - "Maya Look assigner", + "Look assigner", parent=pipeline._menu, command=lambda *args: mayalookassigner.show() ) From 826f437dbac71716293f400843e18e4f89d4b997 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Fri, 2 Oct 2020 14:42:31 +0200 Subject: [PATCH 781/813] add custom menu at all times --- pype/hosts/maya/menu.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pype/hosts/maya/menu.py b/pype/hosts/maya/menu.py index 6d610b2645..9dadd8d1f5 100644 --- a/pype/hosts/maya/menu.py +++ b/pype/hosts/maya/menu.py @@ -34,7 +34,6 @@ def deferred(): def add_look_assigner_item(): import mayalookassigner - cmds.menuItem(divider=True, parent=pipeline._menu) cmds.menuItem( "Look assigner", parent=pipeline._menu, @@ -43,6 +42,9 @@ def deferred(): log.info("Attempting to install scripts menu..") + add_build_workfiles_item() + add_look_assigner_item() + try: import scriptsmenu.launchformaya as launchformaya import scriptsmenu.scriptsmenu as scriptsmenu @@ -51,8 +53,6 @@ def deferred(): "Skipping studio.menu install, because " "'scriptsmenu' module seems unavailable." ) - add_build_workfiles_item() - add_look_assigner_item() return # load configuration of custom menu From 066c38b4becf9351c597c4573b7d245a04c520a0 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 2 Oct 2020 15:51:35 +0200 Subject: [PATCH 782/813] fix(nuke): broken gizmo workflow --- pype/plugins/nuke/load/load_gizmo.py | 186 ++++++++++++++++++ pype/plugins/nuke/load/load_gizmo_ip.py | 1 - pype/plugins/nuke/publish/collect_gizmo.py | 2 +- .../plugins/nuke/publish/collect_instances.py | 6 +- 4 files changed, 188 insertions(+), 7 deletions(-) create mode 100644 pype/plugins/nuke/load/load_gizmo.py diff --git a/pype/plugins/nuke/load/load_gizmo.py b/pype/plugins/nuke/load/load_gizmo.py new file mode 100644 index 0000000000..58b7db667e --- /dev/null +++ b/pype/plugins/nuke/load/load_gizmo.py @@ -0,0 +1,186 @@ +from avalon import api, style, io +import nuke +from avalon.nuke import lib as anlib +from avalon.nuke import containerise, update_container + + +class LoadGizmo(api.Loader): + """Loading nuke Gizmo""" + + representations = ["gizmo"] + families = ["gizmo"] + + label = "Load Gizmo" + order = 0 + icon = "dropbox" + color = style.colors.light + node_color = "0x75338eff" + + def load(self, context, name, namespace, data): + """ + Loading function to get Gizmo into node graph + + Arguments: + context (dict): context of version + name (str): name of the version + namespace (str): asset name + data (dict): compulsory attribute > not used + + Returns: + nuke node: containerised nuke node object + """ + + # get main variables + version = context['version'] + version_data = version.get("data", {}) + vname = version.get("name", None) + first = version_data.get("frameStart", None) + last = version_data.get("frameEnd", None) + namespace = namespace or context['asset']['name'] + colorspace = version_data.get("colorspace", None) + object_name = "{}_{}".format(name, namespace) + + # prepare data for imprinting + # add additional metadata from the version to imprint to Avalon knob + add_keys = ["frameStart", "frameEnd", "handleStart", "handleEnd", + "source", "author", "fps"] + + data_imprint = {"frameStart": first, + "frameEnd": last, + "version": vname, + "colorspaceInput": colorspace, + "objectName": object_name} + + for k in add_keys: + data_imprint.update({k: version_data[k]}) + + # getting file path + file = self.fname.replace("\\", "/") + + # adding nodes to node graph + # just in case we are in group lets jump out of it + nuke.endGroup() + + with anlib.maintained_selection(): + # add group from nk + nuke.nodePaste(file) + + GN = nuke.selectedNode() + + GN["name"].setValue(object_name) + + return containerise( + node=GN, + name=name, + namespace=namespace, + context=context, + loader=self.__class__.__name__, + data=data_imprint) + + def update(self, container, representation): + """Update the Loader's path + + Nuke automatically tries to reset some variables when changing + the loader's path to a new file. These automatic changes are to its + inputs: + + """ + + # get main variables + # Get version from io + version = io.find_one({ + "type": "version", + "_id": representation["parent"] + }) + # get corresponding node + GN = nuke.toNode(container['objectName']) + + file = api.get_representation_path(representation).replace("\\", "/") + name = container['name'] + version_data = version.get("data", {}) + vname = version.get("name", None) + first = version_data.get("frameStart", None) + last = version_data.get("frameEnd", None) + namespace = container['namespace'] + colorspace = version_data.get("colorspace", None) + object_name = "{}_{}".format(name, namespace) + + add_keys = ["frameStart", "frameEnd", "handleStart", "handleEnd", + "source", "author", "fps"] + + data_imprint = {"representation": str(representation["_id"]), + "frameStart": first, + "frameEnd": last, + "version": vname, + "colorspaceInput": colorspace, + "objectName": object_name} + + for k in add_keys: + data_imprint.update({k: version_data[k]}) + + # adding nodes to node graph + # just in case we are in group lets jump out of it + nuke.endGroup() + + with anlib.maintained_selection(): + xpos = GN.xpos() + ypos = GN.ypos() + avalon_data = anlib.get_avalon_knob_data(GN) + nuke.delete(GN) + # add group from nk + nuke.nodePaste(file) + + GN = nuke.selectedNode() + anlib.set_avalon_knob_data(GN, avalon_data) + GN.setXYpos(xpos, ypos) + GN["name"].setValue(object_name) + + # get all versions in list + versions = io.find({ + "type": "version", + "parent": version["parent"] + }).distinct('name') + + max_version = max(versions) + + # change color of node + if version.get("name") not in [max_version]: + GN["tile_color"].setValue(int("0xd88467ff", 16)) + else: + GN["tile_color"].setValue(int(self.node_color, 16)) + + self.log.info("udated to version: {}".format(version.get("name"))) + + return update_container(GN, data_imprint) + + def byteify(self, input): + """ + Converts unicode strings to strings + It goes trought all dictionary + + Arguments: + input (dict/str): input + + Returns: + dict: with fixed values and keys + + """ + + if isinstance(input, dict): + return {self.byteify(key): self.byteify(value) + for key, value in input.iteritems()} + elif isinstance(input, list): + return [self.byteify(element) for element in input] + elif isinstance(input, unicode): + return input.encode('utf-8') + else: + return input + + def switch(self, container, representation): + self.update(container, representation) + + def remove(self, container): + from avalon.nuke import viewer_update_and_undo_stop + node = nuke.toNode(container['objectName']) + with viewer_update_and_undo_stop(): + nuke.delete(node) diff --git a/pype/plugins/nuke/load/load_gizmo_ip.py b/pype/plugins/nuke/load/load_gizmo_ip.py index e735e27bbf..a2e8a6abb8 100644 --- a/pype/plugins/nuke/load/load_gizmo_ip.py +++ b/pype/plugins/nuke/load/load_gizmo_ip.py @@ -102,7 +102,6 @@ class LoadGizmoInputProcess(api.Loader): GN = nuke.toNode(container['objectName']) file = api.get_representation_path(representation).replace("\\", "/") - context = representation["context"] name = container['name'] version_data = version.get("data", {}) vname = version.get("name", None) diff --git a/pype/plugins/nuke/publish/collect_gizmo.py b/pype/plugins/nuke/publish/collect_gizmo.py index 11e8c17a3f..6436bdc562 100644 --- a/pype/plugins/nuke/publish/collect_gizmo.py +++ b/pype/plugins/nuke/publish/collect_gizmo.py @@ -31,7 +31,7 @@ class CollectGizmo(pyblish.api.InstancePlugin): # get version version = pype.get_version_from_path(nuke.root().name()) - instance.data['version'] = version + instance.data['version'] = int(version) # Add version data to instance version_data = { diff --git a/pype/plugins/nuke/publish/collect_instances.py b/pype/plugins/nuke/publish/collect_instances.py index 9085e12bd8..26a5bf3a2a 100644 --- a/pype/plugins/nuke/publish/collect_instances.py +++ b/pype/plugins/nuke/publish/collect_instances.py @@ -37,7 +37,6 @@ class CollectNukeInstances(pyblish.api.ContextPlugin): except Exception as E: self.log.warning(E) - # get data from avalon knob self.log.debug("node[name]: {}".format(node['name'].value())) avalon_knob_data = get_avalon_knob_data(node, ["avalon:", "ak:"]) @@ -60,7 +59,6 @@ class CollectNukeInstances(pyblish.api.ContextPlugin): families.append(family) - # except disabled nodes but exclude backdrops in test if ("nukenodes" not in family) and (node["disable"].value()): continue @@ -75,8 +73,7 @@ class CollectNukeInstances(pyblish.api.ContextPlugin): # Add all nodes in group instances. if node.Class() == "Group": # only alter families for render family - if "write" in families_ak: - + if "write" == families_ak: if node["render"].value(): self.log.info("flagged for render") add_family = "{}.local".format("render") @@ -97,7 +94,6 @@ class CollectNukeInstances(pyblish.api.ContextPlugin): self.log.debug("__ families: `{}`".format(families)) - # Get format format = root['format'].value() resolution_width = format.width() From add97d828c252860d17e6fa542a652abba2ace1b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 2 Oct 2020 15:53:09 +0200 Subject: [PATCH 783/813] fixed bad intent values in burnins --- pype/plugins/global/publish/extract_burnin.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pype/plugins/global/publish/extract_burnin.py b/pype/plugins/global/publish/extract_burnin.py index b81cfbc050..5649c9aef2 100644 --- a/pype/plugins/global/publish/extract_burnin.py +++ b/pype/plugins/global/publish/extract_burnin.py @@ -314,14 +314,15 @@ class ExtractBurnin(pype.api.Extractor): "comment": context.data.get("comment") or "" }) - intent_label = context.data.get("intent") + intent_label = context.data.get("intent") or "" if intent_label and isinstance(intent_label, dict): value = intent_label.get("value") if value: - intent_label = intent_label.get("label") + intent_label = intent_label["label"] + else: + intent_label = "" - if intent_label: - burnin_data["intent"] = intent_label + burnin_data["intent"] = intent_label temp_data = { "frame_start": frame_start, From 8fff3f2fbacba1fcc1b44cfdc211ff326ef2fcf0 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 2 Oct 2020 15:55:05 +0200 Subject: [PATCH 784/813] clean(nuke): hound fixes --- pype/plugins/nuke/load/load_gizmo.py | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/pype/plugins/nuke/load/load_gizmo.py b/pype/plugins/nuke/load/load_gizmo.py index 58b7db667e..c6228b95f6 100644 --- a/pype/plugins/nuke/load/load_gizmo.py +++ b/pype/plugins/nuke/load/load_gizmo.py @@ -153,29 +153,6 @@ class LoadGizmo(api.Loader): return update_container(GN, data_imprint) - def byteify(self, input): - """ - Converts unicode strings to strings - It goes trought all dictionary - - Arguments: - input (dict/str): input - - Returns: - dict: with fixed values and keys - - """ - - if isinstance(input, dict): - return {self.byteify(key): self.byteify(value) - for key, value in input.iteritems()} - elif isinstance(input, list): - return [self.byteify(element) for element in input] - elif isinstance(input, unicode): - return input.encode('utf-8') - else: - return input - def switch(self, container, representation): self.update(container, representation) From f5a911aed932534fa2b3e231f080075377a47630 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Sun, 4 Oct 2020 09:46:52 +0200 Subject: [PATCH 785/813] fixed super calls --- pype/tools/pyblish_pype/widgets.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pype/tools/pyblish_pype/widgets.py b/pype/tools/pyblish_pype/widgets.py index 880d4755ad..54198a8cfe 100644 --- a/pype/tools/pyblish_pype/widgets.py +++ b/pype/tools/pyblish_pype/widgets.py @@ -6,7 +6,7 @@ from .constants import PluginStates, InstanceStates, Roles class EllidableLabel(QtWidgets.QLabel): def __init__(self, *args, **kwargs): - super(self.__class__, self).__init__(*args, **kwargs) + super(EllidableLabel, self).__init__(*args, **kwargs) self.setObjectName("EllidableLabel") def paintEvent(self, event): @@ -21,7 +21,7 @@ class EllidableLabel(QtWidgets.QLabel): class PerspectiveLabel(QtWidgets.QTextEdit): def __init__(self, parent=None): - super(self.__class__, self).__init__(parent) + super(PerspectiveLabel, self).__init__(parent) self.setObjectName("PerspectiveLabel") size_policy = self.sizePolicy() @@ -50,7 +50,7 @@ class PerspectiveLabel(QtWidgets.QTextEdit): return margins.top() + document.size().height() + margins.bottom() def sizeHint(self): - width = super(self.__class__, self).sizeHint().width() + width = super(PerspectiveLabel, self).sizeHint().width() return QtCore.QSize(width, self.heightForWidth(width)) @@ -407,7 +407,7 @@ class ExpandableWidget(QtWidgets.QWidget): self.content_widget.setVisible(checked) def resizeEvent(self, event): - super(self.__class__, self).resizeEvent(event) + super(ExpandableWidget, self).resizeEvent(event) self.content.updateGeometry() def set_content(self, in_widget): @@ -481,7 +481,7 @@ class CommentBox(QtWidgets.QLineEdit): class TerminalDetail(QtWidgets.QTextEdit): def __init__(self, text, *args, **kwargs): - super(self.__class__, self).__init__(*args, **kwargs) + super(TerminalDetail, self).__init__(*args, **kwargs) self.setReadOnly(True) self.setHtml(text) @@ -504,7 +504,7 @@ class FilterButton(QtWidgets.QPushButton): def __init__(self, name, *args, **kwargs): self.filter_name = name - super(self.__class__, self).__init__(*args, **kwargs) + super(FilterButton, self).__init__(*args, **kwargs) self.toggled.connect(self.on_toggle) @@ -522,8 +522,8 @@ class FilterButton(QtWidgets.QPushButton): class TerminalFilterWidget(QtWidgets.QWidget): # timer.timeout.connect(lambda: self._update(self.parent_widget)) def __init__(self, *args, **kwargs): - super(self.__class__, self).__init__(*args, **kwargs) + super(TerminalFilterWidget, self).__init__(*args, **kwargs) self.filter_changed = QtCore.Signal() info_icon = awesome.tags["info"] From 83e356102ccb24a45020150d4ee139cc51d01d20 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Sun, 4 Oct 2020 09:47:11 +0200 Subject: [PATCH 786/813] added parenting for terminal fiter widget --- pype/tools/pyblish_pype/widgets.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pype/tools/pyblish_pype/widgets.py b/pype/tools/pyblish_pype/widgets.py index 54198a8cfe..ebdb6d6dce 100644 --- a/pype/tools/pyblish_pype/widgets.py +++ b/pype/tools/pyblish_pype/widgets.py @@ -531,16 +531,16 @@ class TerminalFilterWidget(QtWidgets.QWidget): error_icon = awesome.tags["exclamation-triangle"] filter_buttons = ( - FilterButton("info", info_icon), - FilterButton("log_debug", log_icon), - FilterButton("log_info", log_icon), - FilterButton("log_warning", log_icon), - FilterButton("log_error", log_icon), - FilterButton("log_critical", log_icon), - FilterButton("error", error_icon) + FilterButton("info", info_icon, self), + FilterButton("log_debug", log_icon, self), + FilterButton("log_info", log_icon, self), + FilterButton("log_warning", log_icon, self), + FilterButton("log_error", log_icon, self), + FilterButton("log_critical", log_icon, self), + FilterButton("error", error_icon, self) ) - layout = QtWidgets.QHBoxLayout() + layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) # Add spacers layout.addWidget(QtWidgets.QWidget(), 1) From 7fbeef6c8eb1dd6de3dc4eceb27064dbde301714 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Sun, 4 Oct 2020 09:47:36 +0200 Subject: [PATCH 787/813] TerminalFilterWidget has object name and stylesheets for that --- pype/tools/pyblish_pype/app.css | 33 ++++++++++++++++++++++++++++-- pype/tools/pyblish_pype/widgets.py | 2 +- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/pype/tools/pyblish_pype/app.css b/pype/tools/pyblish_pype/app.css index 3a2c05c1f3..c51126e89f 100644 --- a/pype/tools/pyblish_pype/app.css +++ b/pype/tools/pyblish_pype/app.css @@ -459,7 +459,7 @@ QToolButton { color: #fff; } -#TerminalFilerBtn { +#TerminalFilterWidget QPushButton { /* font: %(font_size_pt)spt; */ font-family: "FontAwesome"; text-align: center; @@ -468,29 +468,58 @@ QToolButton { border-color: #777777; border-style: none; padding: 0px; - border-radius: 3px; + border-radius: 8px; +} +#TerminalFilterWidget QPushButton:hover { + background: #5f5f5f; + border-style: none; +} +#TerminalFilterWidget QPushButton:pressed { + background: #606060; + border-style: none; +} +#TerminalFilterWidget QPushButton:pressed:hover { + background: #626262; + border-style: none; } #TerminalFilerBtn[type="info"]:checked {color: rgb(255, 255, 255);} +#TerminalFilerBtn[type="info"]:hover:pressed {color: rgba(255, 255, 255, 163);} #TerminalFilerBtn[type="info"] {color: rgba(255, 255, 255, 63);} #TerminalFilerBtn[type="error"]:checked {color: rgb(255, 74, 74);} +#TerminalFilerBtn[type="error"]:hover:pressed {color: rgba(255, 74, 74, 163);} #TerminalFilerBtn[type="error"] {color: rgba(255, 74, 74, 63);} #TerminalFilerBtn[type="log_debug"]:checked {color: rgb(255, 102, 232);} #TerminalFilerBtn[type="log_debug"] {color: rgba(255, 102, 232, 63);} +#TerminalFilerBtn[type="log_debug"]:hover:pressed { + color: rgba(255, 102, 232, 163); +} #TerminalFilerBtn[type="log_info"]:checked {color: rgb(102, 171, 255);} #TerminalFilerBtn[type="log_info"] {color: rgba(102, 171, 255, 63);} +#TerminalFilerBtn[type="log_info"]:hover:pressed { + color: rgba(102, 171, 255, 163); +} #TerminalFilerBtn[type="log_warning"]:checked {color: rgb(255, 186, 102);} #TerminalFilerBtn[type="log_warning"] {color: rgba(255, 186, 102, 63);} +#TerminalFilerBtn[type="log_warning"]:hover:pressed { + color: rgba(255, 186, 102, 163); +} #TerminalFilerBtn[type="log_error"]:checked {color: rgb(255, 77, 88);} #TerminalFilerBtn[type="log_error"] {color: rgba(255, 77, 88, 63);} +#TerminalFilerBtn[type="log_error"]:hover:pressed { + color: rgba(255, 77, 88, 163); +} #TerminalFilerBtn[type="log_critical"]:checked {color: rgb(255, 79, 117);} #TerminalFilerBtn[type="log_critical"] {color: rgba(255, 79, 117, 63);} +#TerminalFilerBtn[type="log_critical"]:hover:pressed { + color: rgba(255, 79, 117, 163); +} #SuspendLogsBtn { background: #444; diff --git a/pype/tools/pyblish_pype/widgets.py b/pype/tools/pyblish_pype/widgets.py index ebdb6d6dce..2a9b66a5a2 100644 --- a/pype/tools/pyblish_pype/widgets.py +++ b/pype/tools/pyblish_pype/widgets.py @@ -522,8 +522,8 @@ class FilterButton(QtWidgets.QPushButton): class TerminalFilterWidget(QtWidgets.QWidget): # timer.timeout.connect(lambda: self._update(self.parent_widget)) def __init__(self, *args, **kwargs): - super(TerminalFilterWidget, self).__init__(*args, **kwargs) + self.setObjectName("TerminalFilterWidget") self.filter_changed = QtCore.Signal() info_icon = awesome.tags["info"] From 76a7437fd04153a008e81b2e3986e49c951f95e4 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Sun, 4 Oct 2020 09:47:50 +0200 Subject: [PATCH 788/813] mover resize of window --- pype/tools/pyblish_pype/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/tools/pyblish_pype/app.py b/pype/tools/pyblish_pype/app.py index 0f662d5b3e..9879c63030 100644 --- a/pype/tools/pyblish_pype/app.py +++ b/pype/tools/pyblish_pype/app.py @@ -92,7 +92,6 @@ def show(parent=None): self._window.show() self._window.activateWindow() - self._window.resize(*settings.WindowSize) self._window.setWindowTitle(settings.WindowTitle) font = QtGui.QFont("Open Sans", 8, QtGui.QFont.Normal) @@ -100,5 +99,6 @@ def show(parent=None): self._window.setStyleSheet(css) self._window.reset() + self._window.resize(*settings.WindowSize) return self._window From 79aa29cb7a577e4452fe3436b9dd767f3873b0b0 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Sun, 4 Oct 2020 09:47:59 +0200 Subject: [PATCH 789/813] fixed toggle --- pype/tools/pyblish_pype/widgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/tools/pyblish_pype/widgets.py b/pype/tools/pyblish_pype/widgets.py index 2a9b66a5a2..4da759899e 100644 --- a/pype/tools/pyblish_pype/widgets.py +++ b/pype/tools/pyblish_pype/widgets.py @@ -328,7 +328,7 @@ class PerspectiveWidget(QtWidgets.QWidget): self.records.toggle_content(len_records > 0) def toggle_me(self): - self.parent_widget.toggle_perspective_widget() + self.parent_widget.parent().toggle_perspective_widget() class ClickableWidget(QtWidgets.QLabel): From b547dffa41bce29ffad251ece873a54418302e40 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Sun, 4 Oct 2020 09:48:20 +0200 Subject: [PATCH 790/813] views can respond to animated mouse clicks --- pype/tools/pyblish_pype/view.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/pype/tools/pyblish_pype/view.py b/pype/tools/pyblish_pype/view.py index 477303eae8..542d95bf05 100644 --- a/pype/tools/pyblish_pype/view.py +++ b/pype/tools/pyblish_pype/view.py @@ -83,6 +83,7 @@ class OverviewView(QtWidgets.QTreeView): self.setHeaderHidden(True) self.setRootIsDecorated(False) self.setIndentation(0) + self.setAnimated(True) def event(self, event): if not event.type() == QtCore.QEvent.KeyPress: @@ -201,6 +202,34 @@ class InstanceView(OverviewView): model.setData(index, new_state, QtCore.Qt.CheckStateRole) self.toggled.emit(index, new_state) + def mousePressEvent(self, event): + if event.button() == QtCore.Qt.LeftButton: + pos_index = self.indexAt(event.pos()) + if ( + pos_index.isValid() + and pos_index.data(Roles.TypeRole) == model.InstanceType + ): + if event.pos().x() < 20: + indexes = self.selectionModel().selectedIndexes() + if pos_index in indexes: + any_checked = False + for index in indexes: + if index.data(QtCore.Qt.CheckStateRole): + any_checked = True + break + + for index in indexes: + self.toggled.emit(index, not any_checked) + return + + else: + self.toggled.emit(pos_index, not any_checked) + + elif event.pos().x() > self.width() - 20: + self.show_perspective.emit(pos_index) + + return super(InstanceView, self).mousePressEvent(event) + def mouseReleaseEvent(self, event): if event.button() == QtCore.Qt.LeftButton: indexes = self.selectionModel().selectedIndexes() From 424ca4181dacc345f328b8faaed98b8f642fba4c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Sun, 4 Oct 2020 09:48:57 +0200 Subject: [PATCH 791/813] Implemented animated pages --- pype/tools/pyblish_pype/window.py | 273 +++++++++++++++++++++++++++--- 1 file changed, 251 insertions(+), 22 deletions(-) diff --git a/pype/tools/pyblish_pype/window.py b/pype/tools/pyblish_pype/window.py index 2a037ba4bc..0365962dc2 100644 --- a/pype/tools/pyblish_pype/window.py +++ b/pype/tools/pyblish_pype/window.py @@ -39,6 +39,7 @@ Todo: the first time to understand how to actually to it! """ +import sys from functools import partial from . import delegate, model, settings, util, view, widgets @@ -48,6 +49,10 @@ from Qt import QtCore, QtGui, QtWidgets from .constants import ( PluginStates, PluginActionStates, InstanceStates, GroupStates, Roles ) +if sys.version_info[0] == 3: + from queue import Queue +else: + from Queue import Queue class Window(QtWidgets.QDialog): @@ -267,6 +272,7 @@ class Window(QtWidgets.QDialog): layout.addWidget(footer_button_play, 0) footer_layout = QtWidgets.QVBoxLayout(footer_widget) + footer_layout.addWidget(terminal_filters_widget) footer_layout.addWidget(comment_intent_widget) footer_layout.addLayout(layout) @@ -281,16 +287,21 @@ class Window(QtWidgets.QDialog): ) closing_placeholder.hide() - perspective_widget = widgets.PerspectiveWidget(self) + perspective_widget = widgets.PerspectiveWidget(main_widget) perspective_widget.hide() + pages_widget = QtWidgets.QWidget(main_widget) + layout = QtWidgets.QVBoxLayout(pages_widget) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(0) + layout.addWidget(header_widget, 0) + layout.addWidget(body_widget, 1) + # Main layout layout = QtWidgets.QVBoxLayout(main_widget) - layout.addWidget(header_widget, 0) - layout.addWidget(body_widget, 3) + layout.addWidget(pages_widget, 3) layout.addWidget(perspective_widget, 3) layout.addWidget(closing_placeholder, 1) - layout.addWidget(terminal_filters_widget, 0) layout.addWidget(footer_widget, 0) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) @@ -382,6 +393,7 @@ class Window(QtWidgets.QDialog): # Enable CSS on plain QWidget objects for _widget in ( + pages_widget, header_widget, body_widget, artist_page, @@ -457,6 +469,7 @@ class Window(QtWidgets.QDialog): self.main_widget = main_widget + self.pages_widget = pages_widget self.header_widget = header_widget self.body_widget = body_widget @@ -498,13 +511,20 @@ class Window(QtWidgets.QDialog): "overview": header_tab_overview, "terminal": header_tab_terminal } - self.pages = { - "artist": artist_page, - "overview": overview_page, - "terminal": terminal_page - } + self.pages = ( + ("artist", artist_page), + ("overview", overview_page), + ("terminal", terminal_page) + ) current_page = settings.InitialTab or "artist" + self.comment_main_widget.setVisible( + not current_page == "terminal" + ) + self.terminal_filters_widget.setVisible( + current_page == "terminal" + ) + self.state = { "is_closing": False, "current_page": current_page @@ -548,11 +568,9 @@ class Window(QtWidgets.QDialog): show = True self.perspective_widget.set_context(index) - self.body_widget.setVisible(not show) - self.header_widget.setVisible(not show) - + self.pages_widget.setVisible(not show) self.perspective_widget.setVisible(show) - self.terminal_filters_widget.setVisible(show) + self.footer_items_visibility() def change_toggleability(self, enable_value): for plugin_item in self.plugin_model.plugin_items.values(): @@ -603,25 +621,235 @@ class Window(QtWidgets.QDialog): self.update_compatibility() def on_tab_changed(self, target): - self.comment_main_widget.setVisible(not target == "terminal") - self.terminal_filters_widget.setVisible(target == "terminal") - - for name, page in self.pages.items(): - if name != target: - page.hide() - - self.pages[target].show() + previous_page = None + target_page = None + direction = None + for name, page in self.pages: + if name == target: + target_page = page + if direction is None: + direction = -1 + elif name == self.state["current_page"]: + previous_page = page + if direction is None: + direction = 1 + else: + page.setVisible(False) self.state["current_page"] = target + self.slide_page(previous_page, target_page, direction) + + def slide_page(self, previous_page, target_page, direction): + if previous_page is None: + for name, page in self.pages: + for _name, _page in self.pages: + if name != _name: + _page.hide() + page.show() + page.hide() + + if ( + previous_page == target_page + or previous_page is None + ): + if not target_page.isVisible(): + target_page.show() + return + + width = previous_page.frameGeometry().width() + offset = QtCore.QPoint(direction * width, 0) + + previous_rect = ( + previous_page.frameGeometry().x(), + previous_page.frameGeometry().y(), + width, + previous_page.frameGeometry().height() + ) + curr_pos = previous_page.pos() + + previous_page.hide() + target_page.show() + target_page.update() + target_rect = ( + target_page.frameGeometry().x(), + target_page.frameGeometry().y(), + target_page.frameGeometry().width(), + target_page.frameGeometry().height() + ) + previous_page.show() + + target_page.raise_() + previous_page.setGeometry(*previous_rect) + target_page.setGeometry(*target_rect) + + target_page.move(curr_pos + offset) + + duration = 450 + + anim_old = QtCore.QPropertyAnimation( + previous_page, b"pos", self + ) + anim_old.setDuration(duration) + anim_old.setStartValue(curr_pos) + anim_old.setEndValue(curr_pos - offset) + anim_old.setEasingCurve(QtCore.QEasingCurve.OutQuad) + + anim_new = QtCore.QPropertyAnimation( + target_page, b"pos", self + ) + anim_new.setDuration(duration) + anim_new.setStartValue(curr_pos + offset) + anim_new.setEndValue(curr_pos) + anim_new.setEasingCurve(QtCore.QEasingCurve.OutQuad) + + anim_group = QtCore.QParallelAnimationGroup(self) + anim_group.addAnimation(anim_old) + anim_group.addAnimation(anim_new) + + def slide_finished(): + previous_page.hide() + self.footer_items_visibility() + + anim_group.finished.connect(slide_finished) + anim_group.start() + + def footer_items_visibility( + self, + comment_visible=None, + terminal_filters_visibile=None + ): + target = self.state["current_page"] + comment_visibility = ( + not target == "terminal" + and self.comment_box.isEnabled() + ) + terminal_filters_visibility = target == "terminal" + + if comment_visible is not None and comment_visibility: + comment_visibility = comment_visible + + if ( + terminal_filters_visibile is not None + and terminal_filters_visibility + ): + terminal_filters_visibility = terminal_filters_visibile + + duration = 150 + + hiding_widgets = [] + showing_widgets = [] + if (comment_visibility != ( + self.comment_main_widget.isVisible() + )): + if self.comment_main_widget.isVisible(): + hiding_widgets.append(self.comment_main_widget) + else: + showing_widgets.append(self.comment_main_widget) + + if (terminal_filters_visibility != ( + self.terminal_filters_widget.isVisible() + )): + if self.terminal_filters_widget.isVisible(): + hiding_widgets.append(self.terminal_filters_widget) + else: + showing_widgets.append(self.terminal_filters_widget) + + if not hiding_widgets and not showing_widgets: + return + + hiding_widgets_queue = Queue() + showing_widgets_queue = Queue() + widgets_by_pos_y = {} + for widget in hiding_widgets: + key = widget.mapToGlobal(widget.rect().topLeft()).x() + widgets_by_pos_y[key] = widget + + for key in sorted(widgets_by_pos_y.keys()): + widget = widgets_by_pos_y[key] + hiding_widgets_queue.put((widget, )) + + for widget in hiding_widgets: + widget.hide() + + for widget in showing_widgets: + widget.show() + + self.footer_widget.updateGeometry() + widgets_by_pos_y = {} + for widget in showing_widgets: + key = widget.mapToGlobal(widget.rect().topLeft()).x() + widgets_by_pos_y[key] = widget + + for key in reversed(sorted(widgets_by_pos_y.keys())): + widget = widgets_by_pos_y[key] + showing_widgets_queue.put(widget) + + for widget in showing_widgets: + widget.hide() + + for widget in hiding_widgets: + widget.show() + + def process_showing(): + if showing_widgets_queue.empty(): + return + + widget = showing_widgets_queue.get() + widget.show() + + widget_rect = widget.frameGeometry() + second_rect = QtCore.QRect(widget_rect) + second_rect.setTopLeft(second_rect.bottomLeft()) + + animation = QtCore.QPropertyAnimation( + widget, b"geometry", self + ) + animation.setDuration(duration) + animation.setStartValue(second_rect) + animation.setEndValue(widget_rect) + animation.setEasingCurve(QtCore.QEasingCurve.OutQuad) + + animation.finished.connect(process_showing) + animation.start() + + def process_hiding(): + if hiding_widgets_queue.empty(): + return process_showing() + + item = hiding_widgets_queue.get() + if isinstance(item, tuple): + widget = item[0] + hiding_widgets_queue.put(widget) + widget_rect = widget.frameGeometry() + second_rect = QtCore.QRect(widget_rect) + second_rect.setTopLeft(second_rect.bottomLeft()) + + anim = QtCore.QPropertyAnimation( + widget, b"geometry", self + ) + anim.setDuration(duration) + anim.setStartValue(widget_rect) + anim.setEndValue(second_rect) + anim.setEasingCurve(QtCore.QEasingCurve.OutQuad) + + anim.finished.connect(process_hiding) + anim.start() + else: + item.hide() + return process_hiding() + + process_hiding() def on_validate_clicked(self): self.comment_box.setEnabled(False) + self.footer_items_visibility() self.intent_box.setEnabled(False) self.validate() def on_play_clicked(self): self.comment_box.setEnabled(False) + self.footer_items_visibility() self.intent_box.setEnabled(False) self.publish() @@ -644,7 +872,7 @@ class Window(QtWidgets.QDialog): def apply_log_suspend_value(self, value): self._suspend_logs = value if self.state["current_page"] == "terminal": - self.on_tab_changed("overview") + self.tabs["overview"].setChecked(True) self.tabs["terminal"].setVisible(not self._suspend_logs) @@ -753,6 +981,7 @@ class Window(QtWidgets.QDialog): comment = self.controller.context.data.get("comment") self.comment_box.setText(comment or None) self.comment_box.setEnabled(True) + self.footer_items_visibility() if self.intent_model.has_items: self.on_intent_changed() From a7f7efa470896c618b17861bbf6d9363e1954ba3 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Sun, 4 Oct 2020 09:57:51 +0200 Subject: [PATCH 792/813] fixed terminal visibility for perspective widget --- pype/tools/pyblish_pype/window.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pype/tools/pyblish_pype/window.py b/pype/tools/pyblish_pype/window.py index 0365962dc2..9c22e41c43 100644 --- a/pype/tools/pyblish_pype/window.py +++ b/pype/tools/pyblish_pype/window.py @@ -720,10 +720,14 @@ class Window(QtWidgets.QDialog): ): target = self.state["current_page"] comment_visibility = ( - not target == "terminal" + not self.perspective_widget.isVisible() + and not target == "terminal" and self.comment_box.isEnabled() ) - terminal_filters_visibility = target == "terminal" + terminal_filters_visibility = ( + target == "terminal" + or self.perspective_widget.isVisible() + ) if comment_visible is not None and comment_visibility: comment_visibility = comment_visible From 91dde033a9c56353d160bacc59c4b55d7fd2de05 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Sun, 4 Oct 2020 09:57:59 +0200 Subject: [PATCH 793/813] lowered animation duration --- pype/tools/pyblish_pype/window.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/tools/pyblish_pype/window.py b/pype/tools/pyblish_pype/window.py index 9c22e41c43..88e3a75af1 100644 --- a/pype/tools/pyblish_pype/window.py +++ b/pype/tools/pyblish_pype/window.py @@ -684,7 +684,7 @@ class Window(QtWidgets.QDialog): target_page.move(curr_pos + offset) - duration = 450 + duration = 250 anim_old = QtCore.QPropertyAnimation( previous_page, b"pos", self From e435b96b1f94f5ec036e5c309649a4152658e110 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 5 Oct 2020 11:12:40 +0200 Subject: [PATCH 794/813] fix(nuke): nukes bug workaround animation keys --- pype/plugins/nuke/load/load_camera_abc.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pype/plugins/nuke/load/load_camera_abc.py b/pype/plugins/nuke/load/load_camera_abc.py index 95ebb65005..12042ce215 100644 --- a/pype/plugins/nuke/load/load_camera_abc.py +++ b/pype/plugins/nuke/load/load_camera_abc.py @@ -50,12 +50,15 @@ class AlembicCameraLoader(api.Loader): inpanel=False ) camera_node.forceValidate() - # camera_node["read_from_file"].setValue(True) - # camera_node["file"].setValue(file) camera_node["frame_rate"].setValue(float(fps)) camera_node["tile_color"].setValue(int("0x3469ffff", 16)) - camera_node["reload"].execute() + # workaround because nuke's bug is not adding animation keys properly + nuke.nodeCopy("%clipboard%") + camera_node_name = camera_node["name"].value() + nuke.delete(camera_node) + nuke.nodePaste("%clipboard%") + camera_node = nuke.toNode(camera_node_name) return containerise( node=camera_node, From d4611c6dadab16a554c9d7ce075ee12176a890ef Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Mon, 5 Oct 2020 11:30:05 +0200 Subject: [PATCH 795/813] bump version --- pype/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/version.py b/pype/version.py index 0f90260218..521faacafe 100644 --- a/pype/version.py +++ b/pype/version.py @@ -1 +1 @@ -__version__ = "2.12.2" +__version__ = "2.12.3" From 73a8581825df437859789f05c8f63eedd095d355 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 5 Oct 2020 12:30:37 +0200 Subject: [PATCH 796/813] fixed animations in instance view --- pype/tools/pyblish_pype/view.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pype/tools/pyblish_pype/view.py b/pype/tools/pyblish_pype/view.py index 542d95bf05..b4a7d1fe5b 100644 --- a/pype/tools/pyblish_pype/view.py +++ b/pype/tools/pyblish_pype/view.py @@ -160,6 +160,7 @@ class InstanceView(OverviewView): def __init__(self, parent=None): super(InstanceView, self).__init__(parent) self.viewport().setMouseTracking(True) + self.clicked.connect(self.item_expand) def mouseMoveEvent(self, event): index = self.indexAt(event.pos()) @@ -244,11 +245,11 @@ class InstanceView(OverviewView): elif event.pos().x() > self.width() - 20: self.show_perspective.emit(index) else: - if event.pos().x() < EXPANDER_WIDTH: - self.item_expand(index) - else: + if event.pos().x() >= EXPANDER_WIDTH: self.group_toggle(index) self.item_expand(index, True) + event.accept() + return True return super(InstanceView, self).mouseReleaseEvent(event) From 596520dec85373f0e9b944e3b52c060ea522aae8 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Mon, 5 Oct 2020 12:46:10 +0200 Subject: [PATCH 797/813] allow skipping of resolution check for a task name --- pype/hosts/harmony/__init__.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/pype/hosts/harmony/__init__.py b/pype/hosts/harmony/__init__.py index f920e38765..a6a3310374 100644 --- a/pype/hosts/harmony/__init__.py +++ b/pype/hosts/harmony/__init__.py @@ -7,6 +7,7 @@ from avalon.vendor import Qt import avalon.tools.sceneinventory import pyblish.api from pype import lib +from pype.api import config signature = str(uuid4()) @@ -53,7 +54,7 @@ def get_asset_settings(): resolution_width = asset_data.get("resolutionWidth") resolution_height = asset_data.get("resolutionHeight") - return { + scene_data = { "fps": fps, "frameStart": frame_start, "frameEnd": frame_end, @@ -61,6 +62,15 @@ def get_asset_settings(): "resolutionHeight": resolution_height } + harmony_config = config.get_presets().["harmony"]["general"] + + skip_resolution_check = harmony_config.get(["skip_resolution_check"], []) + if os.getenv('AVALON_TASK') in skip_resolution_check: + scene_data.pop("resolutionWidth") + scene_data.pop("resolutionHeight") + + return scene_data + def ensure_scene_settings(): settings = get_asset_settings() From 922ec77ea6ce84ee4803cdf9d07ad87c0e55e454 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 5 Oct 2020 13:27:43 +0200 Subject: [PATCH 798/813] fix(nuke): repair workaround node position and name --- pype/plugins/nuke/load/load_camera_abc.py | 46 +++++++++++++---------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/pype/plugins/nuke/load/load_camera_abc.py b/pype/plugins/nuke/load/load_camera_abc.py index 12042ce215..58a4d24b36 100644 --- a/pype/plugins/nuke/load/load_camera_abc.py +++ b/pype/plugins/nuke/load/load_camera_abc.py @@ -1,6 +1,8 @@ -from avalon import api +from avalon import api, io +from avalon.nuke import lib as anlib +from avalon.nuke import containerise, update_container import nuke -from pprint import pformat + class AlembicCameraLoader(api.Loader): """ @@ -13,12 +15,9 @@ class AlembicCameraLoader(api.Loader): label = "Load Alembic Camera" icon = "camera" color = "orange" + node_color = "0x3469ffff" def load(self, context, name, namespace, data): - - # import dependencies - from avalon.nuke import containerise - # get main variables version = context['version'] version_data = version.get("data", {}) @@ -44,21 +43,28 @@ class AlembicCameraLoader(api.Loader): # getting file path file = self.fname.replace("\\", "/") - camera_node = nuke.createNode( - "Camera2", - "file {} read_from_file True".format(file), - inpanel=False - ) - camera_node.forceValidate() - camera_node["frame_rate"].setValue(float(fps)) - camera_node["tile_color"].setValue(int("0x3469ffff", 16)) + with anlib.maintained_selection(): + camera_node = nuke.createNode( + "Camera2", + "name {} file {} read_from_file True".format( + object_name, file), + inpanel=False + ) + camera_node.forceValidate() + camera_node["frame_rate"].setValue(float(fps)) - # workaround because nuke's bug is not adding animation keys properly - nuke.nodeCopy("%clipboard%") - camera_node_name = camera_node["name"].value() - nuke.delete(camera_node) - nuke.nodePaste("%clipboard%") - camera_node = nuke.toNode(camera_node_name) + # workaround because nuke's bug is not adding + # animation keys properly + xpos = camera_node.xpos() + ypos = camera_node.ypos() + nuke.nodeCopy("%clipboard%") + nuke.delete(camera_node) + nuke.nodePaste("%clipboard%") + camera_node = nuke.toNode(object_name) + camera_node.setXYpos(xpos, ypos) + + # color node by correct color by actual version + self.node_version_color(version, camera_node) return containerise( node=camera_node, From b6388d37843cd77bf38b9e7800e3e4bef3d11902 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 5 Oct 2020 13:28:04 +0200 Subject: [PATCH 799/813] feat(nuke): adding update and remove actions --- pype/plugins/nuke/load/load_camera_abc.py | 111 ++++++++++++++++++++++ 1 file changed, 111 insertions(+) diff --git a/pype/plugins/nuke/load/load_camera_abc.py b/pype/plugins/nuke/load/load_camera_abc.py index 58a4d24b36..d8ca125f7f 100644 --- a/pype/plugins/nuke/load/load_camera_abc.py +++ b/pype/plugins/nuke/load/load_camera_abc.py @@ -73,3 +73,114 @@ class AlembicCameraLoader(api.Loader): context=context, loader=self.__class__.__name__, data=data_imprint) + + def update(self, container, representation): + """ + Called by Scene Inventory when look should be updated to current + version. + If any reference edits cannot be applied, eg. shader renamed and + material not present, reference is unloaded and cleaned. + All failed edits are highlighted to the user via message box. + + Args: + container: object that has look to be updated + representation: (dict): relationship data to get proper + representation from DB and persisted + data in .json + Returns: + None + """ + # Get version from io + version = io.find_one({ + "type": "version", + "_id": representation["parent"] + }) + object_name = container['objectName'] + # get corresponding node + camera_node = nuke.toNode(object_name) + + # get main variables + version_data = version.get("data", {}) + vname = version.get("name", None) + first = version_data.get("frameStart", None) + last = version_data.get("frameEnd", None) + fps = version_data.get("fps") or nuke.root()["fps"].getValue() + + # prepare data for imprinting + # add additional metadata from the version to imprint to Avalon knob + add_keys = ["source", "author", "fps"] + + data_imprint = {"frameStart": first, + "frameEnd": last, + "version": vname, + "objectName": object_name} + + for k in add_keys: + data_imprint.update({k: version_data[k]}) + + # getting file path + file = api.get_representation_path(representation).replace("\\", "/") + + with anlib.maintained_selection(): + camera_node = nuke.toNode(object_name) + camera_node['selected'].setValue(True) + + # collect input output dependencies + dependencies = camera_node.dependencies() + dependent = camera_node.dependent() + + camera_node["frame_rate"].setValue(float(fps)) + camera_node["file"].setValue(file) + + # workaround because nuke's bug is + # not adding animation keys properly + xpos = camera_node.xpos() + ypos = camera_node.ypos() + nuke.nodeCopy("%clipboard%") + nuke.delete(camera_node) + nuke.nodePaste("%clipboard%") + camera_node = nuke.toNode(object_name) + camera_node.setXYpos(xpos, ypos) + + # link to original input nodes + for i, input in enumerate(dependencies): + camera_node.setInput(i, input) + # link to original output nodes + for d in dependent: + index = next((i for i, dpcy in enumerate( + d.dependencies()) + if camera_node is dpcy), 0) + d.setInput(index, camera_node) + + # color node by correct color by actual version + self.node_version_color(version, camera_node) + + self.log.info("udated to version: {}".format(version.get("name"))) + + return update_container(camera_node, data_imprint) + + def node_version_color(self, version, node): + """ Coloring a node by correct color by actual version + """ + # get all versions in list + versions = io.find({ + "type": "version", + "parent": version["parent"] + }).distinct('name') + + max_version = max(versions) + + # change color of node + if version.get("name") not in [max_version]: + node["tile_color"].setValue(int("0xd88467ff", 16)) + else: + node["tile_color"].setValue(int(self.node_color, 16)) + + def switch(self, container, representation): + self.update(container, representation) + + def remove(self, container): + from avalon.nuke import viewer_update_and_undo_stop + node = nuke.toNode(container['objectName']) + with viewer_update_and_undo_stop(): + nuke.delete(node) From dc81f5dfac4530cc9222ac742aac9cbd2338f70f Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 5 Oct 2020 13:48:48 +0200 Subject: [PATCH 800/813] fix(nuke): missing arguments to data imprint --- pype/plugins/nuke/load/load_camera_abc.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pype/plugins/nuke/load/load_camera_abc.py b/pype/plugins/nuke/load/load_camera_abc.py index d8ca125f7f..377d60e84b 100644 --- a/pype/plugins/nuke/load/load_camera_abc.py +++ b/pype/plugins/nuke/load/load_camera_abc.py @@ -110,7 +110,8 @@ class AlembicCameraLoader(api.Loader): # add additional metadata from the version to imprint to Avalon knob add_keys = ["source", "author", "fps"] - data_imprint = {"frameStart": first, + data_imprint = {"representation": str(representation["_id"]), + "frameStart": first, "frameEnd": last, "version": vname, "objectName": object_name} From ac0ef2a94a9e615106e60705640e5ab1f759097c Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 5 Oct 2020 13:49:04 +0200 Subject: [PATCH 801/813] clean(nuke): spaces --- pype/plugins/nuke/load/load_backdrop.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pype/plugins/nuke/load/load_backdrop.py b/pype/plugins/nuke/load/load_backdrop.py index 66f9a8e1c1..7d18893965 100644 --- a/pype/plugins/nuke/load/load_backdrop.py +++ b/pype/plugins/nuke/load/load_backdrop.py @@ -240,7 +240,6 @@ class LoadBackdropNodes(api.Loader): return update_container(GN, data_imprint) - def switch(self, container, representation): self.update(container, representation) From 496a29ba992cd2071612b649e1b4204e14e3503f Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Mon, 5 Oct 2020 13:58:57 +0200 Subject: [PATCH 802/813] fixing harmony js function signatures --- pype/hosts/harmony/__init__.py | 32 ++++++++++++------- pype/plugins/harmony/create/create_render.py | 7 ++-- pype/plugins/harmony/load/load_audio.py | 8 ++--- pype/plugins/harmony/load/load_background.py | 8 ++--- .../harmony/load/load_imagesequence.py | 7 ++-- .../harmony/load/load_template_workfile.py | 7 ++-- .../harmony/publish/collect_current_file.py | 7 ++-- .../harmony/publish/collect_palettes.py | 7 ++-- .../harmony/publish/extract_palette.py | 7 ++-- .../plugins/harmony/publish/extract_render.py | 14 ++++---- .../harmony/publish/extract_template.py | 22 +++++++------ .../harmony/publish/extract_workfile.py | 6 ++-- .../plugins/harmony/publish/validate_audio.py | 13 +++++--- .../publish/validate_scene_settings.py | 11 ++++--- 14 files changed, 89 insertions(+), 67 deletions(-) diff --git a/pype/hosts/harmony/__init__.py b/pype/hosts/harmony/__init__.py index a6a3310374..6721939c7e 100644 --- a/pype/hosts/harmony/__init__.py +++ b/pype/hosts/harmony/__init__.py @@ -14,7 +14,9 @@ signature = str(uuid4()) def set_scene_settings(settings): - func = """function %s_func(args) + + signature = harmony.signature("set_scene_settings") + func = """function %s(args) { if (args[0]["fps"]) { @@ -41,7 +43,7 @@ def set_scene_settings(settings): ) } } - %s_func + %s """ % (signature, signature) harmony.send({"function": func, "args": [settings]}) @@ -62,7 +64,7 @@ def get_asset_settings(): "resolutionHeight": resolution_height } - harmony_config = config.get_presets().["harmony"]["general"] + harmony_config = config.get_presets()["harmony"]["general"] skip_resolution_check = harmony_config.get(["skip_resolution_check"], []) if os.getenv('AVALON_TASK') in skip_resolution_check: @@ -121,15 +123,17 @@ def check_inventory(): outdated_containers.append(container) # Colour nodes. - func = """function %s_func(args){ + sig = harmony.signature("set_color") + func = """function %s(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); } } - %s_func - """ % (signature, signature) + %s + """ % (sig, sig) outdated_nodes = [] for container in outdated_containers: if container["loader"] == "ImageSequenceLoader": @@ -158,7 +162,9 @@ def application_launch(): def export_template(backdrops, nodes, filepath): - func = """function %s_func(args) + + sig = harmony.signature("set_color") + func = """function %s(args) { var temp_node = node.add("Top", "temp_note", "NOTE", 0, 0, 0); @@ -193,8 +199,8 @@ def export_template(backdrops, nodes, filepath): Action.perform("onActionUpToParent()", "Node View"); node.deleteNode(template_group, true, true); } - %s_func - """ % (signature, signature) + %s + """ % (sig, sig) harmony.send({ "function": func, "args": [ @@ -235,12 +241,14 @@ def install(): def on_pyblish_instance_toggled(instance, old_value, new_value): """Toggle node enabling on instance toggles.""" - func = """function %s_func(args) + + sig = harmony.signature("enable_node") + func = """function %s(args) { node.setEnable(args[0], args[1]) } - %s_func - """ % (signature, signature) + %s + """ % (sig, sig) try: harmony.send( {"function": func, "args": [instance[0], new_value]} diff --git a/pype/plugins/harmony/create/create_render.py b/pype/plugins/harmony/create/create_render.py index 493c585a09..a94e395241 100644 --- a/pype/plugins/harmony/create/create_render.py +++ b/pype/plugins/harmony/create/create_render.py @@ -13,13 +13,14 @@ class CreateRender(harmony.Creator): super(CreateRender, self).__init__(*args, **kwargs) def setup_node(self, node): - func = """function func(args) + sig = harmony.signature() + func = """function %s(args) { node.setTextAttr(args[0], "DRAWING_TYPE", 1, "PNG4"); node.setTextAttr(args[0], "DRAWING_NAME", 1, args[1]); node.setTextAttr(args[0], "MOVIE_PATH", 1, args[1]); } - func - """ + %s + """ % (sig, sig) path = "{0}/{0}".format(node.split("/")[-1]) harmony.send({"function": func, "args": [node, path]}) diff --git a/pype/plugins/harmony/load/load_audio.py b/pype/plugins/harmony/load/load_audio.py index 600791e61a..71ce5b30e0 100644 --- a/pype/plugins/harmony/load/load_audio.py +++ b/pype/plugins/harmony/load/load_audio.py @@ -1,6 +1,6 @@ from avalon import api, harmony - +sig = harmony.signature() func = """ function getUniqueColumnName( column_prefix ) { @@ -18,14 +18,14 @@ function getUniqueColumnName( column_prefix ) return column_name; } -function func(args) +function %s(args) { var uniqueColumnName = getUniqueColumnName(args[0]); column.add(uniqueColumnName , "SOUND"); column.importSound(uniqueColumnName, 1, args[1]); } -func -""" +%s +""" % (sig, sig) class ImportAudioLoader(api.Loader): diff --git a/pype/plugins/harmony/load/load_background.py b/pype/plugins/harmony/load/load_background.py index f96fc275be..b1be9389e3 100644 --- a/pype/plugins/harmony/load/load_background.py +++ b/pype/plugins/harmony/load/load_background.py @@ -324,9 +324,9 @@ class BackgroundLoader(api.Loader): )["result"] container['nodes'].append(read_node) - # Colour node. - func = """function func(args){ + sig = harmony.signature("set_color") + func = """function %s(args){ for( var i =0; i <= args[0].length - 1; ++i) { var red_color = new ColorRGBA(255, 0, 0, 255); @@ -339,8 +339,8 @@ class BackgroundLoader(api.Loader): } } } - func - """ + %s + """ % (sig, sig) if pype.lib.is_latest(representation): harmony.send({"function": func, "args": [node, "green"]}) else: diff --git a/pype/plugins/harmony/load/load_imagesequence.py b/pype/plugins/harmony/load/load_imagesequence.py index c5f50a7d23..056d5554ad 100644 --- a/pype/plugins/harmony/load/load_imagesequence.py +++ b/pype/plugins/harmony/load/load_imagesequence.py @@ -301,7 +301,8 @@ class ImageSequenceLoader(api.Loader): ) # Colour node. - func = """function func(args){ + sig = harmony.signature("copyFile") + func = """function %s(args){ for( var i =0; i <= args[0].length - 1; ++i) { var red_color = new ColorRGBA(255, 0, 0, 255); @@ -314,8 +315,8 @@ class ImageSequenceLoader(api.Loader): } } } - func - """ + %s + """ % (sig, sig) if pype.lib.is_latest(representation): harmony.send({"function": func, "args": [node, "green"]}) else: diff --git a/pype/plugins/harmony/load/load_template_workfile.py b/pype/plugins/harmony/load/load_template_workfile.py index 3e79cc1903..1d1fd7f7be 100644 --- a/pype/plugins/harmony/load/load_template_workfile.py +++ b/pype/plugins/harmony/load/load_template_workfile.py @@ -21,15 +21,16 @@ class ImportTemplateLoader(api.Loader): with zipfile.ZipFile(zip_file, "r") as zip_ref: zip_ref.extractall(template_path) - func = """function func(args) + sig = harmony.signature("paste") + func = """function %s(args) { var template_path = args[0]; var drag_object = copyPaste.pasteTemplateIntoGroup( template_path, "Top", 1 ); } - func - """ + %s + """ % (sig, sig) harmony.send({"function": func, "args": [template_path]}) diff --git a/pype/plugins/harmony/publish/collect_current_file.py b/pype/plugins/harmony/publish/collect_current_file.py index aab66c2b62..40c154e847 100644 --- a/pype/plugins/harmony/publish/collect_current_file.py +++ b/pype/plugins/harmony/publish/collect_current_file.py @@ -13,15 +13,16 @@ class CollectCurrentFile(pyblish.api.ContextPlugin): def process(self, context): """Inject the current working file""" - func = """function func() + sig = harmony.signature() + func = """function %s() { return ( scene.currentProjectPath() + "/" + scene.currentVersionName() + ".xstage" ); } - func - """ + %s + """ % (sig, sig) current_file = harmony.send({"function": func})["result"] context.data["currentFile"] = os.path.normpath(current_file) diff --git a/pype/plugins/harmony/publish/collect_palettes.py b/pype/plugins/harmony/publish/collect_palettes.py index 2a2c1066c0..dc573c381f 100644 --- a/pype/plugins/harmony/publish/collect_palettes.py +++ b/pype/plugins/harmony/publish/collect_palettes.py @@ -13,7 +13,8 @@ class CollectPalettes(pyblish.api.ContextPlugin): hosts = ["harmony"] def process(self, context): - func = """function func() + sig = harmony.signature() + func = """function %s() { var palette_list = PaletteObjectManager.getScenePaletteList(); @@ -26,8 +27,8 @@ class CollectPalettes(pyblish.api.ContextPlugin): return palettes; } - func - """ + %s + """ % (sig, sig) palettes = harmony.send({"function": func})["result"] for name, id in palettes.items(): diff --git a/pype/plugins/harmony/publish/extract_palette.py b/pype/plugins/harmony/publish/extract_palette.py index 9bca005278..9b5f1f5dc9 100644 --- a/pype/plugins/harmony/publish/extract_palette.py +++ b/pype/plugins/harmony/publish/extract_palette.py @@ -13,14 +13,15 @@ class ExtractPalette(pype.api.Extractor): families = ["harmony.palette"] def process(self, instance): - func = """function func(args) + sig = harmony.signature() + func = """function %s(args) { var palette_list = PaletteObjectManager.getScenePaletteList(); var palette = palette_list.getPaletteById(args[0]); return (palette.getPath() + "/" + palette.getName() + ".plt"); } - func - """ + %s + """ % (sig, sig) palette_file = harmony.send( {"function": func, "args": [instance.data["id"]]} )["result"] diff --git a/pype/plugins/harmony/publish/extract_render.py b/pype/plugins/harmony/publish/extract_render.py index 70dceb9ca2..10e6b05bea 100644 --- a/pype/plugins/harmony/publish/extract_render.py +++ b/pype/plugins/harmony/publish/extract_render.py @@ -21,7 +21,8 @@ class ExtractRender(pyblish.api.InstancePlugin): def process(self, instance): # Collect scene data. - func = """function func(write_node) + sig = harmony.signature() + func = """function %s(write_node) { return [ about.getApplicationPath(), @@ -33,8 +34,8 @@ class ExtractRender(pyblish.api.InstancePlugin): sound.getSoundtrackAll().path() ] } - func - """ + %s + """ % (sig, sig) result = harmony.send( {"function": func, "args": [instance[0]]} )["result"] @@ -50,12 +51,13 @@ class ExtractRender(pyblish.api.InstancePlugin): # Set output path to temp folder. path = tempfile.mkdtemp() - func = """function func(args) + sig = harmony.signature() + func = """function %s(args) { node.setTextAttr(args[0], "DRAWING_NAME", 1, args[1]); } - func - """ + %s + """ % (sig, sig) result = harmony.send( { "function": func, diff --git a/pype/plugins/harmony/publish/extract_template.py b/pype/plugins/harmony/publish/extract_template.py index 1ba0befc54..d6851e4027 100644 --- a/pype/plugins/harmony/publish/extract_template.py +++ b/pype/plugins/harmony/publish/extract_template.py @@ -2,7 +2,7 @@ import os import shutil import pype.api -import avalon.harmony +from avalon import harmony import pype.hosts.harmony @@ -30,7 +30,7 @@ class ExtractTemplate(pype.api.Extractor): unique_backdrops = [backdrops[x] for x in set(backdrops.keys())] # Get non-connected nodes within backdrops. - all_nodes = avalon.harmony.send( + all_nodes = harmony.send( {"function": "node.subNodes", "args": ["Top"]} )["result"] for node in [x for x in all_nodes if x not in dependencies]: @@ -66,7 +66,8 @@ class ExtractTemplate(pype.api.Extractor): instance.data["representations"] = [representation] def get_backdrops(self, node): - func = """function func(probe_node) + sig = harmony.signature() + func = """function %s(probe_node) { var backdrops = Backdrop.backdrops("Top"); var valid_backdrops = []; @@ -92,14 +93,15 @@ class ExtractTemplate(pype.api.Extractor): } return valid_backdrops; } - func - """ - return avalon.harmony.send( + %s + """ % (sig, sig) + return harmony.send( {"function": func, "args": [node]} )["result"] def get_dependencies(self, node, dependencies): - func = """function func(args) + sig = harmony.signature() + func = """function %s(args) { var target_node = args[0]; var numInput = node.numberOfInputPorts(target_node); @@ -110,10 +112,10 @@ class ExtractTemplate(pype.api.Extractor): } return dependencies; } - func - """ + %s + """ % (sig, sig) - current_dependencies = avalon.harmony.send( + current_dependencies = harmony.send( {"function": func, "args": [node]} )["result"] diff --git a/pype/plugins/harmony/publish/extract_workfile.py b/pype/plugins/harmony/publish/extract_workfile.py index 304b70e293..3c5af11021 100644 --- a/pype/plugins/harmony/publish/extract_workfile.py +++ b/pype/plugins/harmony/publish/extract_workfile.py @@ -2,7 +2,7 @@ import os import shutil import pype.api -import avalon.harmony +from avalon import harmony import pype.hosts.harmony @@ -15,10 +15,10 @@ class ExtractWorkfile(pype.api.Extractor): def process(self, instance): # Export template. - backdrops = avalon.harmony.send( + backdrops = harmony.send( {"function": "Backdrop.backdrops", "args": ["Top"]} )["result"] - nodes = avalon.harmony.send( + nodes = harmony.send( {"function": "node.subNodes", "args": ["Top"]} )["result"] staging_dir = self.staging_dir(instance) diff --git a/pype/plugins/harmony/publish/validate_audio.py b/pype/plugins/harmony/publish/validate_audio.py index ba113e7610..cc8d2cdc35 100644 --- a/pype/plugins/harmony/publish/validate_audio.py +++ b/pype/plugins/harmony/publish/validate_audio.py @@ -1,14 +1,17 @@ -import json import os import pyblish.api -import avalon.harmony -import pype.hosts.harmony +from avalon import harmony class ValidateAudio(pyblish.api.InstancePlugin): - """Ensures that there is an audio file in the scene. If you are sure that you want to send render without audio, you can disable this validator before clicking on "publish" """ + """Ensures that there is an audio file in the scene. + + If you are sure that you want to send render without audio, you can + disable this validator before clicking on "publish" + + """ order = pyblish.api.ValidatorOrder label = "Validate Audio" @@ -26,7 +29,7 @@ class ValidateAudio(pyblish.api.InstancePlugin): } func """ - result = avalon.harmony.send( + result = harmony.send( {"function": func, "args": [instance[0]]} )["result"] diff --git a/pype/plugins/harmony/publish/validate_scene_settings.py b/pype/plugins/harmony/publish/validate_scene_settings.py index d7895804bd..fbeedeab77 100644 --- a/pype/plugins/harmony/publish/validate_scene_settings.py +++ b/pype/plugins/harmony/publish/validate_scene_settings.py @@ -2,7 +2,7 @@ import json import pyblish.api -import avalon.harmony +from avalon import harmony import pype.hosts.harmony @@ -46,7 +46,8 @@ class ValidateSceneSettings(pyblish.api.InstancePlugin): for string in self.frame_check_filter): expected_settings.pop("frameEnd") - func = """function func() + sig = harmony.signature() + func = """function %s() { return { "fps": scene.getFrameRate(), @@ -56,9 +57,9 @@ class ValidateSceneSettings(pyblish.api.InstancePlugin): "resolutionHeight": scene.defaultResolutionY() }; } - func - """ - current_settings = avalon.harmony.send({"function": func})["result"] + %s + """ % (sig, sig) + current_settings = harmony.send({"function": func})["result"] invalid_settings = [] for key, value in expected_settings.items(): From 05eeca0307adbb61010cdc76660891a39351df85 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 5 Oct 2020 19:11:40 +0200 Subject: [PATCH 803/813] validate index --- pype/tools/pyblish_pype/view.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pype/tools/pyblish_pype/view.py b/pype/tools/pyblish_pype/view.py index b4a7d1fe5b..11a6217712 100644 --- a/pype/tools/pyblish_pype/view.py +++ b/pype/tools/pyblish_pype/view.py @@ -178,6 +178,8 @@ class InstanceView(OverviewView): self.collapse(index) def group_toggle(self, index): + if not index.isValid(): + return model = index.model() chilren_indexes_checked = [] From b84ad5449f342997301d8ac2569cced51aaba0d9 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 5 Oct 2020 19:11:51 +0200 Subject: [PATCH 804/813] moved variable definition --- pype/tools/pyblish_pype/view.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/tools/pyblish_pype/view.py b/pype/tools/pyblish_pype/view.py index 11a6217712..56c396d027 100644 --- a/pype/tools/pyblish_pype/view.py +++ b/pype/tools/pyblish_pype/view.py @@ -214,8 +214,8 @@ class InstanceView(OverviewView): ): if event.pos().x() < 20: indexes = self.selectionModel().selectedIndexes() + any_checked = False if pos_index in indexes: - any_checked = False for index in indexes: if index.data(QtCore.Qt.CheckStateRole): any_checked = True From 81a0aab10bc80c70080f2aaae7f5fec06c2b8c30 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 5 Oct 2020 19:12:05 +0200 Subject: [PATCH 805/813] clicked signal not used --- pype/tools/pyblish_pype/view.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pype/tools/pyblish_pype/view.py b/pype/tools/pyblish_pype/view.py index 56c396d027..b8fc41b0b2 100644 --- a/pype/tools/pyblish_pype/view.py +++ b/pype/tools/pyblish_pype/view.py @@ -160,7 +160,6 @@ class InstanceView(OverviewView): def __init__(self, parent=None): super(InstanceView, self).__init__(parent) self.viewport().setMouseTracking(True) - self.clicked.connect(self.item_expand) def mouseMoveEvent(self, event): index = self.indexAt(event.pos()) From 939b03d74c0de0b65bfcb305f2639701799e959b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 5 Oct 2020 19:54:38 +0200 Subject: [PATCH 806/813] fixed mouse press/release in instance view --- pype/tools/pyblish_pype/view.py | 125 +++++++++++++++++++++----------- 1 file changed, 82 insertions(+), 43 deletions(-) diff --git a/pype/tools/pyblish_pype/view.py b/pype/tools/pyblish_pype/view.py index b8fc41b0b2..6ecc4dfdc7 100644 --- a/pype/tools/pyblish_pype/view.py +++ b/pype/tools/pyblish_pype/view.py @@ -160,6 +160,8 @@ class InstanceView(OverviewView): def __init__(self, parent=None): super(InstanceView, self).__init__(parent) self.viewport().setMouseTracking(True) + self._pressed_group_index = None + self._pressed_expander = None def mouseMoveEvent(self, event): index = self.indexAt(event.pos()) @@ -204,53 +206,90 @@ class InstanceView(OverviewView): model.setData(index, new_state, QtCore.Qt.CheckStateRole) self.toggled.emit(index, new_state) + def _mouse_press(self, event): + if event.button() != QtCore.Qt.LeftButton: + return + + self._pressed_group_index = None + self._pressed_expander = None + + pos_index = self.indexAt(event.pos()) + if not pos_index.isValid(): + return + + if pos_index.data(Roles.TypeRole) != model.InstanceType: + self._pressed_group_index = pos_index + if event.pos().x() < 20: + self._pressed_expander = True + else: + self._pressed_expander = False + + elif event.pos().x() < 20: + indexes = self.selectionModel().selectedIndexes() + any_checked = False + if pos_index in indexes: + for index in indexes: + if index.data(QtCore.Qt.CheckStateRole): + any_checked = True + break + + for index in indexes: + self.toggled.emit(index, not any_checked) + return True + self.toggled.emit(pos_index, not any_checked) + + elif event.pos().x() > self.width() - 20: + self.show_perspective.emit(pos_index) + def mousePressEvent(self, event): - if event.button() == QtCore.Qt.LeftButton: - pos_index = self.indexAt(event.pos()) - if ( - pos_index.isValid() - and pos_index.data(Roles.TypeRole) == model.InstanceType - ): - if event.pos().x() < 20: - indexes = self.selectionModel().selectedIndexes() - any_checked = False - if pos_index in indexes: - for index in indexes: - if index.data(QtCore.Qt.CheckStateRole): - any_checked = True - break - - for index in indexes: - self.toggled.emit(index, not any_checked) - return - - else: - self.toggled.emit(pos_index, not any_checked) - - elif event.pos().x() > self.width() - 20: - self.show_perspective.emit(pos_index) - + if self._mouse_press(event): + return return super(InstanceView, self).mousePressEvent(event) - def mouseReleaseEvent(self, event): - if event.button() == QtCore.Qt.LeftButton: + def _mouse_release(self, event, pressed_expander, pressed_index): + if event.button() != QtCore.Qt.LeftButton: + return + + pos_index = self.indexAt(event.pos()) + if not pos_index.isValid(): + return + + if pos_index.data(Roles.TypeRole) == model.InstanceType: indexes = self.selectionModel().selectedIndexes() - if len(indexes) == 1: - index = indexes[0] - pos_index = self.indexAt(event.pos()) - if index == pos_index: - # If instance or Plugin - if index.data(Roles.TypeRole) == model.InstanceType: - if event.pos().x() < 20: - self.toggled.emit(index, None) - elif event.pos().x() > self.width() - 20: - self.show_perspective.emit(index) - else: - if event.pos().x() >= EXPANDER_WIDTH: - self.group_toggle(index) - self.item_expand(index, True) - event.accept() - return True + if len(indexes) == 1 and indexes[0] == pos_index: + if event.pos().x() < 20: + self.toggled.emit(indexes[0], None) + elif event.pos().x() > self.width() - 20: + self.show_perspective.emit(indexes[0]) + return True + return + + if pressed_index != pos_index: + return + + if self.state() == QtWidgets.QTreeView.State.DragSelectingState: + indexes = self.selectionModel().selectedIndexes() + if len(indexes) != 1 or indexes[0] != pos_index: + return + + if event.pos().x() < EXPANDER_WIDTH: + if pressed_expander is True: + self.item_expand(pos_index) + return True + else: + if pressed_expander is False: + self.group_toggle(pos_index) + self.item_expand(pos_index, True) + return True + + def mouseReleaseEvent(self, event): + pressed_index = self._pressed_group_index + pressed_expander = self._pressed_expander is True + self._pressed_group_index = None + self._pressed_expander = None + result = self._mouse_release(event, pressed_expander, pressed_index) + if result: + return return super(InstanceView, self).mouseReleaseEvent(event) From 6a6071eb87c1c415073a64d8e4b881e6934606c1 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 5 Oct 2020 20:11:38 +0200 Subject: [PATCH 807/813] added application icon to standalone publisher tool --- pype/tools/standalonepublish/__main__.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/pype/tools/standalonepublish/__main__.py b/pype/tools/standalonepublish/__main__.py index aba8e6c0a4..85a574f8dc 100644 --- a/pype/tools/standalonepublish/__main__.py +++ b/pype/tools/standalonepublish/__main__.py @@ -1,15 +1,26 @@ import os import sys import app +import ctypes import signal -from Qt import QtWidgets +from Qt import QtWidgets, QtGui from avalon import style +from pype.api import resources if __name__ == "__main__": + + # Allow to change icon of running process in windows taskbar + if os.name == "nt": + ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID( + u"standalonepublish" + ) + qt_app = QtWidgets.QApplication([]) # app.setQuitOnLastWindowClosed(False) qt_app.setStyleSheet(style.load_stylesheet()) + icon = QtGui.QIcon(resources.pype_icon_filepath()) + qt_app.setWindowIcon(icon) def signal_handler(sig, frame): print("You pressed Ctrl+C. Process ended.") From 855f363ed90e13198016d136cbf25e66cbb52843 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 5 Oct 2020 21:57:40 +0200 Subject: [PATCH 808/813] colect current pype user makes sure user is filled --- pype/plugins/global/publish/collect_current_pype_user.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/plugins/global/publish/collect_current_pype_user.py b/pype/plugins/global/publish/collect_current_pype_user.py index 359e6b852c..a8947dd8fb 100644 --- a/pype/plugins/global/publish/collect_current_pype_user.py +++ b/pype/plugins/global/publish/collect_current_pype_user.py @@ -13,7 +13,7 @@ class CollectCurrentUserPype(pyblish.api.ContextPlugin): def process(self, context): user = os.getenv("PYPE_USERNAME", "").strip() if not user: - return + user = context.data.get("user", getpass.getuser()) context.data["user"] = user - self.log.debug("Pype user is \"{}\"".format(user)) + self.log.debug("Colected user \"{}\"".format(user)) From 1b8fba5d8a28e620211229e048717ee809e0dec8 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 5 Oct 2020 21:58:07 +0200 Subject: [PATCH 809/813] add `PYPE_USERNAME` to deadline environments so it is filled on farm during publish --- pype/plugins/global/publish/submit_publish_job.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pype/plugins/global/publish/submit_publish_job.py b/pype/plugins/global/publish/submit_publish_job.py index 99f0ae7cb6..d26ec2bf14 100644 --- a/pype/plugins/global/publish/submit_publish_job.py +++ b/pype/plugins/global/publish/submit_publish_job.py @@ -174,7 +174,8 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): "FTRACK_SERVER", "PYPE_METADATA_FILE", "AVALON_PROJECT", - "PYPE_LOG_NO_COLORS" + "PYPE_LOG_NO_COLORS", + "PYPE_USERNAME" ] # custom deadline atributes @@ -297,6 +298,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): environment["PYPE_METADATA_FILE"] = roothless_metadata_path environment["AVALON_PROJECT"] = io.Session["AVALON_PROJECT"] environment["PYPE_LOG_NO_COLORS"] = "1" + environment["PYPE_USERNAME"] = instance.context.data["user"] try: environment["PYPE_PYTHON_EXE"] = os.environ["PYPE_PYTHON_EXE"] except KeyError: From e0ff9cc04f47a46899f4d03381db80f8dbc5f567 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Mon, 5 Oct 2020 23:15:31 +0200 Subject: [PATCH 810/813] fix QApp utilization and minor style changes --- pype/hosts/harmony/__init__.py | 28 +++++++++---------- pype/plugins/harmony/load/load_background.py | 16 ++++++----- .../plugins/harmony/publish/extract_render.py | 2 +- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/pype/hosts/harmony/__init__.py b/pype/hosts/harmony/__init__.py index 6721939c7e..fbf5ca6f12 100644 --- a/pype/hosts/harmony/__init__.py +++ b/pype/hosts/harmony/__init__.py @@ -1,6 +1,5 @@ import os import sys -from uuid import uuid4 from avalon import api, io, harmony from avalon.vendor import Qt @@ -10,9 +9,6 @@ from pype import lib from pype.api import config -signature = str(uuid4()) - - def set_scene_settings(settings): signature = harmony.signature("set_scene_settings") @@ -64,9 +60,12 @@ def get_asset_settings(): "resolutionHeight": resolution_height } - harmony_config = config.get_presets()["harmony"]["general"] + try: + skip_resolution_check = \ + config.get_presets()["harmony"]["general"]["skip_resolution_check"] + except KeyError: + skip_resolution_check = [] - skip_resolution_check = harmony_config.get(["skip_resolution_check"], []) if os.getenv('AVALON_TASK') in skip_resolution_check: scene_data.pop("resolutionWidth") scene_data.pop("resolutionHeight") @@ -86,21 +85,20 @@ def ensure_scene_settings(): valid_settings[key] = value # Warn about missing attributes. - print("Starting new QApplication..") - app = Qt.QtWidgets.QApplication(sys.argv) - - message_box = Qt.QtWidgets.QMessageBox() - message_box.setIcon(Qt.QtWidgets.QMessageBox.Warning) - msg = "Missing attributes:" if invalid_settings: + print("Starting new QApplication..") + app = Qt.QtWidgets.QApplication.instance() + if not app: + app = Qt.QtWidgets.QApplication(sys.argv) + + message_box = Qt.QtWidgets.QMessageBox() + message_box.setIcon(Qt.QtWidgets.QMessageBox.Warning) + msg = "Missing attributes:" for item in invalid_settings: msg += f"\n{item}" message_box.setText(msg) message_box.exec_() - # Garbage collect QApplication. - del app - set_scene_settings(valid_settings) diff --git a/pype/plugins/harmony/load/load_background.py b/pype/plugins/harmony/load/load_background.py index b1be9389e3..5ef4535576 100644 --- a/pype/plugins/harmony/load/load_background.py +++ b/pype/plugins/harmony/load/load_background.py @@ -1,11 +1,9 @@ import os -import uuid - -import clique +import json from avalon import api, harmony import pype.lib -import json + copy_files = """function copyFile(srcFilename, dstFilename) { @@ -256,7 +254,9 @@ class BackgroundLoader(api.Loader): container_nodes = [] for layer in sorted(layers): - file_to_import = [os.path.join(bg_folder, layer).replace("\\", "/")] + file_to_import = [ + os.path.join(bg_folder, layer).replace("\\", "/") + ] read_node = harmony.send( { @@ -301,8 +301,10 @@ class BackgroundLoader(api.Loader): print(container) for layer in sorted(layers): - file_to_import = [os.path.join(bg_folder, layer).replace("\\", "/")] - print(20*"#") + file_to_import = [ + os.path.join(bg_folder, layer).replace("\\", "/") + ] + print(20 * "#") print(f"FILE TO REPLACE: {file_to_import}") print(f"LAYER: {layer}") node = harmony.find_node_by_name(layer, "READ") diff --git a/pype/plugins/harmony/publish/extract_render.py b/pype/plugins/harmony/publish/extract_render.py index 10e6b05bea..4fd61efbbf 100644 --- a/pype/plugins/harmony/publish/extract_render.py +++ b/pype/plugins/harmony/publish/extract_render.py @@ -91,7 +91,7 @@ class ExtractRender(pyblish.api.InstancePlugin): if len(collections) > 1: for col in collections: if len(list(col)) > 1: - collection = col + collection = col else: collection = collections[0] From 9772bf76607106b3f8a563aa0cf1966595993007 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 6 Oct 2020 14:07:52 +0200 Subject: [PATCH 811/813] add audio to instance.data only if audio file exists --- pype/plugins/harmony/publish/extract_render.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pype/plugins/harmony/publish/extract_render.py b/pype/plugins/harmony/publish/extract_render.py index 4fd61efbbf..9d3ae33e23 100644 --- a/pype/plugins/harmony/publish/extract_render.py +++ b/pype/plugins/harmony/publish/extract_render.py @@ -45,8 +45,7 @@ class ExtractRender(pyblish.api.InstancePlugin): frame_start = result[4] frame_end = result[5] audio_path = result[6] - if audio_path: - instance.data["audio"] = [{"filename": audio_path}] + instance.data["fps"] = frame_rate # Set output path to temp folder. @@ -139,6 +138,9 @@ class ExtractRender(pyblish.api.InstancePlugin): } instance.data["representations"] = [representation, thumbnail] + if audio_path and os.path.exists(audio_path): + instance.data["audio"] = [{"filename": audio_path}] + # Required for extract_review plugin (L222 onwards). instance.data["frameStart"] = frame_start instance.data["frameEnd"] = frame_end From f04301bf5e05d5f808b628afd4cd34379801b3e9 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 6 Oct 2020 14:33:44 +0200 Subject: [PATCH 812/813] fixed instance toggle --- pype/tools/pyblish_pype/view.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pype/tools/pyblish_pype/view.py b/pype/tools/pyblish_pype/view.py index 6ecc4dfdc7..248c1fbbf9 100644 --- a/pype/tools/pyblish_pype/view.py +++ b/pype/tools/pyblish_pype/view.py @@ -227,6 +227,9 @@ class InstanceView(OverviewView): elif event.pos().x() < 20: indexes = self.selectionModel().selectedIndexes() any_checked = False + if len(indexes) <= 1: + return + if pos_index in indexes: for index in indexes: if index.data(QtCore.Qt.CheckStateRole): @@ -238,9 +241,6 @@ class InstanceView(OverviewView): return True self.toggled.emit(pos_index, not any_checked) - elif event.pos().x() > self.width() - 20: - self.show_perspective.emit(pos_index) - def mousePressEvent(self, event): if self._mouse_press(event): return From b08395579fcdc43873489057ab0524de9fd044c9 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Tue, 6 Oct 2020 15:04:30 +0200 Subject: [PATCH 813/813] user pype user in deadline submission --- pype/plugins/maya/publish/submit_maya_deadline.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/plugins/maya/publish/submit_maya_deadline.py b/pype/plugins/maya/publish/submit_maya_deadline.py index 7509b5875a..0ae19cbb81 100644 --- a/pype/plugins/maya/publish/submit_maya_deadline.py +++ b/pype/plugins/maya/publish/submit_maya_deadline.py @@ -348,7 +348,7 @@ class MayaSubmitDeadline(pyblish.api.InstancePlugin): comment = context.data.get("comment", "") dirname = os.path.join(workspace, "renders") renderlayer = instance.data['setMembers'] # rs_beauty - deadline_user = context.data.get("deadlineUser", getpass.getuser()) + deadline_user = context.data.get("user", getpass.getuser()) jobname = "%s - %s" % (filename, instance.name) # Get the variables depending on the renderer @@ -418,7 +418,7 @@ class MayaSubmitDeadline(pyblish.api.InstancePlugin): # Adding file dependencies. dependencies = instance.context.data["fileDependencies"] dependencies.append(filepath) - if self.assembly_files: + if self.asset_dependencies: for dependency in dependencies: key = "AssetDependency" + str(dependencies.index(dependency)) payload_skeleton["JobInfo"][key] = dependency