From 643c8d9a1967cff5ffa41409f3bf4a2cd66cc4e7 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 19 Feb 2021 12:31:56 +0100 Subject: [PATCH 01/91] added cli command `extractenvironments` to extract environments --- pype/cli.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/pype/cli.py b/pype/cli.py index 137ae327b2..0588e8b079 100644 --- a/pype/cli.py +++ b/pype/cli.py @@ -108,6 +108,27 @@ def eventserver(debug, ) +@main.command() +@click.argument("output_json_path") +@click.option("--project", help="Project name", default=None) +@click.option("--asset", help="Asset name", default=None) +@click.option("--task", help="Task name", default=None) +@click.option("--app", help="Application name", default=None) +def extractenvironments(output_json_path, project, asset, task, app): + """Extract environment variables for entered context to a json file. + + Entered output filepath will be created if does not exists. + + All context options must be passed otherwise only pype's global + environments will be extracted. + + Context options are "project", "asset", "task", "app" + """ + PypeCommands().extractenvironments( + output_json_path, project, asset, task, app + ) + + @main.command() @click.argument("paths", nargs=-1) @click.option("-g", "--gui", is_flag=True, help="Run pyblish GUI") From 164444e5aac4679166e62ae05242abbaf943dddd Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 19 Feb 2021 12:32:43 +0100 Subject: [PATCH 02/91] implemented logic of extractenvironments in pype commands --- pype/cli.py | 2 +- pype/pype_commands.py | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/pype/cli.py b/pype/cli.py index 0588e8b079..0fa9824c3c 100644 --- a/pype/cli.py +++ b/pype/cli.py @@ -124,7 +124,7 @@ def extractenvironments(output_json_path, project, asset, task, app): Context options are "project", "asset", "task", "app" """ - PypeCommands().extractenvironments( + PypeCommands.extractenvironments( output_json_path, project, asset, task, app ) diff --git a/pype/pype_commands.py b/pype/pype_commands.py index 58a3fe738c..2f81e6a405 100644 --- a/pype/pype_commands.py +++ b/pype/pype_commands.py @@ -2,6 +2,7 @@ """Implementation of Pype commands.""" import os import sys +import json from pathlib import Path from pype.lib import PypeLogger @@ -40,6 +41,22 @@ class PypeCommands: from pype.tools import standalonepublish standalonepublish.main() + @staticmethod + def extractenvironments(output_json_path, project, asset, task, app): + env = os.environ.copy() + if all((project, asset, task, app)): + from pype.api import get_app_environments_for_context + env = get_app_environments_for_context( + project, asset, task, app, env + ) + + output_dir = os.path.dirname(output_json_path) + if not os.path.exists(output_dir): + os.makedirs(output_dir) + + with open(output_json_path, "w") as file_stream: + json.dump(env, file_stream, indent=4) + def publish(self, gui, paths): pass From d5d6e970cde90174f714872d296148a660f3ebdd Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 19 Feb 2021 16:20:46 +0100 Subject: [PATCH 03/91] fixed sync when entity moved under project --- pype/modules/ftrack/events/event_sync_to_avalon.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pype/modules/ftrack/events/event_sync_to_avalon.py b/pype/modules/ftrack/events/event_sync_to_avalon.py index daab3425f3..527f3996e1 100644 --- a/pype/modules/ftrack/events/event_sync_to_avalon.py +++ b/pype/modules/ftrack/events/event_sync_to_avalon.py @@ -1677,15 +1677,15 @@ class SyncToAvalonEvent(BaseEvent): self.updates[mongo_id]["data"] = {} vis_par_id = None + ent_path_items = [self.cur_project["full_name"]] if par_av_ent["type"].lower() != "project": vis_par_id = par_av_ent["_id"] + ent_path_items.extend(par_av_ent["data"]["parents"]) + ent_path_items.append(par_av_ent["name"]) + self.updates[mongo_id]["data"]["visualParent"] = vis_par_id self.moved_in_avalon.append(mongo_id) - # TODO logging - ent_path_items = [self.cur_project["full_name"]] - ent_path_items.extend(par_av_ent["data"]["parents"]) - ent_path_items.append(par_av_ent["name"]) ent_path_items.append(avalon_ent["name"]) ent_path = "/".join(ent_path_items) self.log.debug(( From c6382d71d7dda62dc080ef2d74141680446f3c96 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 22 Feb 2021 18:20:07 +0100 Subject: [PATCH 04/91] initial commit and rought variant of local settings widget --- .../settings/widgets/local_settings.py | 409 ++++++++++++++++++ 1 file changed, 409 insertions(+) create mode 100644 pype/tools/settings/settings/widgets/local_settings.py diff --git a/pype/tools/settings/settings/widgets/local_settings.py b/pype/tools/settings/settings/widgets/local_settings.py new file mode 100644 index 0000000000..2dda046302 --- /dev/null +++ b/pype/tools/settings/settings/widgets/local_settings.py @@ -0,0 +1,409 @@ +import platform +import logging +from Qt import QtWidgets, QtCore +from .widgets import ( + ExpandingWidget, + SpacerWidget +) +from .. import style +from .lib import CHILD_OFFSET +from pype.api import SystemSettings + +log = logging.getLogger(__name__) + + +class Separator(QtWidgets.QFrame): + def __init__(self, height=None, parent=None): + super(Separator, self).__init__(parent) + if height is None: + height = 2 + + splitter_item = QtWidgets.QWidget(self) + splitter_item.setStyleSheet("background-color: #21252B;") + splitter_item.setMinimumHeight(height) + splitter_item.setMaximumHeight(height) + + layout = QtWidgets.QHBoxLayout(self) + layout.setContentsMargins(5, 5, 5, 5) + layout.addWidget(splitter_item) + + +class LocalGeneralWidgets(QtWidgets.QWidget): + def __init__(self, parent): + super(LocalGeneralWidgets, self).__init__(parent) + + mongo_url_label = QtWidgets.QLabel("Pype Mongo URL", self) + mongo_url_input = QtWidgets.QLineEdit(self) + local_site_name_label = QtWidgets.QLabel("Local site name", self) + local_site_name_input = QtWidgets.QLineEdit(self) + + layout = QtWidgets.QGridLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + row = 0 + layout.addWidget(mongo_url_label, row, 0) + layout.addWidget(mongo_url_input, row, 1) + row += 1 + layout.addWidget(local_site_name_label, row, 0) + layout.addWidget(local_site_name_input, row, 1) + + self.mongo_url_input = mongo_url_input + self.local_site_name_input = local_site_name_input + + def set_value(self, value): + mongo_url = "" + site_name = "" + if value: + mongo_url = value.get("mongo_url", mongo_url) + site_name = value.get("site_name", site_name) + self.mongo_url_input.setText(mongo_url) + self.local_site_name_input.setText(site_name) + + def settings_value(self): + # Add changed + # If these have changed then + output = {} + mongo_url = self.mongo_url_input.text() + if mongo_url: + output["mongo_url"] = mongo_url + + local_site_name = self.local_site_name_input.text() + if local_site_name: + output["site_name"] = local_site_name + # Do not return output yet since we don't have mechanism to save or + # load these data through api calls + return None + + +class PathInput(QtWidgets.QWidget): + def __init__( + self, + parent, + executable_placeholder=None, + argument_placeholder=None + ): + super(PathInput, self).__init__(parent) + + executable_input = QtWidgets.QLineEdit(self) + if executable_placeholder: + executable_input.setPlaceholderText(executable_placeholder) + + arguments_input = QtWidgets.QLineEdit(self) + if argument_placeholder: + arguments_input.setPlaceholderText(argument_placeholder) + + layout = QtWidgets.QHBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(5) + + layout.addWidget(executable_input) + layout.addWidget(arguments_input) + + self.executable_input = executable_input + self.arguments_input = arguments_input + + def set_read_only(self, readonly=True): + self.executable_input.setReadOnly(readonly) + self.arguments_input.setReadOnly(readonly) + + def set_value(self, arguments): + executable = "" + args = "" + if arguments: + if isinstance(arguments, str): + executable = arguments + elif isinstance(arguments, list): + executable = arguments[0] + if len(arguments) > 1: + args = " ".join(arguments[1:]) + self.executable_input.setText(executable) + self.arguments_input.setText(args) + + def settings_value(self): + executable = self.executable_input.text() + if not executable: + return None + + output = [executable] + args = self.arguments_input.text() + if args: + output.append(args) + return output + + +class AppVariantWidget(QtWidgets.QWidget): + exec_placeholder = "< Specific path for this machine >" + args_placeholder = "< Launch arguments >" + + def __init__(self, group_label, variant_entity, parent): + super(AppVariantWidget, self).__init__(parent) + + self.input_widget = None + + label = " ".join([group_label, variant_entity.label]) + + expading_widget = ExpandingWidget(label, self) + content_widget = QtWidgets.QWidget(expading_widget) + content_layout = QtWidgets.QVBoxLayout(content_widget) + content_layout.setContentsMargins(CHILD_OFFSET, 5, 0, 0) + + expading_widget.set_content_widget(content_widget) + + # Add expanding widget to main layout + layout = QtWidgets.QHBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.addWidget(expading_widget) + + # TODO For celaction - not sure what is "Celaction publish" for + if not variant_entity["executables"].multiplatform: + warn_label = QtWidgets.QLabel( + "Application without multiplatform paths" + ) + content_layout.addWidget(warn_label) + return + + input_widget = PathInput( + content_widget, self.exec_placeholder, self.args_placeholder + ) + content_layout.addWidget(input_widget) + + studio_executables = ( + variant_entity["executables"][platform.system().lower()] + ) + if len(studio_executables) > 0: + content_layout.addWidget(Separator(parent=self)) + + for item in studio_executables: + path_widget = PathInput(content_widget) + path_widget.set_read_only() + path_widget.set_value(item.value) + content_layout.addWidget(path_widget) + + self.input_widget = input_widget + + def set_value(self, value): + if not self.input_widget: + return + + if not value: + value = [] + self.input_widget.set_value(value) + + def settings_value(self): + if not self.input_widget: + return None + return self.input_widget.settings_value() + + +class AppGroupWidget(QtWidgets.QWidget): + def __init__(self, group_entity, parent): + super(AppGroupWidget, self).__init__(parent) + + valid_variants = {} + for key, entity in group_entity["variants"].items(): + if entity["enabled"]: + valid_variants[key] = entity + + group_label = group_entity.label + expading_widget = ExpandingWidget(group_label, self) + content_widget = QtWidgets.QWidget(expading_widget) + content_layout = QtWidgets.QVBoxLayout(content_widget) + content_layout.setContentsMargins(CHILD_OFFSET, 5, 0, 0) + + widgets_by_variant_name = {} + for variant_name, variant_entity in valid_variants.items(): + variant_widget = AppVariantWidget( + group_label, variant_entity, content_widget + ) + widgets_by_variant_name[variant_name] = variant_widget + content_layout.addWidget(variant_widget) + + expading_widget.set_content_widget(content_widget) + + layout = QtWidgets.QHBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.addWidget(expading_widget) + + self.widgets_by_variant_name = widgets_by_variant_name + + def set_value(self, value): + if not value: + value = {} + + for variant_name, widget in self.widgets_by_variant_name.items(): + widget.set_value(value.get(variant_name)) + + def settings_value(self): + output = {} + for variant_name, widget in self.widgets_by_variant_name.items(): + value = widget.settings_value() + if value: + output[variant_name] = value + + if not output: + return None + return output + + +class LocalApplicationsWidgets(QtWidgets.QWidget): + def __init__(self, system_settings_entity, parent): + super(LocalApplicationsWidgets, self).__init__(parent) + + widgets_by_group_name = {} + + layout = QtWidgets.QVBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + + for key, entity in system_settings_entity["applications"].items(): + # Filter not enabled app groups + if not entity["enabled"]: + continue + + # Check if has enabled any variant + enabled_variant = False + for variant_entity in entity["variants"].values(): + if variant_entity["enabled"]: + enabled_variant = True + break + + if not enabled_variant: + continue + + # Create App group specific widget and store it by the key + group_widget = AppGroupWidget(entity, self) + widgets_by_group_name[key] = group_widget + layout.addWidget(group_widget) + + self.widgets_by_group_name = widgets_by_group_name + + def set_value(self, value): + if not value: + value = {} + + for group_name, widget in self.widgets_by_group_name.items(): + widget.set_value(value.get(group_name)) + + def settings_value(self): + output = {} + for group_name, widget in self.widgets_by_group_name.items(): + value = widget.settings_value() + if value: + output[group_name] = value + if not output: + return None + return output + + +class LocalSettingsWidget(QtWidgets.QWidget): + def __init__(self, parent=None): + super(LocalSettingsWidget, self).__init__(parent) + + self.system_settings = SystemSettings() + # self.project_settings = SystemSettings() + user_settings = {} + + self.main_layout = QtWidgets.QVBoxLayout(self) + + self.general_widget = None + self.apps_widget = None + + self._create_general_ui() + self._create_app_ui() + + # Add spacer to main layout + self.main_layout.addWidget(SpacerWidget(self), 1) + + self.set_value(user_settings) + + def _create_general_ui(self): + # General + general_expand_widget = ExpandingWidget( + "General (Does nothing!)", self + ) + + general_content = QtWidgets.QWidget(self) + general_layout = QtWidgets.QVBoxLayout(general_content) + general_layout.setContentsMargins(CHILD_OFFSET, 5, 0, 0) + general_expand_widget.set_content_widget(general_content) + + general_widget = LocalGeneralWidgets(general_content) + general_layout.addWidget(general_widget) + + self.main_layout.addWidget(general_expand_widget) + + self.general_widget = general_widget + + def _create_app_ui(self): + # Applications + app_expand_widget = ExpandingWidget("Applications", self) + + app_content = QtWidgets.QWidget(self) + app_layout = QtWidgets.QVBoxLayout(app_content) + app_layout.setContentsMargins(CHILD_OFFSET, 5, 0, 0) + app_expand_widget.set_content_widget(app_content) + + app_widget = LocalApplicationsWidgets( + self.system_settings, app_content + ) + app_layout.addWidget(app_widget) + + self.main_layout.addWidget(app_expand_widget) + + self.app_widget = app_widget + + def set_value(self, value): + if not value: + value = {} + + self.general_widget.set_value(value.get("general")) + self.app_widget.set_value(value.get("applications")) + + def settings_value(self): + output = {} + general_value = self.general_widget.settings_value() + if general_value: + output["general"] = general_value + + app_value = self.app_widget.settings_value() + if app_value: + output["applications"] = app_value + return output + + +class LocalSettingsWindow(QtWidgets.QWidget): + def __init__(self, parent=None): + super(LocalSettingsWindow, self).__init__(parent) + + self.resize(1000, 600) + + stylesheet = style.load_stylesheet() + self.setStyleSheet(stylesheet) + + scroll_widget = QtWidgets.QScrollArea(self) + scroll_widget.setObjectName("GroupWidget") + settings_widget = LocalSettingsWidget(scroll_widget) + + scroll_widget.setWidget(settings_widget) + scroll_widget.setWidgetResizable(True) + + footer = QtWidgets.QWidget(self) + save_btn = QtWidgets.QPushButton("Save", footer) + footer_layout = QtWidgets.QHBoxLayout(footer) + footer_layout.addWidget(SpacerWidget(footer), 1) + footer_layout.addWidget(save_btn, 0) + + main_layout = QtWidgets.QVBoxLayout(self) + main_layout.setContentsMargins(0, 0, 0, 0) + main_layout.addWidget(scroll_widget, 1) + main_layout.addWidget(footer, 0) + + save_btn.clicked.connect(self._on_save_clicked) + + self.settings_widget = settings_widget + self.save_btn = save_btn + + def _on_save_clicked(self): + try: + value = self.settings_widget.settings_value() + print(value) + except Exception: + log.warning("Failed to save", exc_info=True) From 912cf1efa1ce439dff5870a0e5f49d5d5e740da6 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 22 Feb 2021 19:22:40 +0100 Subject: [PATCH 05/91] added basics of project settings --- .../settings/widgets/local_settings.py | 86 ++++++++++++++++++- 1 file changed, 83 insertions(+), 3 deletions(-) diff --git a/pype/tools/settings/settings/widgets/local_settings.py b/pype/tools/settings/settings/widgets/local_settings.py index 2dda046302..35ec7d7714 100644 --- a/pype/tools/settings/settings/widgets/local_settings.py +++ b/pype/tools/settings/settings/widgets/local_settings.py @@ -3,11 +3,15 @@ import logging from Qt import QtWidgets, QtCore from .widgets import ( ExpandingWidget, - SpacerWidget + SpacerWidget, + ProjectListWidget ) from .. import style from .lib import CHILD_OFFSET -from pype.api import SystemSettings +from pype.api import ( + SystemSettings, + ProjectSettings +) log = logging.getLogger(__name__) @@ -293,21 +297,83 @@ class LocalApplicationsWidgets(QtWidgets.QWidget): return output +class RootsWidget(QtWidgets.QWidget): + def __init__(self, project_settings, parent): + super(RootsWidget, self).__init__(parent) + + self.project_settings = project_settings + + main_layout = QtWidgets.QVBoxLayout(self) + + self.content_layout = main_layout + + def refresh(self): + roots_entity = self.project_settings["project_anatomy"]["roots"] + for root_name, path_entity in roots_entity.items(): + platform_entity = path_entity[platform.system().lower()] + print(root_name, platform_entity.value) + + + +class _ProjectListWidget(ProjectListWidget): + 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 + + self.select_project(new_project_name) + self.current_project = new_project_name + self.project_changed.emit() + + +class ProjectSettingsWidget(QtWidgets.QWidget): + def __init__(self, project_settings, parent): + super(ProjectSettingsWidget, self).__init__(parent) + + projects_widget = _ProjectListWidget(self) + roots_widget = RootsWidget(project_settings, self) + + main_layout = QtWidgets.QHBoxLayout(self) + main_layout.setContentsMargins(0, 0, 0, 0) + main_layout.addWidget(projects_widget, 0) + main_layout.addWidget(roots_widget, 1) + + projects_widget.refresh() + + projects_widget.project_changed.connect(self._on_project_change) + + self.project_settings = project_settings + + self.projects_widget = projects_widget + self.roots_widget = roots_widget + + def _on_project_change(self): + self.project_settings.change_project( + self.projects_widget.project_name() + ) + self.roots_widget.refresh() + + class LocalSettingsWidget(QtWidgets.QWidget): def __init__(self, parent=None): super(LocalSettingsWidget, self).__init__(parent) self.system_settings = SystemSettings() - # self.project_settings = SystemSettings() + self.project_settings = ProjectSettings() user_settings = {} self.main_layout = QtWidgets.QVBoxLayout(self) self.general_widget = None self.apps_widget = None + self.projects_widget = None self._create_general_ui() self._create_app_ui() + self._create_project_ui() # Add spacer to main layout self.main_layout.addWidget(SpacerWidget(self), 1) @@ -350,6 +416,20 @@ class LocalSettingsWidget(QtWidgets.QWidget): self.app_widget = app_widget + def _create_project_ui(self): + project_expand_widget = ExpandingWidget("Project settings", self) + project_content = QtWidgets.QWidget(self) + project_layout = QtWidgets.QVBoxLayout(project_content) + project_layout.setContentsMargins(CHILD_OFFSET, 5, 0, 0) + project_expand_widget.set_content_widget(project_content) + + projects_widget = ProjectSettingsWidget(self.project_settings, self) + project_layout.addWidget(projects_widget) + + self.main_layout.addWidget(project_expand_widget) + + self.projects_widget = projects_widget + def set_value(self, value): if not value: value = {} From 8b7fa057e85949fb604757e1c38f5c4b32f5ee64 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 22 Feb 2021 19:23:00 +0100 Subject: [PATCH 06/91] roots widgets reflects project settings roots --- .../settings/widgets/local_settings.py | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/pype/tools/settings/settings/widgets/local_settings.py b/pype/tools/settings/settings/widgets/local_settings.py index 35ec7d7714..6b26c2ea03 100644 --- a/pype/tools/settings/settings/widgets/local_settings.py +++ b/pype/tools/settings/settings/widgets/local_settings.py @@ -302,17 +302,47 @@ class RootsWidget(QtWidgets.QWidget): super(RootsWidget, self).__init__(parent) self.project_settings = project_settings + self.widgts_by_root_name = {} main_layout = QtWidgets.QVBoxLayout(self) self.content_layout = main_layout def refresh(self): + while self.content_layout.count(): + item = self.content_layout.itemAt(0) + item.widget().hide() + self.content_layout.removeItem(item) + roots_entity = self.project_settings["project_anatomy"]["roots"] for root_name, path_entity in roots_entity.items(): platform_entity = path_entity[platform.system().lower()] - print(root_name, platform_entity.value) + root_widget = QtWidgets.QWidget(self) + key_input = QtWidgets.QLineEdit(root_widget) + key_input.setText(root_name) + key_input.setReadOnly(True) + + root_input_widget = QtWidgets.QWidget(root_widget) + root_input_layout = QtWidgets.QVBoxLayout(root_input_widget) + + value_input = QtWidgets.QLineEdit(root_input_widget) + value_input.setPlaceholderText( + "< Root overrides for this machine >" + ) + studio_input = QtWidgets.QLineEdit(root_input_widget) + studio_input.setText(platform_entity.value) + studio_input.setReadOnly(True) + + root_input_layout.addWidget(value_input) + root_input_layout.addWidget(studio_input) + + root_layout = QtWidgets.QHBoxLayout(root_widget) + root_layout.addWidget(key_input) + root_layout.addWidget(root_input_widget) + + self.content_layout.addWidget(root_widget) + self.content_layout.addWidget(SpacerWidget(self), 1) class _ProjectListWidget(ProjectListWidget): From ba2d1c7eddd9ed59802cb56a0dd4bdfad130cc47 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Tue, 23 Feb 2021 11:22:30 +0100 Subject: [PATCH 07/91] autorepair unicode strings --- .../publish/repair_unicode_strings.py} | 27 ++++++------------- 1 file changed, 8 insertions(+), 19 deletions(-) rename pype/{hosts/maya/plugins/publish/validate_unicode_strings.py => plugins/publish/repair_unicode_strings.py} (53%) diff --git a/pype/hosts/maya/plugins/publish/validate_unicode_strings.py b/pype/plugins/publish/repair_unicode_strings.py similarity index 53% rename from pype/hosts/maya/plugins/publish/validate_unicode_strings.py rename to pype/plugins/publish/repair_unicode_strings.py index cfcba4f514..593b8f9f31 100644 --- a/pype/hosts/maya/plugins/publish/validate_unicode_strings.py +++ b/pype/plugins/publish/repair_unicode_strings.py @@ -1,26 +1,23 @@ import os -from maya import cmds - import pyblish.api import pype.api -import pype.hosts.maya.api.action -class ValidateUnicodeStrings(pyblish.api.Validator): +class RepairUnicodeStrings(pyblish.api.Collector): """Validate all environment variables are string type. """ - order = pype.api.ValidateContentsOrder - hosts = ['maya'] - families = ['review'] + order = pyblish.api.CollectorOrder label = 'Unicode Strings' - actions = [pype.api.RepairAction] def process(self, instance): - invalid = self.get_invalid(instance) - if invalid: - raise RuntimeError("Found unicode strings in environment variables.") + # invalid = self.get_invalid(instance) + # if invalid: + for key, value in os.environ.items(): + self.log.info(type(value)) + if type(value) is type(u't'): + os.environ[key] = str(value) @classmethod def get_invalid(cls, instance): @@ -30,11 +27,3 @@ class ValidateUnicodeStrings(pyblish.api.Validator): invalid.append((key, value)) return invalid - - @classmethod - def repair(cls, instance): - """Retype all unicodes to strings.""" - - for key, value in os.environ.items(): - if type(value) is type(u't'): - os.environ[key] = str(value) From 0841a0763f65f4aab1eae07d9be71e965b7d7b0d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 23 Feb 2021 11:24:09 +0100 Subject: [PATCH 08/91] root changes are projected as should be --- .../settings/widgets/local_settings.py | 107 ++++++++++++++++-- 1 file changed, 97 insertions(+), 10 deletions(-) diff --git a/pype/tools/settings/settings/widgets/local_settings.py b/pype/tools/settings/settings/widgets/local_settings.py index 6b26c2ea03..c73e1a7871 100644 --- a/pype/tools/settings/settings/widgets/local_settings.py +++ b/pype/tools/settings/settings/widgets/local_settings.py @@ -298,7 +298,10 @@ class LocalApplicationsWidgets(QtWidgets.QWidget): class RootsWidget(QtWidgets.QWidget): + value_changed = QtCore.Signal() + def __init__(self, project_settings, parent): + self._parent_widget = parent super(RootsWidget, self).__init__(parent) self.project_settings = project_settings @@ -314,36 +317,78 @@ class RootsWidget(QtWidgets.QWidget): item.widget().hide() self.content_layout.removeItem(item) + self.widgts_by_root_name.clear() + + default_placeholder = "< Root overrides for this machine >" + default_root_values = self.local_default_project_values() or {} + roots_entity = self.project_settings["project_anatomy"]["roots"] + is_in_default = self.project_settings.project_name is None for root_name, path_entity in roots_entity.items(): platform_entity = path_entity[platform.system().lower()] root_widget = QtWidgets.QWidget(self) - key_input = QtWidgets.QLineEdit(root_widget) - key_input.setText(root_name) - key_input.setReadOnly(True) + key_label = QtWidgets.QLabel(root_name, root_widget) root_input_widget = QtWidgets.QWidget(root_widget) root_input_layout = QtWidgets.QVBoxLayout(root_input_widget) value_input = QtWidgets.QLineEdit(root_input_widget) - value_input.setPlaceholderText( - "< Root overrides for this machine >" - ) + placeholder = None + if not is_in_default: + placeholder = default_root_values.get(root_name) + if placeholder: + placeholder = "< {} >".format(placeholder) + + if not placeholder: + placeholder = default_placeholder + value_input.setPlaceholderText(placeholder) + value_input.textChanged.connect(self._on_root_value_change) + studio_input = QtWidgets.QLineEdit(root_input_widget) studio_input.setText(platform_entity.value) studio_input.setReadOnly(True) root_input_layout.addWidget(value_input) + root_input_layout.addWidget(Separator(parent=self)) root_input_layout.addWidget(studio_input) root_layout = QtWidgets.QHBoxLayout(root_widget) - root_layout.addWidget(key_input) + root_layout.addWidget(key_label) root_layout.addWidget(root_input_widget) self.content_layout.addWidget(root_widget) + self.widgts_by_root_name[root_name] = value_input + self.content_layout.addWidget(SpacerWidget(self), 1) + def _on_root_value_change(self): + self.value_changed.emit() + + def local_default_project_values(self): + default_project = self._parent_widget.per_project_settings.get(None) + if default_project: + return default_project.get("roots") + return None + + def set_value(self, value): + if not value: + value = {} + + for root_name, widget in self.widgts_by_root_name.items(): + root_value = value.get(root_name) or "" + widget.setText(root_value) + + def settings_value(self): + output = {} + for root_name, widget in self.widgts_by_root_name.items(): + value = widget.text() + if value: + output[root_name] = value + if not output: + return None + return output + class _ProjectListWidget(ProjectListWidget): def on_item_clicked(self, new_index): @@ -363,6 +408,8 @@ class ProjectSettingsWidget(QtWidgets.QWidget): def __init__(self, project_settings, parent): super(ProjectSettingsWidget, self).__init__(parent) + self.per_project_settings = {} + projects_widget = _ProjectListWidget(self) roots_widget = RootsWidget(project_settings, self) @@ -374,18 +421,53 @@ class ProjectSettingsWidget(QtWidgets.QWidget): projects_widget.refresh() projects_widget.project_changed.connect(self._on_project_change) + roots_widget.value_changed.connect(self._on_root_value_change) + + roots_widget.refresh() self.project_settings = project_settings self.projects_widget = projects_widget self.roots_widget = roots_widget + def _current_value(self): + roots_value = self.roots_widget.settings_value() + current_value = {} + if roots_value: + current_value["roots"] = roots_value + return current_value + + def project_name(self): + return self.projects_widget.project_name() + def _on_project_change(self): - self.project_settings.change_project( - self.projects_widget.project_name() - ) + project_name = self.project_name() + + self.project_settings.change_project(project_name) self.roots_widget.refresh() + project_value = self.per_project_settings.get(project_name) or {} + self.roots_widget.set_value(project_value.get("roots")) + + def _on_root_value_change(self): + self.per_project_settings[self.project_name()] = ( + self._current_value() + ) + + def set_value(self, value): + if not value: + value = {} + self.per_project_settings = value + + def settings_value(self): + output = {} + for project_name, value in self.per_project_settings.items(): + if value: + output[project_name] = value + if not output: + return None + return output + class LocalSettingsWidget(QtWidgets.QWidget): def __init__(self, parent=None): @@ -466,6 +548,7 @@ class LocalSettingsWidget(QtWidgets.QWidget): self.general_widget.set_value(value.get("general")) self.app_widget.set_value(value.get("applications")) + self.projects_widget.set_value(value.get("projects")) def settings_value(self): output = {} @@ -476,6 +559,10 @@ class LocalSettingsWidget(QtWidgets.QWidget): app_value = self.app_widget.settings_value() if app_value: output["applications"] = app_value + + projects_value = self.projects_widget.settings_value() + if projects_value: + output["projects"] = projects_value return output From 55df47c8f3656ae02240cd110efe8257767b738e Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Tue, 23 Feb 2021 12:39:15 +0100 Subject: [PATCH 09/91] ensure keys are converted to string as well --- pype/lib/execute.py | 2 +- pype/plugins/publish/repair_unicode_strings.py | 18 +++--------------- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/pype/lib/execute.py b/pype/lib/execute.py index 7e37e5d6da..f815d05f1b 100644 --- a/pype/lib/execute.py +++ b/pype/lib/execute.py @@ -94,7 +94,7 @@ def run_subprocess(*args, **kwargs): # not passed. env = kwargs.get("env") or os.environ # Make sure environment contains only strings - filtered_env = {k: str(v) for k, v in env.items()} + filtered_env = {str(k): str(v) for k, v in env.items()} # Use lib's logger if was not passed with kwargs. logger = kwargs.pop("logger", log) diff --git a/pype/plugins/publish/repair_unicode_strings.py b/pype/plugins/publish/repair_unicode_strings.py index 593b8f9f31..59e629a892 100644 --- a/pype/plugins/publish/repair_unicode_strings.py +++ b/pype/plugins/publish/repair_unicode_strings.py @@ -10,20 +10,8 @@ class RepairUnicodeStrings(pyblish.api.Collector): order = pyblish.api.CollectorOrder label = 'Unicode Strings' + actions = [pype.api.RepairContextAction] - def process(self, instance): - # invalid = self.get_invalid(instance) - # if invalid: + def process(self, context): for key, value in os.environ.items(): - self.log.info(type(value)) - if type(value) is type(u't'): - os.environ[key] = str(value) - - @classmethod - def get_invalid(cls, instance): - invalid = [] - for key, value in os.environ.items(): - if type(value) is type(u't'): - invalid.append((key, value)) - - return invalid + os.environ[str(key)] = str(value) From ca381e1a8c306bdab47ac7fda695a81c32cb565b Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Tue, 23 Feb 2021 12:40:43 +0100 Subject: [PATCH 10/91] remove repair action --- pype/plugins/publish/repair_unicode_strings.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pype/plugins/publish/repair_unicode_strings.py b/pype/plugins/publish/repair_unicode_strings.py index 59e629a892..fdd4bb7d03 100644 --- a/pype/plugins/publish/repair_unicode_strings.py +++ b/pype/plugins/publish/repair_unicode_strings.py @@ -1,6 +1,5 @@ import os import pyblish.api -import pype.api class RepairUnicodeStrings(pyblish.api.Collector): @@ -10,7 +9,6 @@ class RepairUnicodeStrings(pyblish.api.Collector): order = pyblish.api.CollectorOrder label = 'Unicode Strings' - actions = [pype.api.RepairContextAction] def process(self, context): for key, value in os.environ.items(): From cdf6eedecc2548af58479fa6a94557121d710723 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 23 Feb 2021 15:26:27 +0100 Subject: [PATCH 11/91] added function for validation of mongo uri --- pype/lib/__init__.py | 2 ++ pype/lib/mongo.py | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/pype/lib/__init__.py b/pype/lib/__init__.py index 4b7e59ab3d..9d04c1ce31 100644 --- a/pype/lib/__init__.py +++ b/pype/lib/__init__.py @@ -23,6 +23,7 @@ from .mongo import ( decompose_url, compose_url, get_default_components, + validate_mongo_connection, PypeMongoConnection ) from .anatomy import ( @@ -191,6 +192,7 @@ __all__ = [ "decompose_url", "compose_url", "get_default_components", + "validate_mongo_connection", "PypeMongoConnection", "IniSettingRegistry", diff --git a/pype/lib/mongo.py b/pype/lib/mongo.py index f82c8b5e23..04798d88ff 100644 --- a/pype/lib/mongo.py +++ b/pype/lib/mongo.py @@ -93,6 +93,42 @@ def extract_port_from_url(url): return parsed_url.port +def validate_mongo_connection(mongo_uri): + """Check if provided mongodb URL is valid. + + Args: + mongo_uri (str): URL to validate. + + Raises: + ValueError: When port in mongo uri is not valid. + pymongo.errors.InvalidURI: If passed mongo is invalid. + pymongo.errors.ServerSelectionTimeoutError: If connection timeout + passed so probably couldn't connect to mongo server. + + """ + parsed = urlparse(mongo_uri) + # Force validation of scheme + if parsed.scheme not in ["mongodb", "mongodb+srv"]: + raise pymongo.errors.InvalidURI(( + "Invalid URI scheme:" + " URI must begin with 'mongodb://' or 'mongodb+srv://'" + )) + # we have mongo connection string. Let's try if we can connect. + components = decompose_url(mongo_uri) + mongo_args = { + "host": compose_url(**components), + "serverSelectionTimeoutMS": 1000 + } + port = components.get("port") + if port is not None: + mongo_args["port"] = int(port) + + # Create connection + client = pymongo.MongoClient(**mongo_args) + client.server_info() + client.close() + + class PypeMongoConnection: """Singleton MongoDB connection. From 958d221c449c31be01339f9f24acaf55c405cd93 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 23 Feb 2021 16:24:44 +0100 Subject: [PATCH 12/91] added functions to change pype mongo url and get local site id --- pype/api.py | 9 +++++++-- pype/lib/__init__.py | 7 ++++++- pype/lib/user_settings.py | 42 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 3 deletions(-) diff --git a/pype/api.py b/pype/api.py index 71c31efa93..44caa7632f 100644 --- a/pype/api.py +++ b/pype/api.py @@ -22,7 +22,9 @@ from .lib import ( get_app_environments_for_context, source_hash, get_latest_version, - get_global_environments + get_global_environments, + get_local_site_id, + change_pype_mongo_url ) from .lib.mongo import ( @@ -109,5 +111,8 @@ __all__ = [ "run_subprocess", "get_latest_version", - "get_global_environments" + "get_global_environments", + + "get_local_site_id", + "change_pype_mongo_url" ] diff --git a/pype/lib/__init__.py b/pype/lib/__init__.py index 9d04c1ce31..b5674a9e5d 100644 --- a/pype/lib/__init__.py +++ b/pype/lib/__init__.py @@ -95,7 +95,9 @@ from .plugin_tools import ( from .user_settings import ( IniSettingRegistry, JSONSettingRegistry, - PypeSettingsRegistry + PypeSettingsRegistry, + get_local_site_id, + change_pype_mongo_url ) from .path_tools import ( @@ -198,6 +200,9 @@ __all__ = [ "IniSettingRegistry", "JSONSettingRegistry", "PypeSettingsRegistry", + "get_local_site_id", + "change_pype_mongo_url", + "timeit", "is_overlapping_otio_ranges", diff --git a/pype/lib/user_settings.py b/pype/lib/user_settings.py index 00ce68cb0b..def126d913 100644 --- a/pype/lib/user_settings.py +++ b/pype/lib/user_settings.py @@ -28,6 +28,8 @@ import platform import appdirs import six +from .import validate_mongo_connection + @six.add_metaclass(ABCMeta) class ASettingRegistry(): @@ -464,3 +466,43 @@ class PypeSettingsRegistry(JSONSettingRegistry): self.product = "pype" path = appdirs.user_data_dir(self.product, self.vendor) super(PypeSettingsRegistry, self).__init__("pype_settings", path) + + +def _create_local_site_id(registry=None): + """Create a local site identifier.""" + from uuid import uuid4 + + if registry is None: + registry = PypeSettingsRegistry() + + new_id = str(uuid4()) + + print("Created local site id \"{}\"".format(new_id)) + + registry.set_secure_item("localId", new_id) + + return new_id + + +def get_local_site_id(): + """Get local site identifier. + + Identifier is created if does not exists yet. + """ + registry = PypeSettingsRegistry() + try: + return registry.get_secure_item("localId") + except ValueError: + return _create_local_site_id() + + +def change_pype_mongo_url(new_mongo_url): + """Change mongo url in pype registry. + + Change of Pype mongo URL require restart of running pype processes or + processes using pype. + """ + + validate_mongo_connection(new_mongo_url) + registry = PypeSettingsRegistry() + registry.set_secure_item("pypeMongo", new_mongo_url) From ebdd83d1965ac1cde7f14ca4964ea94026ff7815 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 23 Feb 2021 16:25:24 +0100 Subject: [PATCH 13/91] added abstract handler of local settings --- pype/settings/handlers.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/pype/settings/handlers.py b/pype/settings/handlers.py index 0df4c98820..f9e1ad514a 100644 --- a/pype/settings/handlers.py +++ b/pype/settings/handlers.py @@ -103,6 +103,23 @@ class SettingsHandler: pass +@six.add_metaclass(ABCMeta) +class LocalSettingsHandler: + @abstractmethod + def save_local_settings(self, data): + """Save local data of local settings. + + Args: + data(dict): Data of local data with override metadata. + """ + pass + + @abstractmethod + def get_local_settings(self): + """Studio overrides of system settings.""" + pass + + class SettingsFileHandler(SettingsHandler): def __init__(self): self.log = logging.getLogger("SettingsFileHandler") From cc47f5d2ef9f1a7f126d8de23d7519c3e925ff71 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 23 Feb 2021 17:07:07 +0100 Subject: [PATCH 14/91] Harmony - use regex search for filtering allowed tasks in collecting palettes --- .../harmony/plugins/publish/collect_palettes.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/pype/hosts/harmony/plugins/publish/collect_palettes.py b/pype/hosts/harmony/plugins/publish/collect_palettes.py index 26b83ff171..e6795f894b 100644 --- a/pype/hosts/harmony/plugins/publish/collect_palettes.py +++ b/pype/hosts/harmony/plugins/publish/collect_palettes.py @@ -2,6 +2,7 @@ """Collect palettes from Harmony.""" import os import json +import re import pyblish.api from avalon import harmony @@ -11,8 +12,10 @@ class CollectPalettes(pyblish.api.ContextPlugin): """Gather palettes from scene when publishing templates.""" label = "Palettes" - order = pyblish.api.CollectorOrder + order = pyblish.api.CollectorOrder + 0.003 hosts = ["harmony"] + # list of regexes for task names where collecting should happen + allowed_tasks = [] def process(self, context): """Collector entry point.""" @@ -22,6 +25,13 @@ class CollectPalettes(pyblish.api.ContextPlugin): "function": f"PypeHarmony.Publish.{self_name}.getPalettes", })["result"] + # skip collecting if not in allowed task + if self.allowed_tasks: + task_name = context.data["anatomyData"]["task"].lower() + if (not any([re.search(pattern, task_name) + for pattern in self.allowed_tasks])): + return + for name, id in palettes.items(): instance = context.create_instance(name) instance.data.update({ From d4f21a95b2a9e6857eb2f7c319477c908f867f5d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 23 Feb 2021 17:21:36 +0100 Subject: [PATCH 15/91] added constants for local settings --- pype/settings/constants.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pype/settings/constants.py b/pype/settings/constants.py index c68826f45b..0e346ffb7e 100644 --- a/pype/settings/constants.py +++ b/pype/settings/constants.py @@ -15,7 +15,10 @@ METADATA_KEYS = ( SYSTEM_SETTINGS_KEY = "system_settings" PROJECT_SETTINGS_KEY = "project_settings" PROJECT_ANATOMY_KEY = "project_anatomy" +LOCAL_SETTING_KEY = "local_settings" +# Key replacing `None` as key for local settings values +DEFAULT_PROJECT_KEY = "__default_project__" __all__ = ( "M_OVERRIDEN_KEY", @@ -26,5 +29,8 @@ __all__ = ( "SYSTEM_SETTINGS_KEY", "PROJECT_SETTINGS_KEY", - "PROJECT_ANATOMY_KEY" + "PROJECT_ANATOMY_KEY", + "LOCAL_SETTING_KEY", + + "DEFAULT_PROJECT_KEY" ) From 51ca593a5ee23d2b079c009cfcd8164e2ef117a5 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 23 Feb 2021 17:21:54 +0100 Subject: [PATCH 16/91] implemented local settings handler using mongo --- pype/settings/handlers.py | 68 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/pype/settings/handlers.py b/pype/settings/handlers.py index f9e1ad514a..e7cf53b116 100644 --- a/pype/settings/handlers.py +++ b/pype/settings/handlers.py @@ -10,7 +10,8 @@ import pype from .constants import ( SYSTEM_SETTINGS_KEY, PROJECT_SETTINGS_KEY, - PROJECT_ANATOMY_KEY + PROJECT_ANATOMY_KEY, + LOCAL_SETTING_KEY ) from .lib import load_json_file @@ -512,3 +513,68 @@ class MongoSettingsHandler(SettingsHandler): if not project_name: return {} return self._get_project_anatomy_overrides(project_name) + + +class MongoLocalSettingshandler(LocalSettingsHandler): + """Settings handler that use mongo for store and load local settings.""" + + def __init__(self, local_site_id=None): + # Get mongo connection + from pype.lib import ( + PypeMongoConnection, + get_local_site_id + ) + + if local_site_id is None: + local_site_id = get_local_site_id() + settings_collection = PypeMongoConnection.get_mongo_client() + + # TODO prepare version of pype + # - pype version should define how are settings saved and loaded + + # TODO modify to not use hardcoded keys + database_name = "pype" + collection_name = "settings" + + self.settings_collection = settings_collection + + self.database_name = database_name + self.collection_name = collection_name + + self.collection = settings_collection[database_name][collection_name] + + self.local_site_id = local_site_id + + self.settings_cache = CacheValues() + + def save_local_settings(self, data): + """Save local settings. + + Args: + data(dict): Data of studio overrides with override metadata. + """ + self.settings_cache.update_data(data) + + self.collection.replace_one( + { + "type": LOCAL_SETTING_KEY, + "site_id": self.local_site_id + }, + { + "type": LOCAL_SETTING_KEY, + "site_id": self.local_site_id, + "value": self.settings_cache.to_json_string() + }, + upsert=True + ) + + def get_local_settings(self): + """Local settings for local site id.""" + if self.settings_cache.is_outdated: + document = self.collection.find_one({ + "type": LOCAL_SETTING_KEY, + "site_id": self.local_site_id + }) + + self.settings_cache.update_from_document(document) + return self.settings_cache.data_copy() From 4d1b825328630660e03ad74e674d2a583d08b10b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 23 Feb 2021 17:22:27 +0100 Subject: [PATCH 17/91] implemented functions to load and save local settings --- pype/settings/lib.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/pype/settings/lib.py b/pype/settings/lib.py index dfc46e1a5a..7b76346089 100644 --- a/pype/settings/lib.py +++ b/pype/settings/lib.py @@ -32,6 +32,9 @@ _DEFAULT_SETTINGS = None # Handler of studio overrides _SETTINGS_HANDLER = None +# Handler of local settings +_LOCAL_SETTINGS_HANDLER = None + def require_handler(func): @functools.wraps(func) @@ -43,6 +46,16 @@ def require_handler(func): return wrapper +def require_local_handler(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + global _LOCAL_SETTINGS_HANDLER + if _LOCAL_SETTINGS_HANDLER is None: + _LOCAL_SETTINGS_HANDLER = create_local_settings_handler() + return func(*args, **kwargs) + return wrapper + + def create_settings_handler(): from .handlers import MongoSettingsHandler # Handler can't be created in global space on initialization but only when @@ -50,6 +63,11 @@ def create_settings_handler(): return MongoSettingsHandler() +def create_local_settings_handler(): + from .handlers import MongoLocalSettingshandler + return MongoLocalSettingshandler() + + @require_handler def save_studio_settings(data): return _SETTINGS_HANDLER.save_studio_settings(data) @@ -65,6 +83,16 @@ def save_project_anatomy(project_name, anatomy_data): return _SETTINGS_HANDLER.save_project_anatomy(project_name, anatomy_data) +@require_local_handler +def save_local_settings(data): + return _LOCAL_SETTINGS_HANDLER.save_local_settings(data) + + +@require_local_handler +def get_local_settings(): + return _LOCAL_SETTINGS_HANDLER.get_local_settings() + + @require_handler def get_studio_system_settings_overrides(): return _SETTINGS_HANDLER.get_studio_system_settings_overrides() From f1190ce0534ac70a97bf520ee79c592ce7dfde97 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 23 Feb 2021 17:37:17 +0100 Subject: [PATCH 18/91] removed DEFAULT_PROJECT_KEY from constants as it is handler specific --- pype/settings/constants.py | 6 +----- pype/settings/handlers.py | 30 ++++++++++++++++++++++++------ 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/pype/settings/constants.py b/pype/settings/constants.py index 0e346ffb7e..fcc3a1d2dc 100644 --- a/pype/settings/constants.py +++ b/pype/settings/constants.py @@ -17,8 +17,6 @@ PROJECT_SETTINGS_KEY = "project_settings" PROJECT_ANATOMY_KEY = "project_anatomy" LOCAL_SETTING_KEY = "local_settings" -# Key replacing `None` as key for local settings values -DEFAULT_PROJECT_KEY = "__default_project__" __all__ = ( "M_OVERRIDEN_KEY", @@ -30,7 +28,5 @@ __all__ = ( "SYSTEM_SETTINGS_KEY", "PROJECT_SETTINGS_KEY", "PROJECT_ANATOMY_KEY", - "LOCAL_SETTING_KEY", - - "DEFAULT_PROJECT_KEY" + "LOCAL_SETTING_KEY" ) diff --git a/pype/settings/handlers.py b/pype/settings/handlers.py index e7cf53b116..c76aa6c40a 100644 --- a/pype/settings/handlers.py +++ b/pype/settings/handlers.py @@ -517,6 +517,7 @@ class MongoSettingsHandler(SettingsHandler): class MongoLocalSettingshandler(LocalSettingsHandler): """Settings handler that use mongo for store and load local settings.""" + default_project_key = "__default_project__" def __init__(self, local_site_id=None): # Get mongo connection @@ -545,7 +546,7 @@ class MongoLocalSettingshandler(LocalSettingsHandler): self.local_site_id = local_site_id - self.settings_cache = CacheValues() + self.local_settings_cache = CacheValues() def save_local_settings(self, data): """Save local settings. @@ -553,7 +554,15 @@ class MongoLocalSettingshandler(LocalSettingsHandler): Args: data(dict): Data of studio overrides with override metadata. """ - self.settings_cache.update_data(data) + data = data or {} + + # Replace key `None` (default project values) with constant string + if "projects" in data and None in data["projects"]: + data["projects"][self.default_project_key] = ( + data["projects"].pop(None) + ) + + self.local_settings_cache.update_data(data) self.collection.replace_one( { @@ -563,18 +572,27 @@ class MongoLocalSettingshandler(LocalSettingsHandler): { "type": LOCAL_SETTING_KEY, "site_id": self.local_site_id, - "value": self.settings_cache.to_json_string() + "value": self.local_settings_cache.to_json_string() }, upsert=True ) def get_local_settings(self): """Local settings for local site id.""" - if self.settings_cache.is_outdated: + if self.local_settings_cache.is_outdated: document = self.collection.find_one({ "type": LOCAL_SETTING_KEY, "site_id": self.local_site_id }) - self.settings_cache.update_from_document(document) - return self.settings_cache.data_copy() + self.local_settings_cache.update_from_document(document) + data = self.local_settings_cache.data + if ( + "projects" in data + and self.default_project_key in data["projects"] + ): + data["projects"][None] = data["projects"].pop( + self.default_project_key + ) + + return self.local_settings_cache.data_copy() From 73ad15d79382407fc79a71997d16831da887919a Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 23 Feb 2021 17:38:58 +0100 Subject: [PATCH 19/91] settings are using local settings functions and have specific category for mongo url --- .../settings/widgets/local_settings.py | 177 ++++++++++++++---- 1 file changed, 137 insertions(+), 40 deletions(-) diff --git a/pype/tools/settings/settings/widgets/local_settings.py b/pype/tools/settings/settings/widgets/local_settings.py index c73e1a7871..25d8b249ec 100644 --- a/pype/tools/settings/settings/widgets/local_settings.py +++ b/pype/tools/settings/settings/widgets/local_settings.py @@ -1,3 +1,6 @@ +import os +import sys +import traceback import platform import logging from Qt import QtWidgets, QtCore @@ -8,14 +11,27 @@ from .widgets import ( ) from .. import style from .lib import CHILD_OFFSET +from pype.settings.constants import PROJECT_ANATOMY_KEY +from pype.settings.lib import ( + get_local_settings, + save_local_settings +) from pype.api import ( SystemSettings, - ProjectSettings + ProjectSettings, + change_pype_mongo_url ) +from pymongo.errors import ServerSelectionTimeoutError log = logging.getLogger(__name__) +LOCAL_GENERAL_KEY = "general" +LOCAL_PROJECTS_KEY = "projects" +LOCAL_APPS_KEY = "applications" +LOCAL_ROOTS_KEY = "roots" + + class Separator(QtWidgets.QFrame): def __init__(self, height=None, parent=None): super(Separator, self).__init__(parent) @@ -32,50 +48,107 @@ class Separator(QtWidgets.QFrame): layout.addWidget(splitter_item) +class PypeMongoWidget(QtWidgets.QWidget): + def __init__(self, parent): + super(PypeMongoWidget, self).__init__(parent) + + # Warning label + warning_label = QtWidgets.QLabel(( + "WARNING: Requires restart. Change of Pype Mongo requires to" + " restart of all running Pype processes and process using Pype" + " (Including this)." + "\n- all changes in different categories won't be saved." + ), self) + warning_label.setStyleSheet("font-weight: bold;") + + # Label + mongo_url_label = QtWidgets.QLabel("Pype Mongo URL", self) + + # Input + mongo_url_input = QtWidgets.QLineEdit(self) + mongo_url_input.setPlaceholderText("< Pype Mongo URL >") + mongo_url_input.setText(os.environ["PYPE_MONGO"]) + + # Confirm button + mongo_url_change_btn = QtWidgets.QPushButton("Confirm Change", self) + + layout = QtWidgets.QGridLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.addWidget(warning_label, 0, 0, 1, 3) + layout.addWidget(mongo_url_label, 1, 0) + layout.addWidget(mongo_url_input, 1, 1) + layout.addWidget(mongo_url_change_btn, 1, 2) + + mongo_url_change_btn.clicked.connect(self._on_confirm_click) + + self.mongo_url_input = mongo_url_input + + def _on_confirm_click(self): + value = self.mongo_url_input.text() + + dialog = QtWidgets.QMessageBox(self) + + title = "Pype mongo changed" + message = ( + "Pype mongo url was successfully changed. Restart Pype please." + ) + details = None + + try: + change_pype_mongo_url(value) + except Exception as exc: + if isinstance(exc, ServerSelectionTimeoutError): + error_message = ( + "Connection timeout passed." + " Probably can't connect to the Mongo server." + ) + else: + error_message = str(exc) + + title = "Pype mongo change failed" + # TODO catch exception message more gracefully + message = ( + "Pype mongo change was not successful." + " Full traceback can be found in details section.\n\n" + "Error message:\n{}" + ).format(error_message) + details = "\n".join(traceback.format_exception(*sys.exc_info())) + dialog.setWindowTitle(title) + dialog.setText(message) + if details: + dialog.setDetailedText(details) + dialog.exec_() + + class LocalGeneralWidgets(QtWidgets.QWidget): def __init__(self, parent): super(LocalGeneralWidgets, self).__init__(parent) - mongo_url_label = QtWidgets.QLabel("Pype Mongo URL", self) - mongo_url_input = QtWidgets.QLineEdit(self) - local_site_name_label = QtWidgets.QLabel("Local site name", self) local_site_name_input = QtWidgets.QLineEdit(self) - layout = QtWidgets.QGridLayout(self) + layout = QtWidgets.QFormLayout(self) layout.setContentsMargins(0, 0, 0, 0) - row = 0 - layout.addWidget(mongo_url_label, row, 0) - layout.addWidget(mongo_url_input, row, 1) - row += 1 - layout.addWidget(local_site_name_label, row, 0) - layout.addWidget(local_site_name_input, row, 1) - self.mongo_url_input = mongo_url_input + layout.addRow("Local site name", local_site_name_input) + self.local_site_name_input = local_site_name_input def set_value(self, value): - mongo_url = "" site_name = "" if value: - mongo_url = value.get("mongo_url", mongo_url) site_name = value.get("site_name", site_name) - self.mongo_url_input.setText(mongo_url) self.local_site_name_input.setText(site_name) def settings_value(self): # Add changed # If these have changed then output = {} - mongo_url = self.mongo_url_input.text() - if mongo_url: - output["mongo_url"] = mongo_url - local_site_name = self.local_site_name_input.text() if local_site_name: output["site_name"] = local_site_name # Do not return output yet since we don't have mechanism to save or # load these data through api calls - return None + return output class PathInput(QtWidgets.QWidget): @@ -322,7 +395,9 @@ class RootsWidget(QtWidgets.QWidget): default_placeholder = "< Root overrides for this machine >" default_root_values = self.local_default_project_values() or {} - roots_entity = self.project_settings["project_anatomy"]["roots"] + roots_entity = ( + self.project_settings[PROJECT_ANATOMY_KEY][LOCAL_ROOTS_KEY] + ) is_in_default = self.project_settings.project_name is None for root_name, path_entity in roots_entity.items(): platform_entity = path_entity[platform.system().lower()] @@ -368,7 +443,7 @@ class RootsWidget(QtWidgets.QWidget): def local_default_project_values(self): default_project = self._parent_widget.per_project_settings.get(None) if default_project: - return default_project.get("roots") + return default_project.get(LOCAL_ROOTS_KEY) return None def set_value(self, value): @@ -418,13 +493,9 @@ class ProjectSettingsWidget(QtWidgets.QWidget): main_layout.addWidget(projects_widget, 0) main_layout.addWidget(roots_widget, 1) - projects_widget.refresh() - projects_widget.project_changed.connect(self._on_project_change) roots_widget.value_changed.connect(self._on_root_value_change) - roots_widget.refresh() - self.project_settings = project_settings self.projects_widget = projects_widget @@ -434,7 +505,7 @@ class ProjectSettingsWidget(QtWidgets.QWidget): roots_value = self.roots_widget.settings_value() current_value = {} if roots_value: - current_value["roots"] = roots_value + current_value[LOCAL_ROOTS_KEY] = roots_value return current_value def project_name(self): @@ -447,7 +518,7 @@ class ProjectSettingsWidget(QtWidgets.QWidget): self.roots_widget.refresh() project_value = self.per_project_settings.get(project_name) or {} - self.roots_widget.set_value(project_value.get("roots")) + self.roots_widget.set_value(project_value.get(LOCAL_ROOTS_KEY)) def _on_root_value_change(self): self.per_project_settings[self.project_name()] = ( @@ -459,6 +530,13 @@ class ProjectSettingsWidget(QtWidgets.QWidget): value = {} self.per_project_settings = value + self.projects_widget.refresh() + self.roots_widget.refresh() + + project_name = self.project_name() + project_value = self.per_project_settings.get(project_name) or {} + self.roots_widget.set_value(project_value.get(LOCAL_ROOTS_KEY)) + def settings_value(self): output = {} for project_name, value in self.per_project_settings.items(): @@ -475,14 +553,15 @@ class LocalSettingsWidget(QtWidgets.QWidget): self.system_settings = SystemSettings() self.project_settings = ProjectSettings() - user_settings = {} self.main_layout = QtWidgets.QVBoxLayout(self) + self.pype_mongo_widget = None self.general_widget = None self.apps_widget = None self.projects_widget = None + self._create_pype_mongo_ui() self._create_general_ui() self._create_app_ui() self._create_project_ui() @@ -490,13 +569,23 @@ class LocalSettingsWidget(QtWidgets.QWidget): # Add spacer to main layout self.main_layout.addWidget(SpacerWidget(self), 1) - self.set_value(user_settings) + def _create_pype_mongo_ui(self): + pype_mongo_expand_widget = ExpandingWidget("Pype Mongo URL", self) + pype_mongo_content = QtWidgets.QWidget(self) + pype_mongo_layout = QtWidgets.QVBoxLayout(pype_mongo_content) + pype_mongo_layout.setContentsMargins(CHILD_OFFSET, 5, 0, 0) + pype_mongo_expand_widget.set_content_widget(pype_mongo_content) + + pype_mongo_widget = PypeMongoWidget(self) + pype_mongo_layout.addWidget(pype_mongo_widget) + + self.main_layout.addWidget(pype_mongo_expand_widget) + + self.pype_mongo_widget = pype_mongo_widget def _create_general_ui(self): # General - general_expand_widget = ExpandingWidget( - "General (Does nothing!)", self - ) + general_expand_widget = ExpandingWidget("General", self) general_content = QtWidgets.QWidget(self) general_layout = QtWidgets.QVBoxLayout(general_content) @@ -546,23 +635,23 @@ class LocalSettingsWidget(QtWidgets.QWidget): if not value: value = {} - self.general_widget.set_value(value.get("general")) - self.app_widget.set_value(value.get("applications")) - self.projects_widget.set_value(value.get("projects")) + self.general_widget.set_value(value.get(LOCAL_GENERAL_KEY)) + self.app_widget.set_value(value.get(LOCAL_APPS_KEY)) + self.projects_widget.set_value(value.get(LOCAL_PROJECTS_KEY)) def settings_value(self): output = {} general_value = self.general_widget.settings_value() if general_value: - output["general"] = general_value + output[LOCAL_GENERAL_KEY] = general_value app_value = self.app_widget.settings_value() if app_value: - output["applications"] = app_value + output[LOCAL_APPS_KEY] = app_value projects_value = self.projects_widget.settings_value() if projects_value: - output["projects"] = projects_value + output[LOCAL_PROJECTS_KEY] = projects_value return output @@ -598,9 +687,17 @@ class LocalSettingsWindow(QtWidgets.QWidget): self.settings_widget = settings_widget self.save_btn = save_btn + self.reset() + + def reset(self): + value = get_local_settings() + self.settings_widget.set_value(value) + def _on_save_clicked(self): try: value = self.settings_widget.settings_value() - print(value) except Exception: log.warning("Failed to save", exc_info=True) + return + + save_local_settings(value) From f8d0f6a14176d23be0b6871a3b3030a5a72b9617 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 23 Feb 2021 17:39:14 +0100 Subject: [PATCH 20/91] moved local settings functions in code --- pype/settings/lib.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/pype/settings/lib.py b/pype/settings/lib.py index 7b76346089..ecb53beaff 100644 --- a/pype/settings/lib.py +++ b/pype/settings/lib.py @@ -83,16 +83,6 @@ def save_project_anatomy(project_name, anatomy_data): return _SETTINGS_HANDLER.save_project_anatomy(project_name, anatomy_data) -@require_local_handler -def save_local_settings(data): - return _LOCAL_SETTINGS_HANDLER.save_local_settings(data) - - -@require_local_handler -def get_local_settings(): - return _LOCAL_SETTINGS_HANDLER.get_local_settings() - - @require_handler def get_studio_system_settings_overrides(): return _SETTINGS_HANDLER.get_studio_system_settings_overrides() @@ -118,6 +108,16 @@ def get_project_anatomy_overrides(project_name): return _SETTINGS_HANDLER.get_project_anatomy_overrides(project_name) +@require_local_handler +def save_local_settings(data): + return _LOCAL_SETTINGS_HANDLER.save_local_settings(data) + + +@require_local_handler +def get_local_settings(): + return _LOCAL_SETTINGS_HANDLER.get_local_settings() + + class DuplicatedEnvGroups(Exception): def __init__(self, duplicated): self.origin_duplicated = duplicated From f924afa41e52e12ca66709650a810cb0a5c2965a Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 23 Feb 2021 18:03:46 +0100 Subject: [PATCH 21/91] app executable is stored under specific key --- .../settings/widgets/local_settings.py | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/pype/tools/settings/settings/widgets/local_settings.py b/pype/tools/settings/settings/widgets/local_settings.py index 25d8b249ec..4eef507839 100644 --- a/pype/tools/settings/settings/widgets/local_settings.py +++ b/pype/tools/settings/settings/widgets/local_settings.py @@ -214,7 +214,7 @@ class AppVariantWidget(QtWidgets.QWidget): def __init__(self, group_label, variant_entity, parent): super(AppVariantWidget, self).__init__(parent) - self.input_widget = None + self.executable_input_widget = None label = " ".join([group_label, variant_entity.label]) @@ -238,10 +238,10 @@ class AppVariantWidget(QtWidgets.QWidget): content_layout.addWidget(warn_label) return - input_widget = PathInput( + executable_input_widget = PathInput( content_widget, self.exec_placeholder, self.args_placeholder ) - content_layout.addWidget(input_widget) + content_layout.addWidget(executable_input_widget) studio_executables = ( variant_entity["executables"][platform.system().lower()] @@ -255,20 +255,28 @@ class AppVariantWidget(QtWidgets.QWidget): path_widget.set_value(item.value) content_layout.addWidget(path_widget) - self.input_widget = input_widget + self.executable_input_widget = executable_input_widget def set_value(self, value): - if not self.input_widget: + if not self.executable_input_widget: return if not value: - value = [] - self.input_widget.set_value(value) + value = {} + elif not isinstance(value, dict): + print("Got invalid value type {}. Expected {}".format( + type(value), dict + )) + value = {} + self.executable_input_widget.set_value(value.get("executable")) def settings_value(self): - if not self.input_widget: + if not self.executable_input_widget: return None - return self.input_widget.settings_value() + value = self.executable_input_widget.settings_value() + if not value: + return None + return {"executable": self.executable_input_widget.settings_value()} class AppGroupWidget(QtWidgets.QWidget): From 5deb8d8efe83c3844f452fd97e4ae786d0d390c0 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Tue, 23 Feb 2021 18:17:50 +0100 Subject: [PATCH 22/91] add maya validators to settings --- ...idden.py => validate_rig_joints_hidden.py} | 3 +- .../entities/dict_immutable_keys_entity.py | 2 +- .../schemas/schema_maya_publish.json | 326 +++++++++++------- .../schemas/template_simple_plugin.json | 30 ++ 4 files changed, 237 insertions(+), 124 deletions(-) rename pype/hosts/maya/plugins/publish/{validate_joints_hidden.py => validate_rig_joints_hidden.py} (93%) create mode 100644 pype/settings/entities/schemas/projects_schema/schemas/template_simple_plugin.json diff --git a/pype/hosts/maya/plugins/publish/validate_joints_hidden.py b/pype/hosts/maya/plugins/publish/validate_rig_joints_hidden.py similarity index 93% rename from pype/hosts/maya/plugins/publish/validate_joints_hidden.py rename to pype/hosts/maya/plugins/publish/validate_rig_joints_hidden.py index 61dfcd563d..a102df50de 100644 --- a/pype/hosts/maya/plugins/publish/validate_joints_hidden.py +++ b/pype/hosts/maya/plugins/publish/validate_rig_joints_hidden.py @@ -6,7 +6,7 @@ import pype.hosts.maya.api.action from pype.hosts.maya.api import lib -class ValidateJointsHidden(pyblish.api.InstancePlugin): +class ValidateRigJointsHidden(pyblish.api.InstancePlugin): """Validate all joints are hidden visually. This includes being hidden: @@ -20,7 +20,6 @@ class ValidateJointsHidden(pyblish.api.InstancePlugin): order = pype.api.ValidateContentsOrder hosts = ['maya'] families = ['rig'] - category = 'rig' version = (0, 1, 0) label = "Joints Hidden" actions = [pype.hosts.maya.api.action.SelectInvalidAction, diff --git a/pype/settings/entities/dict_immutable_keys_entity.py b/pype/settings/entities/dict_immutable_keys_entity.py index 854c904f3c..0e6a98bdde 100644 --- a/pype/settings/entities/dict_immutable_keys_entity.py +++ b/pype/settings/entities/dict_immutable_keys_entity.py @@ -130,7 +130,7 @@ class DictImmutableKeysEntity(ItemEntity): continue if child_obj.key in self.non_gui_children: - raise SchemaDuplicatedKeys(self.path, child_obj.key) + raise SchemaDuplicatedKeys("", child_obj.key) self.non_gui_children[child_obj.key] = child_obj if not first: diff --git a/pype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json b/pype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json index 0d705d3d02..6aeba9b8b2 100644 --- a/pype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json +++ b/pype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json @@ -21,135 +21,219 @@ } ] }, + { - "type": "splitter" - }, - { - "type": "label", - "label": "Collectors" - }, - { - "type": "dict", - "collapsible": true, - "key": "ValidateCameraAttributes", - "label": "Validate Camera Attributes", - "checkbox_key": "enabled", + "type": "collapsible-wrap", + "label": "Validators", "children": [ { - "type": "boolean", - "key": "enabled", - "label": "Enabled" + "type": "dict", + "collapsible": true, + "key": "ValidateShaderName", + "label": "ValidateShaderName", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "label", + "label": "Shader name regex can use named capture group asset to validate against current asset name.

Example:
^.*(?P=<asset>.+)_SHD

" + }, + { + "type": "text", + "key": "regex", + "label": "Validation regex" + } + ] + }, + + { + "type": "dict", + "collapsible": true, + "key": "ValidateAttributes", + "label": "ValidateAttributes", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "raw-json", + "key": "attributes", + "label": "Attributes" + } + ] }, { - "type": "boolean", - "key": "optional", - "label": "Optional" + "type": "collapsible-wrap", + "label": "Model", + "children": [ + { + "type": "dict", + "collapsible": true, + "key": "ValidateModelName", + "label": "Validate Model Name", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "label", + "label": "Path to material file defining list of material names to check. This is material name per line simple text file.
It will be checked against named group shader in your Validation regex.

For example:
^.*(?P=<shader>.+)_GEO

" + }, + { + "type": "path-widget", + "key": "material_file", + "label": "Material File", + "multiplatform": true, + "multipath": false + }, + { + "type": "text", + "key": "regex", + "label": "Validation regex" + } + ] + }, + { + "type": "dict", + "collapsible": true, + "key": "ValidateTransformNamingSuffix", + "label": "ValidateTransformNamingSuffix", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "label", + "label": "Validates transform suffix based on the type of its children shapes." + }, + { + "type": "raw-json", + "key": "SUFFIX_NAMING_TABLE", + "label": "Suffix Naming Table" + }, + { + "type": "boolean", + "key": "ALLOW_IF_NOT_IN_SUFFIX_TABLE", + "label": "Allow if suffix not in table" + } + ] + }, + { + "type": "schema_template", + "name": "template_simple_plugin", + "template_data": [ + { + "key": "ValidateColorSets", + "label": "ValidateColorSets" + }, + { + "key": "ValidateMeshHasOverlappingUVs", + "label": "ValidateMeshHasOverlappingUVs" + }, + { + "key": "ValidateMeshArnoldAttributes", + "label": "ValidateMeshArnoldAttributes" + }, + { + "key": "ValidateMeshShaderConnections", + "label": "ValidateMeshShaderConnections" + }, + { + "key": "ValidateMeshSingleUVSet", + "label": "ValidateMeshSingleUVSet" + }, + { + "key": "ValidateMeshHasUVs", + "label": "ValidateMeshHasUVs" + }, + { + "key": "ValidateMeshLaminaFaces", + "label": "ValidateMeshLaminaFaces" + }, + { + "key": "ValidateMeshNonManifold", + "label": "ValidateMeshNonManifold" + }, + { + "key": "ValidateMeshUVSetMap1", + "label": "ValidateMeshUVSetMap1", + "docstring": "Validate model's default uv set exists and is named 'map1'.

In Maya meshes by default have a uv set named 'map1' that cannot be deleted. It can be renamed, however,
introducing some issues with some renderers. As such we ensure the first (default) UV set index is named 'map1'." + }, + { + "key": "ValidateMeshVerticesHaveEdges", + "label": "ValidateMeshVerticesHaveEdges" + }, + { + "key": "ValidateNoAnimation", + "label": "ValidateNoAnimation", + "docstring": "Ensure no keyframes on nodes in the Instance.
Even though a Model would extract without animCurves correctly this avoids getting different
output from a model when extracted from a different frame than the first frame. (Might be overly restrictive though)." + }, + { + "key": "ValidateNoNamespace", + "label": "ValidateNoNamespace" + }, + { + "key": "ValidateNoNullTransforms", + "label": "ValidateNoNullTransforms" + }, + { + "key": "ValidateNoUnknownNodes", + "label": "ValidateNoUnknownNodes" + }, + { + "key": "ValidateNodeNoGhosting", + "label": "ValidateNodeNoGhosting" + }, + { + "key": "ValidateShapeDefaultNames", + "label": "ValidateShapeDefaultNames" + }, + { + "key": "ValidateShapeRenderStats", + "label": "ValidateShapeRenderStats" + }, + { + "key": "ValidateTransformZero", + "label": "ValidateTransformZero" + } + ] + } + ] + }, + { + "type": "schema_template", + "name": "template_simple_plugin", + "template_data": [ + { + "key": "ValidateCameraAttributes", + "label": "Validate Camera Attributes", + "docstring": "" + }, + { + "key": "ValidateAssemblyName", + "label": "Validate Assembly Name" + }, + { + "key": "ValidateAssRelativePaths", + "label": "ValidateAssRelativePaths" + } + ] } ] }, - { - "type": "dict", - "collapsible": true, - "key": "ValidateModelName", - "label": "Validate Model Name", - "checkbox_key": "enabled", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "label", - "label": "Path to material file defining list of material names to check. This is material name per line simple text file.
It will be checked against named group shader in your Validation regex.

For example:
^.*(?P=<shader>.+)_GEO

" - }, - { - "type": "path-widget", - "key": "material_file", - "label": "Material File", - "multiplatform": true, - "multipath": false - }, - { - "type": "text", - "key": "regex", - "label": "Validation regex" - } - ] - }, - { - "type": "dict", - "collapsible": true, - "key": "ValidateAssemblyName", - "label": "Validate Assembly Name", - "checkbox_key": "enabled", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - } - ] - }, - { - "type": "dict", - "collapsible": true, - "key": "ValidateShaderName", - "label": "ValidateShaderName", - "checkbox_key": "enabled", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "label", - "label": "Shader name regex can use named capture group asset to validate against current asset name.

Example:
^.*(?P=<asset>.+)_SHD

" - }, - { - "type": "text", - "key": "regex", - "label": "Validation regex" - } - ] - }, - { - "type": "dict", - "collapsible": true, - "key": "ValidateMeshHasOverlappingUVs", - "label": "ValidateMeshHasOverlappingUVs", - "checkbox_key": "enabled", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - } - ] - }, - { - "type": "dict", - "collapsible": true, - "key": "ValidateAttributes", - "label": "ValidateAttributes", - "checkbox_key": "enabled", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "raw-json", - "key": "attributes", - "label": "Attributes" - } - ] - }, - { - "type": "splitter" - }, { "type": "label", "label": "Extractors" diff --git a/pype/settings/entities/schemas/projects_schema/schemas/template_simple_plugin.json b/pype/settings/entities/schemas/projects_schema/schemas/template_simple_plugin.json new file mode 100644 index 0000000000..88151f7534 --- /dev/null +++ b/pype/settings/entities/schemas/projects_schema/schemas/template_simple_plugin.json @@ -0,0 +1,30 @@ +[ + { + "__default_values__": { + "docstring": "" + } + }, + { + "type": "dict", + "collapsible": true, + "key": "{key}", + "label": "{label}", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "optional", + "label": "Optional" + }, + { + "type": "label", + "label": "{docstring}" + } + ] + } +] \ No newline at end of file From 158119c2c38c1ec69fb832df8d427abc2dd6d038 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 23 Feb 2021 18:32:03 +0100 Subject: [PATCH 23/91] added functions to apply local settings --- pype/settings/lib.py | 74 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/pype/settings/lib.py b/pype/settings/lib.py index ecb53beaff..b07bd99e5d 100644 --- a/pype/settings/lib.py +++ b/pype/settings/lib.py @@ -2,6 +2,7 @@ import os import json import functools import logging +import platform import copy from .constants import ( M_OVERRIDEN_KEY, @@ -337,6 +338,69 @@ def apply_overrides(source_data, override_data): return merge_overrides(_source_data, override_data) +def apply_local_settings_on_system_settings(system_settings, local_settings): + """Apply local settings on studio system settings. + + In local settings are set application executables. + """ + if not local_settings or "applications" not in local_settings: + return + + current_platform = platform.system().lower() + for app_group_name, value in local_settings["applications"].items(): + if not value or app_group_name not in system_settings["applications"]: + continue + + variants = system_settings["applications"][app_group_name]["variants"] + for app_name, app_value in value.items(): + if not app_value or app_name not in variants: + continue + + executable = app_value.get("executable") + if not executable: + continue + platform_executables = variants[app_name]["executables"].get( + current_platform + ) + new_executables = [executable] + new_executables.extend(platform_executables) + variants[app_name]["executables"] = new_executables + + +def apply_local_settings_on_anatomy_settings( + anatomy_settings, local_settings, project_name +): + if not local_settings: + return + + local_project_settings = local_settings.get("projects") + if not local_project_settings: + return + + current_platform = platform.system().lower() + local_defaults = local_project_settings.get(None) + root_data = anatomy_settings["roots"] + if local_defaults and "roots" in local_defaults: + for root_name, path in local_defaults["roots"].items(): + if root_name not in root_data: + continue + anatomy_settings["roots"][root_name][current_platform] = ( + path + ) + if project_name is None: + return + + local_projects = local_project_settings.get(project_name) + if local_projects and "roots" in local_projects: + for root_name, path in local_projects["roots"].items(): + if root_name not in root_data: + print(root_name, "not in root data") + continue + anatomy_settings["roots"][root_name][current_platform] = ( + path + ) + + def get_system_settings(clear_metadata=True): """System settings with applied studio overrides.""" default_values = get_default_settings()[SYSTEM_SETTINGS_KEY] @@ -344,6 +408,10 @@ def get_system_settings(clear_metadata=True): result = apply_overrides(default_values, studio_values) if clear_metadata: clear_metadata_from_settings(result) + # TODO local settings may be required to apply for environments + local_settings = get_local_settings() + apply_local_settings_on_system_settings(result, local_settings) + return result @@ -371,6 +439,8 @@ def get_default_anatomy_settings(clear_metadata=True): result[key] = value if clear_metadata: clear_metadata_from_settings(result) + local_settings = get_local_settings() + apply_local_settings_on_anatomy_settings(result, local_settings, None) return result @@ -396,6 +466,10 @@ def get_anatomy_settings(project_name, clear_metadata=True): result[key] = value if clear_metadata: clear_metadata_from_settings(result) + local_settings = get_local_settings() + apply_local_settings_on_anatomy_settings( + result, local_settings, project_name + ) return result From d3801cbb4d9a86666d099fbb25b3868bfbba7dae Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 23 Feb 2021 18:33:08 +0100 Subject: [PATCH 24/91] localId is not stored as secured item because would not be accessible in python 2 hosts --- pype/lib/user_settings.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/lib/user_settings.py b/pype/lib/user_settings.py index def126d913..70313bc094 100644 --- a/pype/lib/user_settings.py +++ b/pype/lib/user_settings.py @@ -479,7 +479,7 @@ def _create_local_site_id(registry=None): print("Created local site id \"{}\"".format(new_id)) - registry.set_secure_item("localId", new_id) + registry.set_item("localId", new_id) return new_id @@ -491,7 +491,7 @@ def get_local_site_id(): """ registry = PypeSettingsRegistry() try: - return registry.get_secure_item("localId") + return registry.get_item("localId") except ValueError: return _create_local_site_id() From b6a16de3c5f7c21a2255032e978c42c0e2875932 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Tue, 23 Feb 2021 18:35:52 +0100 Subject: [PATCH 25/91] save maya validator defaults --- .../plugins/publish/validate_model_name.py | 1 - .../plugins/publish/validate_shader_name.py | 1 - .../validate_transform_naming_suffix.py | 12 +- .../defaults/project_settings/maya.json | 124 ++++++++++++++++-- 4 files changed, 117 insertions(+), 21 deletions(-) diff --git a/pype/hosts/maya/plugins/publish/validate_model_name.py b/pype/hosts/maya/plugins/publish/validate_model_name.py index f0004dc81e..2f1586538e 100644 --- a/pype/hosts/maya/plugins/publish/validate_model_name.py +++ b/pype/hosts/maya/plugins/publish/validate_model_name.py @@ -22,7 +22,6 @@ class ValidateModelName(pyblish.api.InstancePlugin): # path to shader names definitions # TODO: move it to preset file material_file = None - active = False regex = '(.*)_(\\d)*_(.*)_(GEO)' @classmethod diff --git a/pype/hosts/maya/plugins/publish/validate_shader_name.py b/pype/hosts/maya/plugins/publish/validate_shader_name.py index 4a42ec6e35..a2951d5551 100644 --- a/pype/hosts/maya/plugins/publish/validate_shader_name.py +++ b/pype/hosts/maya/plugins/publish/validate_shader_name.py @@ -13,7 +13,6 @@ class ValidateShaderName(pyblish.api.InstancePlugin): """ optional = True - active = False order = pype.api.ValidateContentsOrder families = ["look"] hosts = ['maya'] diff --git a/pype/hosts/maya/plugins/publish/validate_transform_naming_suffix.py b/pype/hosts/maya/plugins/publish/validate_transform_naming_suffix.py index 14d4e7d5c8..bd290c73c7 100644 --- a/pype/hosts/maya/plugins/publish/validate_transform_naming_suffix.py +++ b/pype/hosts/maya/plugins/publish/validate_transform_naming_suffix.py @@ -35,11 +35,11 @@ class ValidateTransformNamingSuffix(pyblish.api.InstancePlugin): version = (0, 1, 0) label = 'Suffix Naming Conventions' actions = [pype.hosts.maya.api.action.SelectInvalidAction] - SUFFIX_NAMING_TABLE = {'mesh': ["_GEO", "_GES", "_GEP", "_OSD"], - 'nurbsCurve': ["_CRV"], - 'nurbsSurface': ["_NRB"], - 'locator': ["_LOC"], - None: ['_GRP']} + SUFFIX_NAMING_TABLE = {"mesh": ["_GEO", "_GES", "_GEP", "_OSD"], + "nurbsCurve": ["_CRV"], + "nurbsSurface": ["_NRB"], + "locator": ["_LOC"], + "group": ["_GRP"]} ALLOW_IF_NOT_IN_SUFFIX_TABLE = True @@ -88,7 +88,7 @@ class ValidateTransformNamingSuffix(pyblish.api.InstancePlugin): fullPath=True, noIntermediate=True) - shape_type = cmds.nodeType(shapes[0]) if shapes else None + shape_type = cmds.nodeType(shapes[0]) if shapes else "group" if not cls.is_valid_name(transform, shape_type, cls.SUFFIX_NAMING_TABLE, cls.ALLOW_IF_NOT_IN_SUFFIX_TABLE): diff --git a/pype/settings/defaults/project_settings/maya.json b/pype/settings/defaults/project_settings/maya.json index 2307fd8b82..affc6622df 100644 --- a/pype/settings/defaults/project_settings/maya.json +++ b/pype/settings/defaults/project_settings/maya.json @@ -112,32 +112,130 @@ "CollectMayaRender": { "sync_workfile_version": false }, - "ValidateCameraAttributes": { + "ValidateShaderName": { "enabled": false, - "optional": true + "regex": "(?P.*)_(.*)_SHD" + }, + "ValidateAttributes": { + "enabled": false, + "attributes": {} }, "ValidateModelName": { - "enabled": true, + "enabled": false, "material_file": { "windows": "", "darwin": "", "linux": "" }, - "regex": "" + "regex": "(.*)_(\\\\d)*_(.*)_(GEO)" }, - "ValidateAssemblyName": { - "enabled": true - }, - "ValidateShaderName": { + "ValidateTransformNamingSuffix": { "enabled": true, - "regex": "" + "SUFFIX_NAMING_TABLE": { + "mesh": [ + "_GEO", + "_GES", + "_GEP", + "_OSD" + ], + "nurbsCurve": [ + "_CRV" + ], + "nurbsSurface": [ + "_NRB" + ], + "locator": [ + "_LOC" + ], + "group": [ + "_GRP" + ] + }, + "ALLOW_IF_NOT_IN_SUFFIX_TABLE": true + }, + "ValidateColorSets": { + "enabled": false, + "optional": true }, "ValidateMeshHasOverlappingUVs": { - "enabled": false - }, - "ValidateAttributes": { "enabled": false, - "attributes": {} + "optional": true + }, + "ValidateMeshArnoldAttributes": { + "enabled": false, + "optional": true + }, + "ValidateMeshShaderConnections": { + "enabled": true, + "optional": true + }, + "ValidateMeshSingleUVSet": { + "enabled": false, + "optional": true + }, + "ValidateMeshHasUVs": { + "enabled": true, + "optional": true + }, + "ValidateMeshLaminaFaces": { + "enabled": false, + "optional": true + }, + "ValidateMeshNonManifold": { + "enabled": false, + "optional": true + }, + "ValidateMeshUVSetMap1": { + "enabled": false, + "optional": true + }, + "ValidateMeshVerticesHaveEdges": { + "enabled": true, + "optional": true + }, + "ValidateNoAnimation": { + "enabled": false, + "optional": true + }, + "ValidateNoNamespace": { + "enabled": true, + "optional": true + }, + "ValidateNoNullTransforms": { + "enabled": true, + "optional": true + }, + "ValidateNoUnknownNodes": { + "enabled": true, + "optional": true + }, + "ValidateNodeNoGhosting": { + "enabled": false, + "optional": true + }, + "ValidateShapeDefaultNames": { + "enabled": false, + "optional": true + }, + "ValidateShapeRenderStats": { + "enabled": false, + "optional": true + }, + "ValidateTransformZero": { + "enabled": true, + "optional": true + }, + "ValidateCameraAttributes": { + "enabled": false, + "optional": true + }, + "ValidateAssemblyName": { + "enabled": true, + "optional": true + }, + "ValidateAssRelativePaths": { + "enabled": true, + "optional": true }, "ExtractCameraAlembic": { "enabled": true, From 29430f3b8fa19af0f71300e0cc55715fbb250f8a Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 23 Feb 2021 18:40:33 +0100 Subject: [PATCH 26/91] added local settings action to tray --- pype/modules/__init__.py | 5 +- pype/modules/base.py | 1 + pype/modules/settings_action.py | 55 +++++++++++++++++++ pype/tools/settings/__init__.py | 4 +- pype/tools/settings/settings/__init__.py | 2 + .../settings/settings/widgets/__init__.py | 4 +- 6 files changed, 68 insertions(+), 3 deletions(-) diff --git a/pype/modules/__init__.py b/pype/modules/__init__.py index 93fc92f9d5..874d69c41d 100644 --- a/pype/modules/__init__.py +++ b/pype/modules/__init__.py @@ -9,7 +9,10 @@ from .base import ( ModulesManager, TrayModulesManager ) -from .settings_action import SettingsAction +from .settings_action import ( + SettingsAction, + LocalSettingsAction +) from .rest_api import ( RestApiModule, IRestApi diff --git a/pype/modules/base.py b/pype/modules/base.py index ad0fecb8f7..7efd00e39e 100644 --- a/pype/modules/base.py +++ b/pype/modules/base.py @@ -627,6 +627,7 @@ class TrayModulesManager(ModulesManager): "clockify", "standalonepublish_tool", "log_viewer", + "local_settings", "settings" ) diff --git a/pype/modules/settings_action.py b/pype/modules/settings_action.py index c1fa8a68bc..aab10e9ebf 100644 --- a/pype/modules/settings_action.py +++ b/pype/modules/settings_action.py @@ -58,3 +58,58 @@ class SettingsAction(PypeModule, ITrayAction): # Reset content if was not visible if not was_visible: self.settings_window.reset() + + +class LocalSettingsAction(PypeModule, ITrayAction): + """Action to show Setttings tool.""" + name = "local_settings" + label = "Local Settings" + + def initialize(self, _modules_settings): + # This action is always enabled + self.enabled = True + + # Tray attributes + self.settings_window = None + + def connect_with_modules(self, *_a, **_kw): + return + + def tray_init(self): + """Initialization in tray implementation of ITrayAction.""" + self.create_settings_window() + + def on_action_trigger(self): + """Implementation for action trigger of ITrayAction.""" + self.show_settings_window() + + def create_settings_window(self): + """Initializa Settings Qt window.""" + if self.settings_window: + return + from pype.tools.settings import LocalSettingsWindow + self.settings_window = LocalSettingsWindow() + + def show_settings_window(self): + """Show settings tool window. + + Raises: + AssertionError: Window must be already created. Call + `create_settings_window` before callint this method. + """ + if not self.settings_window: + raise AssertionError("Window is not initialized.") + + # Store if was visible + was_visible = self.settings_window.isVisible() + + # Show settings gui + self.settings_window.show() + + # Pull window to the front. + self.settings_window.raise_() + self.settings_window.activateWindow() + + # Reset content if was not visible + if not was_visible: + self.settings_window.reset() diff --git a/pype/tools/settings/__init__.py b/pype/tools/settings/__init__.py index 7bc54f9ab6..578c6ef82c 100644 --- a/pype/tools/settings/__init__.py +++ b/pype/tools/settings/__init__.py @@ -4,7 +4,8 @@ from Qt import QtWidgets, QtGui from .settings import ( style, MainWidget, - ProjectListWidget + ProjectListWidget, + LocalSettingsWindow ) @@ -33,5 +34,6 @@ __all__ = ( "style", "MainWidget", "ProjectListWidget", + "LocalSettingsWindow", "main" ) diff --git a/pype/tools/settings/settings/__init__.py b/pype/tools/settings/settings/__init__.py index 3c12a73639..4d930b3fda 100644 --- a/pype/tools/settings/settings/__init__.py +++ b/pype/tools/settings/settings/__init__.py @@ -1,6 +1,7 @@ from . import style from .widgets import ( MainWidget, + LocalSettingsWindow, ProjectListWidget ) @@ -8,5 +9,6 @@ from .widgets import ( __all__ = ( "style", "MainWidget", + "LocalSettingsWindow", "ProjectListWidget" ) diff --git a/pype/tools/settings/settings/widgets/__init__.py b/pype/tools/settings/settings/widgets/__init__.py index c9fec16f6e..6e31232818 100644 --- a/pype/tools/settings/settings/widgets/__init__.py +++ b/pype/tools/settings/settings/widgets/__init__.py @@ -1,8 +1,10 @@ from .window import MainWidget from .widgets import ProjectListWidget +from .local_settings import LocalSettingsWindow __all__ = [ "MainWidget", - "ProjectListWidget" + "ProjectListWidget", + "LocalSettingsWindow" ] From 2ae94e5f9f26398b0cef81b3556f48c938fbaa18 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 23 Feb 2021 18:40:46 +0100 Subject: [PATCH 27/91] added window title and icon to local settings window --- pype/tools/settings/settings/widgets/local_settings.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pype/tools/settings/settings/widgets/local_settings.py b/pype/tools/settings/settings/widgets/local_settings.py index 4eef507839..d4c5a9a5f8 100644 --- a/pype/tools/settings/settings/widgets/local_settings.py +++ b/pype/tools/settings/settings/widgets/local_settings.py @@ -3,7 +3,7 @@ import sys import traceback import platform import logging -from Qt import QtWidgets, QtCore +from Qt import QtWidgets, QtCore, QtGui from .widgets import ( ExpandingWidget, SpacerWidget, @@ -669,8 +669,11 @@ class LocalSettingsWindow(QtWidgets.QWidget): self.resize(1000, 600) + self.setWindowTitle("Pype Local settings") + stylesheet = style.load_stylesheet() self.setStyleSheet(stylesheet) + self.setWindowIcon(QtGui.QIcon(style.app_icon_path())) scroll_widget = QtWidgets.QScrollArea(self) scroll_widget.setObjectName("GroupWidget") From 0d50cc35be2dfee93e6e71b0baf3dac893cddd79 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 23 Feb 2021 18:43:48 +0100 Subject: [PATCH 28/91] renamed user_settings.py to local_settings.py --- pype/lib/__init__.py | 2 +- pype/lib/{user_settings.py => local_settings.py} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename pype/lib/{user_settings.py => local_settings.py} (99%) diff --git a/pype/lib/__init__.py b/pype/lib/__init__.py index b5674a9e5d..67a31d1737 100644 --- a/pype/lib/__init__.py +++ b/pype/lib/__init__.py @@ -92,7 +92,7 @@ from .plugin_tools import ( should_decompress ) -from .user_settings import ( +from .local_settings import ( IniSettingRegistry, JSONSettingRegistry, PypeSettingsRegistry, diff --git a/pype/lib/user_settings.py b/pype/lib/local_settings.py similarity index 99% rename from pype/lib/user_settings.py rename to pype/lib/local_settings.py index 70313bc094..aa372a52d2 100644 --- a/pype/lib/user_settings.py +++ b/pype/lib/local_settings.py @@ -120,7 +120,7 @@ class ASettingRegistry(): """Delete item from settings. Note: - see :meth:`pype.lib.user_settings.ARegistrySettings.delete_item` + see :meth:`pype.lib.local_settings.ARegistrySettings.delete_item` """ pass From 6e9c337ec5ebe4dbd41a3b6545312768552afa65 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 23 Feb 2021 18:55:38 +0100 Subject: [PATCH 29/91] added local settings module to all --- pype/modules/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pype/modules/__init__.py b/pype/modules/__init__.py index 874d69c41d..e0481d0c92 100644 --- a/pype/modules/__init__.py +++ b/pype/modules/__init__.py @@ -55,6 +55,7 @@ __all__ = ( "TrayModulesManager", "SettingsAction", + "LocalSettingsAction", "UserModule", "IUserModule", From 8112c6c9480c74485cc650a95d22df12c90e8919 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 23 Feb 2021 18:57:27 +0100 Subject: [PATCH 30/91] fix class name typo --- pype/settings/handlers.py | 2 +- pype/settings/lib.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pype/settings/handlers.py b/pype/settings/handlers.py index c76aa6c40a..f905759bc8 100644 --- a/pype/settings/handlers.py +++ b/pype/settings/handlers.py @@ -515,7 +515,7 @@ class MongoSettingsHandler(SettingsHandler): return self._get_project_anatomy_overrides(project_name) -class MongoLocalSettingshandler(LocalSettingsHandler): +class MongoLocalSettingsHandler(LocalSettingsHandler): """Settings handler that use mongo for store and load local settings.""" default_project_key = "__default_project__" diff --git a/pype/settings/lib.py b/pype/settings/lib.py index b07bd99e5d..8bf6d80bb3 100644 --- a/pype/settings/lib.py +++ b/pype/settings/lib.py @@ -65,8 +65,8 @@ def create_settings_handler(): def create_local_settings_handler(): - from .handlers import MongoLocalSettingshandler - return MongoLocalSettingshandler() + from .handlers import MongoLocalSettingsHandler + return MongoLocalSettingsHandler() @require_handler From 818a7c457e04d36c429546d36b8421e80d52ea73 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 23 Feb 2021 19:06:55 +0100 Subject: [PATCH 31/91] added some docstrings --- pype/settings/lib.py | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/pype/settings/lib.py b/pype/settings/lib.py index 8bf6d80bb3..24e16dd0ff 100644 --- a/pype/settings/lib.py +++ b/pype/settings/lib.py @@ -341,7 +341,8 @@ def apply_overrides(source_data, override_data): def apply_local_settings_on_system_settings(system_settings, local_settings): """Apply local settings on studio system settings. - In local settings are set application executables. + ATM local settings can modify only application executables. Executable + values are not overriden but prepended. """ if not local_settings or "applications" not in local_settings: return @@ -370,6 +371,26 @@ def apply_local_settings_on_system_settings(system_settings, local_settings): def apply_local_settings_on_anatomy_settings( anatomy_settings, local_settings, project_name ): + """Apply local settings on anatomy settings. + + ATM local settings can modify project roots. Project name is required as + local settings have data stored data by project's name. + + Local settings override root values in this order: + 1.) Check if local settings contain overrides for default project and + apply it's values on roots if there are any. + 2.) If passed `project_name` is not None then check project specific + overrides in local settings for the project and apply it's value on + roots if there are any. + + NOTE: Root values of default project from local settings are always applied + if are set. + + Args: + anatomy_settings (dict): Data for anatomy settings. + local_settings (dict): Data of local settings. + project_name (str): Name of project for which anatomy data are. + """ if not local_settings: return @@ -394,8 +415,8 @@ def apply_local_settings_on_anatomy_settings( if local_projects and "roots" in local_projects: for root_name, path in local_projects["roots"].items(): if root_name not in root_data: - print(root_name, "not in root data") continue + anatomy_settings["roots"][root_name][current_platform] = ( path ) From de0fdcd01281b1db29451aafd56153f34686c6ed Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 23 Feb 2021 19:12:03 +0100 Subject: [PATCH 32/91] added some docstring to handler --- pype/settings/handlers.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/pype/settings/handlers.py b/pype/settings/handlers.py index f905759bc8..64bb34e24e 100644 --- a/pype/settings/handlers.py +++ b/pype/settings/handlers.py @@ -516,7 +516,17 @@ class MongoSettingsHandler(SettingsHandler): class MongoLocalSettingsHandler(LocalSettingsHandler): - """Settings handler that use mongo for store and load local settings.""" + """Settings handler that use mongo for store and load local settings. + + Data have 2 query criteria. First is key "type" stored in constant + `LOCAL_SETTING_KEY`. Second is key "site_id" which value can be obstained + with `get_local_site_id` function. + + Because project specific values are stored by project name the default + project would have key `None` which is not allowed. Because of that is + `None` replaced with `default_project_key` value on save and the key + is replaced with `None` of load. + """ default_project_key = "__default_project__" def __init__(self, local_site_id=None): From 88ad9cea207d4b54aa892183debb26b2e96cdbf9 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Tue, 23 Feb 2021 19:15:34 +0100 Subject: [PATCH 33/91] add maya loader and creator settings --- .../defaults/project_settings/maya.json | 149 ++- .../projects_schema/schema_project_maya.json | 4 + .../schemas/schema_maya_capture.json | 1144 ++++++++--------- .../schemas/schema_maya_create.json | 86 ++ .../schemas/schema_maya_load.json | 26 + .../schemas/schema_maya_publish.json | 4 +- .../schemas/template_create_plugin.json | 22 + .../schemas/template_loader_plugin.json | 22 + ...ugin.json => template_publish_plugin.json} | 0 9 files changed, 875 insertions(+), 582 deletions(-) create mode 100644 pype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json create mode 100644 pype/settings/entities/schemas/projects_schema/schemas/template_create_plugin.json create mode 100644 pype/settings/entities/schemas/projects_schema/schemas/template_loader_plugin.json rename pype/settings/entities/schemas/projects_schema/schemas/{template_simple_plugin.json => template_publish_plugin.json} (100%) diff --git a/pype/settings/defaults/project_settings/maya.json b/pype/settings/defaults/project_settings/maya.json index affc6622df..03955732d2 100644 --- a/pype/settings/defaults/project_settings/maya.json +++ b/pype/settings/defaults/project_settings/maya.json @@ -107,7 +107,121 @@ "overscan": 1.0 } }, - "ext_mapping": {}, + "create": { + "CreateAnimation": { + "enabled": true, + "defaults": [ + "Main" + ] + }, + "CreateAss": { + "enabled": true, + "defaults": [ + "Main" + ] + }, + "CreateAssembly": { + "enabled": true, + "defaults": [ + "Main" + ] + }, + "CreateCamera": { + "enabled": true, + "defaults": [ + "Main" + ] + }, + "CreateLayout": { + "enabled": true, + "defaults": [ + "Main" + ] + }, + "CreateLook": { + "enabled": true, + "defaults": [ + "Main" + ] + }, + "CreateMayaScene": { + "enabled": true, + "defaults": [ + "Main" + ] + }, + "CreateModel": { + "enabled": true, + "defaults": [ + "Main", + "Proxy", + "Sculpt" + ] + }, + "CreatePointCache": { + "enabled": true, + "defaults": [ + "Main" + ] + }, + "CreateRender": { + "enabled": true, + "defaults": [ + "Main" + ] + }, + "CreateRenderSetup": { + "enabled": true, + "defaults": [ + "Main" + ] + }, + "CreateReview": { + "enabled": true, + "defaults": [ + "Main" + ] + }, + "CreateRig": { + "enabled": true, + "defaults": [ + "Main", + "Sim", + "Cloth" + ] + }, + "CreateSetDress": { + "enabled": true, + "defaults": [ + "Main", + "Anim" + ] + }, + "CreateUnrealStaticMesh": { + "enabled": true, + "defaults": [ + "Main" + ] + }, + "CreateVrayProxy": { + "enabled": true, + "defaults": [ + "Main" + ] + }, + "CreateVRayScene": { + "enabled": true, + "defaults": [ + "Main" + ] + }, + "CreateYetiRig": { + "enabled": true, + "defaults": [ + "Main" + ] + } + }, "publish": { "CollectMayaRender": { "sync_workfile_version": false @@ -319,6 +433,39 @@ 0.8, 0.5 ] + }, + "ReferenceLoader": { + "enabled": true, + "representations": [ + "ma", + "mb", + "abc", + "fbx" + ] + }, + "AudioLoader": { + "enabled": true, + "representations": [ + "wav" + ] + }, + "GpuCacheLoader": { + "enabled": true, + "representations": [ + "abc" + ] + }, + "ImagePlaneLoader": { + "enabled": true, + "representations": [ + "jpg", + "png", + "mov" + ] + }, + "MatchmoveLoader": { + "enabled": true, + "representations": [] } }, "workfile_build": { diff --git a/pype/settings/entities/schemas/projects_schema/schema_project_maya.json b/pype/settings/entities/schemas/projects_schema/schema_project_maya.json index 5ba5de1557..7a270b0046 100644 --- a/pype/settings/entities/schemas/projects_schema/schema_project_maya.json +++ b/pype/settings/entities/schemas/projects_schema/schema_project_maya.json @@ -9,6 +9,10 @@ "type": "schema", "name": "schema_maya_capture" }, + { + "type": "schema", + "name": "schema_maya_create" + }, { "type": "schema", "name": "schema_maya_publish" diff --git a/pype/settings/entities/schemas/projects_schema/schemas/schema_maya_capture.json b/pype/settings/entities/schemas/projects_schema/schemas/schema_maya_capture.json index 648fca28e6..4745a19075 100644 --- a/pype/settings/entities/schemas/projects_schema/schemas/schema_maya_capture.json +++ b/pype/settings/entities/schemas/projects_schema/schemas/schema_maya_capture.json @@ -1,595 +1,581 @@ { - "type": "collapsible-wrap", - "label": "Collapsible Wrapper without key", + "type": "dict", + "collapsible": true, + "key": "capture", + "label": "Maya Playblast settings", + "is_file": true, "children": [ { "type": "dict", - "collapsible": true, - "key": "capture", - "label": "Maya Playblast settings", - "is_file": true, + "key": "Codec", "children": [ { - "type": "dict", - "key": "Codec", - "children": [ - { - "type": "label", - "label": "Codec" - }, - { - "type": "text", - "key": "compression", - "label": "Compression type" - }, - { - "type": "text", - "key": "format", - "label": "Data format" - }, - { - "type": "number", - "key": "quality", - "label": "Quality", - "decimal": 0, - "minimum": 0, - "maximum": 100 - }, + "type": "label", + "label": "Codec" + }, + { + "type": "text", + "key": "compression", + "label": "Compression type" + }, + { + "type": "text", + "key": "format", + "label": "Data format" + }, + { + "type": "number", + "key": "quality", + "label": "Quality", + "decimal": 0, + "minimum": 0, + "maximum": 100 + }, - { - "type": "splitter" - } - ] - }, - { - "type": "dict", - "key": "Display Options", - "children": [ - { - "type": "label", - "label": "Display Options" - }, - { - "type": "list-strict", - "key": "background", - "label": "Background Color: ", - "object_types": [ - { - "label": "Red", - "type": "number", - "minimum": 0, - "maximum": 1, - "decimal": 3 - }, - { - "label": "Green", - "type": "number", - "minimum": 0, - "maximum": 1, - "decimal": 3 - }, - { - "label": "Blue", - "type": "number", - "minimum": 0, - "maximum": 1, - "decimal": 3 - } - ] - }, - { - "type": "list-strict", - "key": "backgroundBottom", - "label": "Background Bottom: ", - "object_types": [ - { - "label": "Red", - "type": "number", - "minimum": 0, - "maximum": 1, - "decimal": 3 - }, - { - "label": "Green", - "type": "number", - "minimum": 0, - "maximum": 1, - "decimal": 3 - }, - { - "label": "Blue", - "type": "number", - "minimum": 0, - "maximum": 1, - "decimal": 3 - } - ] - }, - { - "type": "list-strict", - "key": "backgroundTop", - "label": "Background Top: ", - "object_types": [ - { - "label": "Red", - "type": "number", - "minimum": 0, - "maximum": 1, - "decimal": 3 - }, - { - "label": "Green", - "type": "number", - "minimum": 0, - "maximum": 1, - "decimal": 3 - }, - { - "label": "Blue", - "type": "number", - "minimum": 0, - "maximum": 1, - "decimal": 3 - } - ] - }, - { - "type": "boolean", - "key": "override_display", - "label": "Override display options" - } - ] - }, { "type": "splitter" - }, - { - "type": "dict", - "key": "Generic", - "children": [ - { - "type": "label", - "label": "Generic" - }, - { - "type": "boolean", - "key": "isolate_view", - "label": " Isolate view" - }, - { - "type": "boolean", - "key": "off_screen", - "label": " Off Screen" - } - ] - }, - { - "type": "dict", - "key": "IO", - "children": [ - { - "type": "label", - "label": "IO" - }, - { - "type": "text", - "key": "name", - "label": "Name" - }, - { - "type": "boolean", - "key": "open_finished", - "label": "Open finished" - }, - { - "type": "boolean", - "key": "raw_frame_numbers", - "label": "Raw frame numbers" - }, - { - "type": "list", - "key": "recent_playblasts", - "label": "Recent Playblasts", - "object_type": "text" - }, - { - "type": "boolean", - "key": "save_file", - "label": "Save file" - } - ] - }, - { - "type": "dict", - "key": "PanZoom", - "children": [ - { - "type": "boolean", - "key": "pan_zoom", - "label": " Pan Zoom" - } - ] - }, - { - "type": "splitter" - }, - { - "type": "dict", - "key": "Renderer", - "children": [ - { - "type": "label", - "label": "Renderer" - }, - { - "type": "text", - "key": "rendererName", - "label": " Renderer name" - } - ] - }, - { - "type": "dict", - "key": "Resolution", - "children": [ - { - "type": "splitter" - }, - { - "type": "label", - "label": "Resolution" - }, - { - "type": "number", - "key": "width", - "label": " Width", - "decimal": 0, - "minimum": 0, - "maximum": 99999 - }, - { - "type": "number", - "key": "height", - "label": "Height", - "decimal": 0, - "minimum": 0, - "maximum": 99999 - }, - { - "type": "number", - "key": "percent", - "label": "percent", - "decimal": 1, - "minimum": 0, - "maximum": 200 - }, - { - "type": "text", - "key": "mode", - "label": "Mode" - } - ] - }, - { - "type": "splitter" - }, - { - "type": "dict", - "key": "Time Range", - "children": [ - { - "type": "label", - "label": "Time Range" - }, - { - "type": "number", - "key": "start_frame", - "label": " Start frame", - "decimal": 0, - "minimum": 0, - "maximum": 999999 - }, - { - "type": "number", - "key": "end_frame", - "label": "End frame", - "decimal": 0, - "minimum": 0, - "maximum": 999999 - }, - { - "type": "text", - "key": "frame", - "label": "Frame" - }, - { - "type": "text", - "key": "time", - "label": "Time" - } - ] - }, - { - "type": "dict", - "collapsible": true, - "key": "Viewport Options", - "label": "Viewport Options", - "children": [ - { - "type": "boolean", - "key": "cameras", - "label": "cameras" - }, - { - "type": "boolean", - "key": "clipGhosts", - "label": "clipGhosts" - }, - { - "type": "boolean", - "key": "controlVertices", - "label": "controlVertices" - }, - { - "type": "boolean", - "key": "deformers", - "label": "deformers" - }, - { - "type": "boolean", - "key": "dimensions", - "label": "dimensions" - }, - { - "type": "number", - "key": "displayLights", - "label": "displayLights", - "decimal": 0, - "minimum": 0, - "maximum": 10 - }, - { - "type": "boolean", - "key": "dynamicConstraints", - "label": "dynamicConstraints" - }, - { - "type": "boolean", - "key": "dynamics", - "label": "dynamics" - }, - { - "type": "boolean", - "key": "fluids", - "label": "fluids" - }, - { - "type": "boolean", - "key": "follicles", - "label": "follicles" - }, - { - "type": "boolean", - "key": "gpuCacheDisplayFilter", - "label": "gpuCacheDisplayFilter" - }, - { - "type": "boolean", - "key": "greasePencils", - "label": "greasePencils" - }, - { - "type": "boolean", - "key": "grid", - "label": "grid" - }, - { - "type": "boolean", - "key": "hairSystems", - "label": "hairSystems" - }, - { - "type": "boolean", - "key": "handles", - "label": "handles" - }, - { - "type": "boolean", - "key": "high_quality", - "label": "high_quality" - }, - { - "type": "boolean", - "key": "hud", - "label": "hud" - }, - { - "type": "boolean", - "key": "hulls", - "label": "hulls" - }, - { - "type": "boolean", - "key": "ikHandles", - "label": "ikHandles" - }, - { - "type": "boolean", - "key": "imagePlane", - "label": "imagePlane" - }, - { - "type": "boolean", - "key": "joints", - "label": "joints" - }, - { - "type": "boolean", - "key": "lights", - "label": "lights" - }, - { - "type": "boolean", - "key": "locators", - "label": "locators" - }, - { - "type": "boolean", - "key": "manipulators", - "label": "manipulators" - }, - { - "type": "boolean", - "key": "motionTrails", - "label": "motionTrails" - }, - { - "type": "boolean", - "key": "nCloths", - "label": "nCloths" - }, - { - "type": "boolean", - "key": "nParticles", - "label": "nParticles" - }, - { - "type": "boolean", - "key": "nRigids", - "label": "nRigids" - }, - { - "type": "boolean", - "key": "nurbsCurves", - "label": "nurbsCurves" - }, - { - "type": "boolean", - "key": "nurbsSurfaces", - "label": "nurbsSurfaces" - }, - { - "type": "boolean", - "key": "override_viewport_options", - "label": "override_viewport_options" - }, - { - "type": "boolean", - "key": "particleInstancers", - "label": "particleInstancers" - }, - { - "type": "boolean", - "key": "pivots", - "label": "pivots" - }, - { - "type": "boolean", - "key": "planes", - "label": "planes" - }, - { - "type": "boolean", - "key": "pluginShapes", - "label": "pluginShapes" - }, - { - "type": "boolean", - "key": "polymeshes", - "label": "polymeshes" - }, - { - "type": "boolean", - "key": "shadows", - "label": "shadows" - }, - { - "type": "boolean", - "key": "strokes", - "label": "strokes" - }, - { - "type": "boolean", - "key": "subdivSurfaces", - "label": "subdivSurfaces" - }, - { - "type": "boolean", - "key": "textures", - "label": "textures" - }, - { - "type": "boolean", - "key": "twoSidedLighting", - "label": "twoSidedLighting" - } - ] - }, - { - "type": "dict", - "collapsible": true, - "key": "Camera Options", - "label": "Camera Options", - "children": [ - { - "type": "boolean", - "key": "displayGateMask", - "label": "displayGateMask" - }, - { - "type": "boolean", - "key": "displayResolution", - "label": "displayResolution" - }, - { - "type": "boolean", - "key": "displayFilmGate", - "label": "displayFilmGate" - }, - { - "type": "boolean", - "key": "displayFieldChart", - "label": "displayFieldChart" - }, - { - "type": "boolean", - "key": "displaySafeAction", - "label": "displaySafeAction" - }, - { - "type": "boolean", - "key": "displaySafeTitle", - "label": "displaySafeTitle" - }, - { - "type": "boolean", - "key": "displayFilmPivot", - "label": "displayFilmPivot" - }, - { - "type": "boolean", - "key": "displayFilmOrigin", - "label": "displayFilmOrigin" - }, - { - "type": "number", - "key": "overscan", - "label": "overscan", - "decimal": 1, - "minimum": 0, - "maximum": 10 - } - ] } ] }, { - "type": "dict-modifiable", - "key": "ext_mapping", - "label": "Extension Mapping", - "object_type": { - "type": "text" - } + "type": "dict", + "key": "Display Options", + "children": [ + { + "type": "label", + "label": "Display Options" + }, + { + "type": "list-strict", + "key": "background", + "label": "Background Color: ", + "object_types": [ + { + "label": "Red", + "type": "number", + "minimum": 0, + "maximum": 1, + "decimal": 3 + }, + { + "label": "Green", + "type": "number", + "minimum": 0, + "maximum": 1, + "decimal": 3 + }, + { + "label": "Blue", + "type": "number", + "minimum": 0, + "maximum": 1, + "decimal": 3 + } + ] + }, + { + "type": "list-strict", + "key": "backgroundBottom", + "label": "Background Bottom: ", + "object_types": [ + { + "label": "Red", + "type": "number", + "minimum": 0, + "maximum": 1, + "decimal": 3 + }, + { + "label": "Green", + "type": "number", + "minimum": 0, + "maximum": 1, + "decimal": 3 + }, + { + "label": "Blue", + "type": "number", + "minimum": 0, + "maximum": 1, + "decimal": 3 + } + ] + }, + { + "type": "list-strict", + "key": "backgroundTop", + "label": "Background Top: ", + "object_types": [ + { + "label": "Red", + "type": "number", + "minimum": 0, + "maximum": 1, + "decimal": 3 + }, + { + "label": "Green", + "type": "number", + "minimum": 0, + "maximum": 1, + "decimal": 3 + }, + { + "label": "Blue", + "type": "number", + "minimum": 0, + "maximum": 1, + "decimal": 3 + } + ] + }, + { + "type": "boolean", + "key": "override_display", + "label": "Override display options" + } + ] + }, + { + "type": "splitter" + }, + { + "type": "dict", + "key": "Generic", + "children": [ + { + "type": "label", + "label": "Generic" + }, + { + "type": "boolean", + "key": "isolate_view", + "label": " Isolate view" + }, + { + "type": "boolean", + "key": "off_screen", + "label": " Off Screen" + } + ] + }, + { + "type": "dict", + "key": "IO", + "children": [ + { + "type": "label", + "label": "IO" + }, + { + "type": "text", + "key": "name", + "label": "Name" + }, + { + "type": "boolean", + "key": "open_finished", + "label": "Open finished" + }, + { + "type": "boolean", + "key": "raw_frame_numbers", + "label": "Raw frame numbers" + }, + { + "type": "list", + "key": "recent_playblasts", + "label": "Recent Playblasts", + "object_type": "text" + }, + { + "type": "boolean", + "key": "save_file", + "label": "Save file" + } + ] + }, + { + "type": "dict", + "key": "PanZoom", + "children": [ + { + "type": "boolean", + "key": "pan_zoom", + "label": " Pan Zoom" + } + ] + }, + { + "type": "splitter" + }, + { + "type": "dict", + "key": "Renderer", + "children": [ + { + "type": "label", + "label": "Renderer" + }, + { + "type": "text", + "key": "rendererName", + "label": " Renderer name" + } + ] + }, + { + "type": "dict", + "key": "Resolution", + "children": [ + { + "type": "splitter" + }, + { + "type": "label", + "label": "Resolution" + }, + { + "type": "number", + "key": "width", + "label": " Width", + "decimal": 0, + "minimum": 0, + "maximum": 99999 + }, + { + "type": "number", + "key": "height", + "label": "Height", + "decimal": 0, + "minimum": 0, + "maximum": 99999 + }, + { + "type": "number", + "key": "percent", + "label": "percent", + "decimal": 1, + "minimum": 0, + "maximum": 200 + }, + { + "type": "text", + "key": "mode", + "label": "Mode" + } + ] + }, + { + "type": "splitter" + }, + { + "type": "dict", + "key": "Time Range", + "children": [ + { + "type": "label", + "label": "Time Range" + }, + { + "type": "number", + "key": "start_frame", + "label": " Start frame", + "decimal": 0, + "minimum": 0, + "maximum": 999999 + }, + { + "type": "number", + "key": "end_frame", + "label": "End frame", + "decimal": 0, + "minimum": 0, + "maximum": 999999 + }, + { + "type": "text", + "key": "frame", + "label": "Frame" + }, + { + "type": "text", + "key": "time", + "label": "Time" + } + ] + }, + { + "type": "dict", + "collapsible": true, + "key": "Viewport Options", + "label": "Viewport Options", + "children": [ + { + "type": "boolean", + "key": "cameras", + "label": "cameras" + }, + { + "type": "boolean", + "key": "clipGhosts", + "label": "clipGhosts" + }, + { + "type": "boolean", + "key": "controlVertices", + "label": "controlVertices" + }, + { + "type": "boolean", + "key": "deformers", + "label": "deformers" + }, + { + "type": "boolean", + "key": "dimensions", + "label": "dimensions" + }, + { + "type": "number", + "key": "displayLights", + "label": "displayLights", + "decimal": 0, + "minimum": 0, + "maximum": 10 + }, + { + "type": "boolean", + "key": "dynamicConstraints", + "label": "dynamicConstraints" + }, + { + "type": "boolean", + "key": "dynamics", + "label": "dynamics" + }, + { + "type": "boolean", + "key": "fluids", + "label": "fluids" + }, + { + "type": "boolean", + "key": "follicles", + "label": "follicles" + }, + { + "type": "boolean", + "key": "gpuCacheDisplayFilter", + "label": "gpuCacheDisplayFilter" + }, + { + "type": "boolean", + "key": "greasePencils", + "label": "greasePencils" + }, + { + "type": "boolean", + "key": "grid", + "label": "grid" + }, + { + "type": "boolean", + "key": "hairSystems", + "label": "hairSystems" + }, + { + "type": "boolean", + "key": "handles", + "label": "handles" + }, + { + "type": "boolean", + "key": "high_quality", + "label": "high_quality" + }, + { + "type": "boolean", + "key": "hud", + "label": "hud" + }, + { + "type": "boolean", + "key": "hulls", + "label": "hulls" + }, + { + "type": "boolean", + "key": "ikHandles", + "label": "ikHandles" + }, + { + "type": "boolean", + "key": "imagePlane", + "label": "imagePlane" + }, + { + "type": "boolean", + "key": "joints", + "label": "joints" + }, + { + "type": "boolean", + "key": "lights", + "label": "lights" + }, + { + "type": "boolean", + "key": "locators", + "label": "locators" + }, + { + "type": "boolean", + "key": "manipulators", + "label": "manipulators" + }, + { + "type": "boolean", + "key": "motionTrails", + "label": "motionTrails" + }, + { + "type": "boolean", + "key": "nCloths", + "label": "nCloths" + }, + { + "type": "boolean", + "key": "nParticles", + "label": "nParticles" + }, + { + "type": "boolean", + "key": "nRigids", + "label": "nRigids" + }, + { + "type": "boolean", + "key": "nurbsCurves", + "label": "nurbsCurves" + }, + { + "type": "boolean", + "key": "nurbsSurfaces", + "label": "nurbsSurfaces" + }, + { + "type": "boolean", + "key": "override_viewport_options", + "label": "override_viewport_options" + }, + { + "type": "boolean", + "key": "particleInstancers", + "label": "particleInstancers" + }, + { + "type": "boolean", + "key": "pivots", + "label": "pivots" + }, + { + "type": "boolean", + "key": "planes", + "label": "planes" + }, + { + "type": "boolean", + "key": "pluginShapes", + "label": "pluginShapes" + }, + { + "type": "boolean", + "key": "polymeshes", + "label": "polymeshes" + }, + { + "type": "boolean", + "key": "shadows", + "label": "shadows" + }, + { + "type": "boolean", + "key": "strokes", + "label": "strokes" + }, + { + "type": "boolean", + "key": "subdivSurfaces", + "label": "subdivSurfaces" + }, + { + "type": "boolean", + "key": "textures", + "label": "textures" + }, + { + "type": "boolean", + "key": "twoSidedLighting", + "label": "twoSidedLighting" + } + ] + }, + { + "type": "dict", + "collapsible": true, + "key": "Camera Options", + "label": "Camera Options", + "children": [ + { + "type": "boolean", + "key": "displayGateMask", + "label": "displayGateMask" + }, + { + "type": "boolean", + "key": "displayResolution", + "label": "displayResolution" + }, + { + "type": "boolean", + "key": "displayFilmGate", + "label": "displayFilmGate" + }, + { + "type": "boolean", + "key": "displayFieldChart", + "label": "displayFieldChart" + }, + { + "type": "boolean", + "key": "displaySafeAction", + "label": "displaySafeAction" + }, + { + "type": "boolean", + "key": "displaySafeTitle", + "label": "displaySafeTitle" + }, + { + "type": "boolean", + "key": "displayFilmPivot", + "label": "displayFilmPivot" + }, + { + "type": "boolean", + "key": "displayFilmOrigin", + "label": "displayFilmOrigin" + }, + { + "type": "number", + "key": "overscan", + "label": "overscan", + "decimal": 1, + "minimum": 0, + "maximum": 10 + } + ] } ] } diff --git a/pype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json b/pype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json new file mode 100644 index 0000000000..575e04c85d --- /dev/null +++ b/pype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json @@ -0,0 +1,86 @@ +{ + "type": "dict", + "collapsible": true, + "key": "create", + "label": "Creator plugins", + "children": [ + { + "type": "schema_template", + "name": "template_create_plugin", + "template_data": [ + { + "key": "CreateAnimation", + "label": "Create Animation" + }, + { + "key": "CreateAss", + "label": "Create Ass" + }, + { + "key": "CreateAssembly", + "label": "Create Assembly" + }, + { + "key": "CreateCamera", + "label": "Create Camera" + }, + { + "key": "CreateLayout", + "label": "Create Layout" + }, + { + "key": "CreateLook", + "label": "Create Look" + }, + { + "key": "CreateMayaScene", + "label": "Create Maya Scene" + }, + { + "key": "CreateModel", + "label": "Create Model" + }, + { + "key": "CreatePointCache", + "label": "Create Cache" + }, + { + "key": "CreateRender", + "label": "Create Render" + }, + { + "key": "CreateRenderSetup", + "label": "Create Render Setup" + }, + { + "key": "CreateReview", + "label": "Create Review" + }, + { + "key": "CreateRig", + "label": "Create Rig" + }, + { + "key": "CreateSetDress", + "label": "Create Set Dress" + }, + { + "key": "CreateUnrealStaticMesh", + "label": "Create Unreal - Static Mesh" + }, + { + "key": "CreateVrayProxy", + "label": "Create VRay Proxy" + }, + { + "key": "CreateVRayScene", + "label": "Create VRay Scene" + }, + { + "key": "CreateYetiRig", + "label": "Create Yeti Rig" + } + ] + } + ] +} diff --git a/pype/settings/entities/schemas/projects_schema/schemas/schema_maya_load.json b/pype/settings/entities/schemas/projects_schema/schemas/schema_maya_load.json index dd9d0508b4..3615c1477c 100644 --- a/pype/settings/entities/schemas/projects_schema/schemas/schema_maya_load.json +++ b/pype/settings/entities/schemas/projects_schema/schemas/schema_maya_load.json @@ -151,6 +151,32 @@ ] } ] + }, + { + "type": "schema_template", + "name": "template_loader_plugin", + "template_data": [ + { + "key": "ReferenceLoader", + "label": "Reference Loader" + }, + { + "key": "AudioLoader", + "label": "Audio Loader" + }, + { + "key": "GpuCacheLoader", + "label": "GpuCache Loader" + }, + { + "key": "ImagePlaneLoader", + "label": "Imageplane Loader" + }, + { + "key": "MatchmoveLoader", + "label": "Matchmove Loader" + } + ] } ] } diff --git a/pype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json b/pype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json index 6aeba9b8b2..1d643b6273 100644 --- a/pype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json +++ b/pype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json @@ -133,7 +133,7 @@ }, { "type": "schema_template", - "name": "template_simple_plugin", + "name": "template_publish_plugin", "template_data": [ { "key": "ValidateColorSets", @@ -215,7 +215,7 @@ }, { "type": "schema_template", - "name": "template_simple_plugin", + "name": "template_publish_plugin", "template_data": [ { "key": "ValidateCameraAttributes", diff --git a/pype/settings/entities/schemas/projects_schema/schemas/template_create_plugin.json b/pype/settings/entities/schemas/projects_schema/schemas/template_create_plugin.json new file mode 100644 index 0000000000..14d15e7840 --- /dev/null +++ b/pype/settings/entities/schemas/projects_schema/schemas/template_create_plugin.json @@ -0,0 +1,22 @@ +[ + { + "type": "dict", + "collapsible": true, + "key": "{key}", + "label": "{label}", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "list", + "key": "defaults", + "label": "Default Subsets", + "object_type": "text" + } + ] + } +] diff --git a/pype/settings/entities/schemas/projects_schema/schemas/template_loader_plugin.json b/pype/settings/entities/schemas/projects_schema/schemas/template_loader_plugin.json new file mode 100644 index 0000000000..20dca6df17 --- /dev/null +++ b/pype/settings/entities/schemas/projects_schema/schemas/template_loader_plugin.json @@ -0,0 +1,22 @@ +[ + { + "type": "dict", + "collapsible": true, + "key": "{key}", + "label": "{label}", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "list", + "key": "representations", + "label": "Representations", + "object_type": "text" + } + ] + } +] diff --git a/pype/settings/entities/schemas/projects_schema/schemas/template_simple_plugin.json b/pype/settings/entities/schemas/projects_schema/schemas/template_publish_plugin.json similarity index 100% rename from pype/settings/entities/schemas/projects_schema/schemas/template_simple_plugin.json rename to pype/settings/entities/schemas/projects_schema/schemas/template_publish_plugin.json From 948f20f99b794411d3cfe732f78d071aa4acaa28 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 23 Feb 2021 19:18:00 +0100 Subject: [PATCH 34/91] added docstring to abstract local settings handler --- pype/settings/handlers.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pype/settings/handlers.py b/pype/settings/handlers.py index 64bb34e24e..135201048a 100644 --- a/pype/settings/handlers.py +++ b/pype/settings/handlers.py @@ -106,6 +106,11 @@ class SettingsHandler: @six.add_metaclass(ABCMeta) class LocalSettingsHandler: + """Handler that should handle about storing and loading of local settings. + + Local settings are "workstation" specific modifications that modify how + system and project settings look on the workstation and only there. + """ @abstractmethod def save_local_settings(self, data): """Save local data of local settings. From d01918d3c24082fd6c33a561b53fd0c4cc9162d4 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Wed, 24 Feb 2021 09:15:09 +0100 Subject: [PATCH 35/91] add nuke validators to settings --- .../nuke/plugins/publish/validate_knobs.py | 2 +- .../defaults/project_settings/nuke.json | 16 ++ .../projects_schema/schema_project_nuke.json | 141 +-------------- .../schemas/schema_nuke_plugins.json | 162 ++++++++++++++++++ 4 files changed, 182 insertions(+), 139 deletions(-) create mode 100644 pype/settings/entities/schemas/projects_schema/schemas/schema_nuke_plugins.json diff --git a/pype/hosts/nuke/plugins/publish/validate_knobs.py b/pype/hosts/nuke/plugins/publish/validate_knobs.py index ce15831c9c..cbc02690cb 100644 --- a/pype/hosts/nuke/plugins/publish/validate_knobs.py +++ b/pype/hosts/nuke/plugins/publish/validate_knobs.py @@ -9,7 +9,7 @@ class ValidateKnobs(pyblish.api.ContextPlugin): Knobs to validate and their values comes from the - Controled by plugin settings that require json in following structure: + Controlled by plugin settings that require json in following structure: "ValidateKnobs": { "enabled": true, "knobs": { diff --git a/pype/settings/defaults/project_settings/nuke.json b/pype/settings/defaults/project_settings/nuke.json index 94cd712639..517065f79a 100644 --- a/pype/settings/defaults/project_settings/nuke.json +++ b/pype/settings/defaults/project_settings/nuke.json @@ -61,6 +61,22 @@ "deadline_pool": "", "deadline_pool_secondary": "", "deadline_chunk_size": 1 + }, + "ValidateOutputResolution": { + "enabled": true, + "optional": true + }, + "ValidateGizmo": { + "enabled": true, + "optional": true + }, + "ValidateScript": { + "enabled": true, + "optional": true + }, + "ValidateNukeWriteBoundingBox": { + "enabled": true, + "optional": true } }, "workfile_build": { diff --git a/pype/settings/entities/schemas/projects_schema/schema_project_nuke.json b/pype/settings/entities/schemas/projects_schema/schema_project_nuke.json index b60b548cb0..2793157c4e 100644 --- a/pype/settings/entities/schemas/projects_schema/schema_project_nuke.json +++ b/pype/settings/entities/schemas/projects_schema/schema_project_nuke.json @@ -42,144 +42,9 @@ ] }, { - "type": "dict", - "collapsible": true, - "key": "publish", - "label": "Publish plugins", - "children": [ - { - "type": "dict", - "collapsible": true, - "key": "PreCollectNukeInstances", - "label": "PreCollectNukeInstances", - "is_group": true, - "children": [ - { - "type": "boolean", - "key": "sync_workfile_version", - "label": "Sync Version from workfile" - } - ] - }, - { - "type": "dict", - "collapsible": true, - "checkbox_key": "enabled", - "key": "ExtractThumbnail", - "label": "ExtractThumbnail", - "is_group": true, - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "raw-json", - "key": "nodes", - "label": "Nodes" - } - ] - }, - { - "type": "dict", - "collapsible": true, - "checkbox_key": "enabled", - "key": "ValidateKnobs", - "label": "ValidateKnobs", - "is_group": true, - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "raw-json", - "key": "knobs", - "label": "Knobs" - } - ] - }, - { - "type": "dict", - "collapsible": true, - "checkbox_key": "enabled", - "key": "ExtractReviewDataLut", - "label": "ExtractReviewDataLut", - "is_group": true, - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - } - ] - }, - { - "type": "dict", - "collapsible": true, - "checkbox_key": "enabled", - "key": "ExtractReviewDataMov", - "label": "ExtractReviewDataMov", - "is_group": true, - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "boolean", - "key": "viewer_lut_raw", - "label": "Viewer LUT raw" - } - ] - }, - { - "type": "dict", - "collapsible": true, - "key": "ExtractSlateFrame", - "label": "ExtractSlateFrame", - "is_group": true, - "children": [ - { - "type": "boolean", - "key": "viewer_lut_raw", - "label": "Viewer LUT raw" - } - ] - }, - { - "type": "dict", - "collapsible": true, - "key": "NukeSubmitDeadline", - "label": "NukeSubmitDeadline", - "is_group": true, - "children": [ - { - "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": "number", - "key": "deadline_chunk_size", - "label": "deadline_chunk_size" - } - ] - } - ] + "type": "schema", + "name": "schema_nuke_plugins", + "template_data": [] }, { "type": "schema", diff --git a/pype/settings/entities/schemas/projects_schema/schemas/schema_nuke_plugins.json b/pype/settings/entities/schemas/projects_schema/schemas/schema_nuke_plugins.json new file mode 100644 index 0000000000..aa767144e5 --- /dev/null +++ b/pype/settings/entities/schemas/projects_schema/schemas/schema_nuke_plugins.json @@ -0,0 +1,162 @@ +{ + "type": "dict", + "collapsible": true, + "key": "publish", + "label": "Publish plugins", + "children": [ + { + "type": "dict", + "collapsible": true, + "key": "PreCollectNukeInstances", + "label": "PreCollectNukeInstances", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "sync_workfile_version", + "label": "Sync Version from workfile" + } + ] + }, + { + "type": "dict", + "collapsible": true, + "checkbox_key": "enabled", + "key": "ExtractThumbnail", + "label": "ExtractThumbnail", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "raw-json", + "key": "nodes", + "label": "Nodes" + } + ] + }, + { + "type": "dict", + "collapsible": true, + "checkbox_key": "enabled", + "key": "ValidateKnobs", + "label": "ValidateKnobs", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "raw-json", + "key": "knobs", + "label": "Knobs" + } + ] + }, + { + "type": "dict", + "collapsible": true, + "checkbox_key": "enabled", + "key": "ExtractReviewDataLut", + "label": "ExtractReviewDataLut", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + } + ] + }, + { + "type": "dict", + "collapsible": true, + "checkbox_key": "enabled", + "key": "ExtractReviewDataMov", + "label": "ExtractReviewDataMov", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "viewer_lut_raw", + "label": "Viewer LUT raw" + } + ] + }, + { + "type": "dict", + "collapsible": true, + "key": "ExtractSlateFrame", + "label": "ExtractSlateFrame", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "viewer_lut_raw", + "label": "Viewer LUT raw" + } + ] + }, + { + "type": "dict", + "collapsible": true, + "key": "NukeSubmitDeadline", + "label": "NukeSubmitDeadline", + "is_group": true, + "children": [ + { + "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": "number", + "key": "deadline_chunk_size", + "label": "deadline_chunk_size" + } + ] + }, + { + "type": "schema_template", + "name": "template_publish_plugin", + "template_data": [ + { + "key": "ValidateOutputResolution", + "label": "Validate Output Resolution" + }, + { + "key": "ValidateGizmo", + "label": "Validate Gizmo (Group)" + }, + { + "key": "ValidateScript", + "label": "Validate script settings" + }, + { + "key": "ValidateNukeWriteBoundingBox", + "label": "Validate and Write Bounding Box" + } + ] + } + ] +} From 2c979b95bbf589bba278d2edb83e6b365ed04aa4 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Wed, 24 Feb 2021 10:03:15 +0100 Subject: [PATCH 36/91] add harmony plugin settings --- .../defaults/project_settings/harmony.json | 15 +- .../schema_project_harmony.json | 62 ++++++- .../schemas/schema_nuke_publish.json | 162 ++++++++++++++++++ 3 files changed, 231 insertions(+), 8 deletions(-) create mode 100644 pype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json diff --git a/pype/settings/defaults/project_settings/harmony.json b/pype/settings/defaults/project_settings/harmony.json index 83d63d3392..d69b05f534 100644 --- a/pype/settings/defaults/project_settings/harmony.json +++ b/pype/settings/defaults/project_settings/harmony.json @@ -1,7 +1,20 @@ { - "publish": {}, "general": { "skip_resolution_check": [], "skip_timelines_check": [] + }, + "publish": { + "CollectPalettes": { + "allowed_tasks": [ + "." + ] + }, + "HarmonySubmitDeadline": { + "use_published": false, + "priority": 50, + "primary_pool": "", + "secondary_pool": "", + "Chunk Size": 10000 + } } } \ No newline at end of file diff --git a/pype/settings/entities/schemas/projects_schema/schema_project_harmony.json b/pype/settings/entities/schemas/projects_schema/schema_project_harmony.json index 5d1cbff1b8..583eca9692 100644 --- a/pype/settings/entities/schemas/projects_schema/schema_project_harmony.json +++ b/pype/settings/entities/schemas/projects_schema/schema_project_harmony.json @@ -5,13 +5,6 @@ "label": "Harmony", "is_file": true, "children": [ - { - "type": "dict", - "collapsible": true, - "key": "publish", - "label": "Publish plugins", - "children": [] - }, { "type": "dict", "collapsible": true, @@ -31,6 +24,61 @@ "label": "Skip Timeliene Check for Tasks" } ] + }, + { + "type": "dict", + "collapsible": true, + "key": "publish", + "label": "Publish plugins", + "children": [ + { + "type": "dict", + "collapsible": true, + "key": "CollectPalettes", + "label": "Collect Palettes", + "children": [ + { + "type": "list", + "key": "allowed_tasks", + "label": "Allowed tasks", + "object_type": "text" + } + ] + }, + { + "type": "dict", + "collapsible": true, + "key": "HarmonySubmitDeadline", + "label": "Harmony Submit to Deadline", + "children": [ + { + "type": "boolean", + "key": "use_published", + "label": "Use Published scene" + }, + { + "type": "number", + "key": "priority", + "label": "priority" + }, + { + "type": "text", + "key": "primary_pool", + "label": "Primary Pool" + }, + { + "type": "text", + "key": "secondary_pool", + "label": "Secondary Pool" + }, + { + "type": "number", + "key": "Chunk Size", + "label": "Chunk Size" + } + ] + } + ] } ] } diff --git a/pype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json b/pype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json new file mode 100644 index 0000000000..aa767144e5 --- /dev/null +++ b/pype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json @@ -0,0 +1,162 @@ +{ + "type": "dict", + "collapsible": true, + "key": "publish", + "label": "Publish plugins", + "children": [ + { + "type": "dict", + "collapsible": true, + "key": "PreCollectNukeInstances", + "label": "PreCollectNukeInstances", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "sync_workfile_version", + "label": "Sync Version from workfile" + } + ] + }, + { + "type": "dict", + "collapsible": true, + "checkbox_key": "enabled", + "key": "ExtractThumbnail", + "label": "ExtractThumbnail", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "raw-json", + "key": "nodes", + "label": "Nodes" + } + ] + }, + { + "type": "dict", + "collapsible": true, + "checkbox_key": "enabled", + "key": "ValidateKnobs", + "label": "ValidateKnobs", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "raw-json", + "key": "knobs", + "label": "Knobs" + } + ] + }, + { + "type": "dict", + "collapsible": true, + "checkbox_key": "enabled", + "key": "ExtractReviewDataLut", + "label": "ExtractReviewDataLut", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + } + ] + }, + { + "type": "dict", + "collapsible": true, + "checkbox_key": "enabled", + "key": "ExtractReviewDataMov", + "label": "ExtractReviewDataMov", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "viewer_lut_raw", + "label": "Viewer LUT raw" + } + ] + }, + { + "type": "dict", + "collapsible": true, + "key": "ExtractSlateFrame", + "label": "ExtractSlateFrame", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "viewer_lut_raw", + "label": "Viewer LUT raw" + } + ] + }, + { + "type": "dict", + "collapsible": true, + "key": "NukeSubmitDeadline", + "label": "NukeSubmitDeadline", + "is_group": true, + "children": [ + { + "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": "number", + "key": "deadline_chunk_size", + "label": "deadline_chunk_size" + } + ] + }, + { + "type": "schema_template", + "name": "template_publish_plugin", + "template_data": [ + { + "key": "ValidateOutputResolution", + "label": "Validate Output Resolution" + }, + { + "key": "ValidateGizmo", + "label": "Validate Gizmo (Group)" + }, + { + "key": "ValidateScript", + "label": "Validate script settings" + }, + { + "key": "ValidateNukeWriteBoundingBox", + "label": "Validate and Write Bounding Box" + } + ] + } + ] +} From 1d91bca162780c7bf99ee23899fe71bb37c5db3e Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Wed, 24 Feb 2021 10:44:27 +0100 Subject: [PATCH 37/91] add deadline plugins to settings --- .../plugins/publish/submit_nuke_deadline.py | 24 +- .../defaults/project_settings/deadline.json | 44 ++++ .../defaults/project_settings/harmony.json | 2 +- .../schemas/projects_schema/schema_main.json | 4 + .../projects_schema/schema_plugins.json | 54 ----- .../schema_project_deadline.json | 223 ++++++++++++++++++ .../schema_project_harmony.json | 2 +- .../schemas/schema_nuke_publish.json | 29 --- 8 files changed, 285 insertions(+), 97 deletions(-) create mode 100644 pype/settings/defaults/project_settings/deadline.json delete mode 100644 pype/settings/entities/schemas/projects_schema/schema_plugins.json create mode 100644 pype/settings/entities/schemas/projects_schema/schema_project_deadline.json diff --git a/pype/modules/deadline/plugins/publish/submit_nuke_deadline.py b/pype/modules/deadline/plugins/publish/submit_nuke_deadline.py index b36e1fdbba..f352a9b5c0 100644 --- a/pype/modules/deadline/plugins/publish/submit_nuke_deadline.py +++ b/pype/modules/deadline/plugins/publish/submit_nuke_deadline.py @@ -23,12 +23,12 @@ class NukeSubmitDeadline(pyblish.api.InstancePlugin): optional = True # presets - deadline_priority = 50 + priority = 50 deadline_chunk_size = 1 - deadline_pool = "" - deadline_pool_secondary = "" - deadline_group = "" - deadline_department = "" + primary_pool = "" + secondary_pool = "" + group = "" + department = "" def process(self, instance): instance.data["toBeRenderedOn"] = "deadline" @@ -142,12 +142,12 @@ class NukeSubmitDeadline(pyblish.api.InstancePlugin): # define chunk and priority chunk_size = instance.data.get("deadlineChunkSize") - if chunk_size == 0 and self.deadline_chunk_size: - chunk_size = self.deadline_chunk_size + if chunk_size == 0 and self.chunk_size: + chunk_size = self.chunk_size priority = instance.data.get("deadlinePriority") if not priority: - priority = self.deadline_priority + priority = self.priority payload = { "JobInfo": { @@ -165,11 +165,11 @@ class NukeSubmitDeadline(pyblish.api.InstancePlugin): "Priority": priority, "ChunkSize": chunk_size, - "Department": self.deadline_department, + "Department": self.department, - "Pool": self.deadline_pool, - "SecondaryPool": self.deadline_pool_secondary, - "Group": self.deadline_group, + "Pool": self.primary_pool, + "SecondaryPool": self.secondary_pool, + "Group": self.group, "Plugin": "Nuke", "Frames": "{start}-{end}".format( diff --git a/pype/settings/defaults/project_settings/deadline.json b/pype/settings/defaults/project_settings/deadline.json new file mode 100644 index 0000000000..6844979ddb --- /dev/null +++ b/pype/settings/defaults/project_settings/deadline.json @@ -0,0 +1,44 @@ +{ + "publish": { + "MayaSubmitDeadline": { + "enabled": true, + "optional": false, + "tile_assembler_plugin": "oiio", + "use_published": true, + "asset_dependencies": true + }, + "NukeSubmitDeadline": { + "enabled": true, + "optional": false, + "use_published": true, + "priority": 50, + "Chunk Size": 10, + "primary_pool": "", + "secondary_pool": "", + "group": "", + "department": "" + }, + "HarmonySubmitDeadline": { + "enabled": true, + "optional": false, + "use_published": true, + "priority": 50, + "Chunk Size": 10000, + "primary_pool": "", + "secondary_pool": "", + "group": "", + "department": "" + }, + "AfterEffectsSubmitDeadline": { + "enabled": true, + "optional": false, + "use_published": true, + "priority": 50, + "Chunk Size": 10000, + "primary_pool": "", + "secondary_pool": "", + "group": "", + "department": "" + } + } +} \ No newline at end of file diff --git a/pype/settings/defaults/project_settings/harmony.json b/pype/settings/defaults/project_settings/harmony.json index d69b05f534..e1de4485a4 100644 --- a/pype/settings/defaults/project_settings/harmony.json +++ b/pype/settings/defaults/project_settings/harmony.json @@ -14,7 +14,7 @@ "priority": 50, "primary_pool": "", "secondary_pool": "", - "Chunk Size": 10000 + "chunk_size": 0 } } } \ No newline at end of file diff --git a/pype/settings/entities/schemas/projects_schema/schema_main.json b/pype/settings/entities/schemas/projects_schema/schema_main.json index 8ad059f1c7..31d7373873 100644 --- a/pype/settings/entities/schemas/projects_schema/schema_main.json +++ b/pype/settings/entities/schemas/projects_schema/schema_main.json @@ -45,6 +45,10 @@ "type": "schema", "name": "schema_project_ftrack" }, + { + "type": "schema", + "name": "schema_project_deadline" + }, { "type": "schema", "name": "schema_project_maya" diff --git a/pype/settings/entities/schemas/projects_schema/schema_plugins.json b/pype/settings/entities/schemas/projects_schema/schema_plugins.json deleted file mode 100644 index a7ed64be17..0000000000 --- a/pype/settings/entities/schemas/projects_schema/schema_plugins.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "type": "dict", - "collapsible": true, - "key": "plugins", - "label": "Plugins", - "children": [ - { - "type": "dict", - "collapsible": true, - "key": "standalonepublisher", - "label": "Standalone Publisher", - "children": [ - { - "type": "dict", - "collapsible": true, - "key": "publish", - "label": "Publish plugins", - "is_file": true, - "children": [ - { - "type": "dict", - "collapsible": true, - "key": "ExtractThumbnailSP", - "label": "ExtractThumbnailSP", - "is_group": true, - "children": [ - { - "type": "dict", - "collapsible": 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" - } - ] - } - ] - } - ] - } - ] - } - ] -} diff --git a/pype/settings/entities/schemas/projects_schema/schema_project_deadline.json b/pype/settings/entities/schemas/projects_schema/schema_project_deadline.json new file mode 100644 index 0000000000..ea76f4e62e --- /dev/null +++ b/pype/settings/entities/schemas/projects_schema/schema_project_deadline.json @@ -0,0 +1,223 @@ +{ + "type": "dict", + "key": "deadline", + "label": "Deadline", + "collapsible": true, + "is_file": true, + "children": [ + { + "type": "dict", + "collapsible": true, + "key": "publish", + "label": "Publish plugins", + "is_file": true, + "children": [ + { + "type": "dict", + "collapsible": true, + "key": "MayaSubmitDeadline", + "label": "Submit maya job to deadline", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "optional", + "label": "Optional" + }, + { + "type": "enum", + "key": "tile_assembler_plugin", + "label": "Tile Assembler Plugin", + "multiselection": false, + "enum_items": [ + { + "DraftTileAssembler": "Draft Tile Assembler" + }, + { + "oiio": "Open Image IO" + } + ] + }, + { + "type": "boolean", + "key": "use_published", + "label": "Use Published scene" + }, + { + "type": "boolean", + "key": "asset_dependencies", + "label": "Use Asset dependencies" + } + ] + }, + { + "type": "dict", + "collapsible": true, + "key": "NukeSubmitDeadline", + "label": "Nuke Submit to Deadline", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "optional", + "label": "Optional" + }, + { + "type": "boolean", + "key": "use_published", + "label": "Use Published scene" + }, + { + "type": "number", + "key": "priority", + "label": "Priority" + }, + { + "type": "number", + "key": "Chunk Size", + "label": "Chunk Size" + }, + { + "type": "text", + "key": "primary_pool", + "label": "Primary Pool" + }, + { + "type": "text", + "key": "secondary_pool", + "label": "Secondary Pool" + }, + { + "type": "text", + "key": "group", + "label": "Group" + }, + { + "type": "text", + "key": "department", + "label": "Department" + } + ] + }, + { + "type": "dict", + "collapsible": true, + "key": "HarmonySubmitDeadline", + "label": "Harmony Submit to Deadline", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "optional", + "label": "Optional" + }, + { + "type": "boolean", + "key": "use_published", + "label": "Use Published scene" + }, + { + "type": "number", + "key": "priority", + "label": "Priority" + }, + { + "type": "number", + "key": "Chunk Size", + "label": "Chunk Size" + }, + { + "type": "text", + "key": "primary_pool", + "label": "Primary Pool" + }, + { + "type": "text", + "key": "secondary_pool", + "label": "Secondary Pool" + }, + { + "type": "text", + "key": "group", + "label": "Group" + }, + { + "type": "text", + "key": "department", + "label": "Department" + } + ] + }, + { + "type": "dict", + "collapsible": true, + "key": "AfterEffectsSubmitDeadline", + "label": "After Effects Submit to Deadline", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "optional", + "label": "Optional" + }, + { + "type": "boolean", + "key": "use_published", + "label": "Use Published scene" + }, + { + "type": "number", + "key": "priority", + "label": "Priority" + }, + { + "type": "number", + "key": "Chunk Size", + "label": "Chunk Size" + }, + { + "type": "text", + "key": "primary_pool", + "label": "Primary Pool" + }, + { + "type": "text", + "key": "secondary_pool", + "label": "Secondary Pool" + }, + { + "type": "text", + "key": "group", + "label": "Group" + }, + { + "type": "text", + "key": "department", + "label": "Department" + } + ] + } + ] + } + ] +} diff --git a/pype/settings/entities/schemas/projects_schema/schema_project_harmony.json b/pype/settings/entities/schemas/projects_schema/schema_project_harmony.json index 583eca9692..c4cdccff42 100644 --- a/pype/settings/entities/schemas/projects_schema/schema_project_harmony.json +++ b/pype/settings/entities/schemas/projects_schema/schema_project_harmony.json @@ -73,7 +73,7 @@ }, { "type": "number", - "key": "Chunk Size", + "key": "chunk_size", "label": "Chunk Size" } ] diff --git a/pype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json b/pype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json index aa767144e5..e9b3e1c36e 100644 --- a/pype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json +++ b/pype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json @@ -107,35 +107,6 @@ } ] }, - { - "type": "dict", - "collapsible": true, - "key": "NukeSubmitDeadline", - "label": "NukeSubmitDeadline", - "is_group": true, - "children": [ - { - "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": "number", - "key": "deadline_chunk_size", - "label": "deadline_chunk_size" - } - ] - }, { "type": "schema_template", "name": "template_publish_plugin", From eff4dbf0772c822fcf70f2624ee4faf0e7667ff0 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 24 Feb 2021 11:23:42 +0100 Subject: [PATCH 38/91] add wrapper to layout on add of first child in wrapper --- pype/tools/settings/settings/widgets/item_widgets.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pype/tools/settings/settings/widgets/item_widgets.py b/pype/tools/settings/settings/widgets/item_widgets.py index fd33f337d7..718724f64e 100644 --- a/pype/tools/settings/settings/widgets/item_widgets.py +++ b/pype/tools/settings/settings/widgets/item_widgets.py @@ -39,6 +39,7 @@ class DictImmutableKeysWidget(BaseWidget): self.widget_mapping = {} self.wrapper_widgets_by_id = {} + self._added_wrapper_ids = set() self._prepare_entity_layouts( self.entity.gui_layout, self.content_widget ) @@ -72,7 +73,6 @@ class DictImmutableKeysWidget(BaseWidget): self.widget_mapping[wrapper.id] = widget self.wrapper_widgets_by_id[wrapper.id] = wrapper - self.add_widget_to_layout(wrapper) self._prepare_entity_layouts(child["children"], wrapper) def _ui_item_without_label(self): @@ -151,6 +151,9 @@ class DictImmutableKeysWidget(BaseWidget): wrapper = self.widget_mapping[map_id] if wrapper is not self.content_widget: wrapper.add_widget_to_layout(widget, label) + if wrapper.id not in self._added_wrapper_ids: + self.add_widget_to_layout(wrapper) + self._added_wrapper_ids.add(wrapper.id) return row = self.content_layout.rowCount() From 3ab0ac66a8f2bc2fc871f1e941a0cb85d4869094 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 24 Feb 2021 11:24:00 +0100 Subject: [PATCH 39/91] removed unused variable --- pype/tools/settings/settings/widgets/item_widgets.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_widgets.py b/pype/tools/settings/settings/widgets/item_widgets.py index 718724f64e..58d3c1cd57 100644 --- a/pype/tools/settings/settings/widgets/item_widgets.py +++ b/pype/tools/settings/settings/widgets/item_widgets.py @@ -38,7 +38,6 @@ class DictImmutableKeysWidget(BaseWidget): ) self.widget_mapping = {} - self.wrapper_widgets_by_id = {} self._added_wrapper_ids = set() self._prepare_entity_layouts( self.entity.gui_layout, self.content_widget @@ -72,7 +71,6 @@ class DictImmutableKeysWidget(BaseWidget): ) self.widget_mapping[wrapper.id] = widget - self.wrapper_widgets_by_id[wrapper.id] = wrapper self._prepare_entity_layouts(child["children"], wrapper) def _ui_item_without_label(self): From bcde1896c91fbae16a6e3ee8c45c65bc6337f8df Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 24 Feb 2021 11:24:14 +0100 Subject: [PATCH 40/91] renamed `widget_mapping` to `_parent_widget_by_entity_id` --- pype/tools/settings/settings/widgets/item_widgets.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_widgets.py b/pype/tools/settings/settings/widgets/item_widgets.py index 58d3c1cd57..e0c97e0c80 100644 --- a/pype/tools/settings/settings/widgets/item_widgets.py +++ b/pype/tools/settings/settings/widgets/item_widgets.py @@ -37,7 +37,7 @@ class DictImmutableKeysWidget(BaseWidget): self.entity.checkbox_key ) - self.widget_mapping = {} + self._parent_widget_by_entity_id = {} self._added_wrapper_ids = set() self._prepare_entity_layouts( self.entity.gui_layout, self.content_widget @@ -56,7 +56,7 @@ class DictImmutableKeysWidget(BaseWidget): for child in children: if not isinstance(child, dict): if child is not self.checkbox_child: - self.widget_mapping[child.id] = widget + self._parent_widget_by_entity_id[child.id] = widget continue if child["type"] == "collapsible-wrap": @@ -70,7 +70,8 @@ class DictImmutableKeysWidget(BaseWidget): "Unknown Wrapper type \"{}\"".format(child["type"]) ) - self.widget_mapping[wrapper.id] = widget + self._parent_widget_by_entity_id[wrapper.id] = widget + self._prepare_entity_layouts(child["children"], wrapper) def _ui_item_without_label(self): @@ -146,7 +147,7 @@ class DictImmutableKeysWidget(BaseWidget): else: map_id = widget.entity.id - wrapper = self.widget_mapping[map_id] + wrapper = self._parent_widget_by_entity_id[map_id] if wrapper is not self.content_widget: wrapper.add_widget_to_layout(widget, label) if wrapper.id not in self._added_wrapper_ids: From af7063e17324eff7f7bbab3ca3106a2fa44c9fe9 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 24 Feb 2021 11:30:10 +0100 Subject: [PATCH 41/91] added attributes use_label_wrap, collapsible, collapsed to every entity --- pype/settings/entities/base_entity.py | 35 +++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/pype/settings/entities/base_entity.py b/pype/settings/entities/base_entity.py index b2be6819d5..0a38f92063 100644 --- a/pype/settings/entities/base_entity.py +++ b/pype/settings/entities/base_entity.py @@ -695,6 +695,12 @@ class ItemEntity(BaseItemEntity): is_dynamic_item (bool): Entity should behave like dynamically created entity. """ + _default_label_wrap = { + "use_label_wrap": False, + "collapsible": True, + "collapsed": False + } + def __init__(self, schema_data, parent, is_dynamic_item=False): super(ItemEntity, self).__init__(schema_data) @@ -736,6 +742,35 @@ class ItemEntity(BaseItemEntity): self.key = self.schema_data.get("key") self.label = self.schema_data.get("label") + # GUI attributes + _default_label_wrap = self.__class__._default_label_wrap + for key, value in ItemEntity._default_label_wrap.items(): + if key not in _default_label_wrap: + self.log.warning( + "Class {} miss default label wrap key \"{}\"".format( + self.__class__.__name__, key + ) + ) + _default_label_wrap[key] = value + + use_label_wrap = self.schema_data.get("use_label_wrap") + if use_label_wrap is None: + if not self.label: + use_label_wrap = False + else: + use_label_wrap = _default_label_wrap["use_label_wrap"] + self.use_label_wrap = use_label_wrap + + # Used only if `use_label_wrap` is set to True + self.collapsible = self.schema_data.get( + "collapsible", + _default_label_wrap["collapsible"] + ) + self.collapsed = self.schema_data.get( + "collapsed", + _default_label_wrap["collapsed"] + ) + self._item_initalization() def save(self): From 2d9cd37e2c20ebeea4558380041177643c40c2b4 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 24 Feb 2021 11:31:00 +0100 Subject: [PATCH 42/91] entities already using label wrap set their defaults --- pype/settings/entities/dict_immutable_keys_entity.py | 10 +++++----- pype/settings/entities/dict_mutable_keys_entity.py | 8 ++++++-- pype/settings/entities/list_entity.py | 11 +++++------ 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/pype/settings/entities/dict_immutable_keys_entity.py b/pype/settings/entities/dict_immutable_keys_entity.py index 854c904f3c..f6cd629d10 100644 --- a/pype/settings/entities/dict_immutable_keys_entity.py +++ b/pype/settings/entities/dict_immutable_keys_entity.py @@ -27,6 +27,11 @@ class DictImmutableKeysEntity(ItemEntity): are not real settings values but entities representing the value. """ schema_types = ["dict"] + _default_label_wrap = { + "use_label_wrap": True, + "collapsible": True, + "collapsed": True + } def __getitem__(self, key): """Return entity inder key.""" @@ -169,11 +174,6 @@ class DictImmutableKeysEntity(ItemEntity): "highlight_content", False ) self.show_borders = self.schema_data.get("show_borders", True) - self.collapsible = self.schema_data.get("collapsible", True) - self.collapsed = self.schema_data.get("collapsed", True) - - # Not yet implemented - self.use_label_wrap = self.schema_data.get("use_label_wrap") or True def get_child_path(self, child_obj): """Get hierarchical path of child entity. diff --git a/pype/settings/entities/dict_mutable_keys_entity.py b/pype/settings/entities/dict_mutable_keys_entity.py index 8fe71db2a3..2fd2b87311 100644 --- a/pype/settings/entities/dict_mutable_keys_entity.py +++ b/pype/settings/entities/dict_mutable_keys_entity.py @@ -29,6 +29,12 @@ class DictMutableKeysEntity(EndpointEntity): - clear callbacks """ schema_types = ["dict-modifiable"] + _default_label_wrap = { + "use_label_wrap": True, + "collapsible": True, + "collapsed": True + } + _miss_arg = object() def __getitem__(self, key): @@ -174,8 +180,6 @@ class DictMutableKeysEntity(EndpointEntity): self.hightlight_content = ( self.schema_data.get("highlight_content") or False ) - self.collapsible = self.schema_data.get("collapsible", True) - self.collapsed = self.schema_data.get("collapsed", True) object_type = self.schema_data["object_type"] if not isinstance(object_type, dict): diff --git a/pype/settings/entities/list_entity.py b/pype/settings/entities/list_entity.py index 07221929b7..752347489a 100644 --- a/pype/settings/entities/list_entity.py +++ b/pype/settings/entities/list_entity.py @@ -15,6 +15,11 @@ from .exceptions import ( class ListEntity(EndpointEntity): schema_types = ["list"] + _default_label_wrap = { + "use_label_wrap": False, + "collapsible": True, + "collapsed": False + } def __iter__(self): for item in self.children: @@ -139,12 +144,6 @@ class ListEntity(EndpointEntity): # Value that was set on set_override_state self.initial_value = [] - # GUI attributes - self.use_label_wrap = self.schema_data.get("use_label_wrap") or False - # Used only if `use_label_wrap` is set to True - self.collapsible = self.schema_data.get("collapsible") or True - self.collapsed = self.schema_data.get("collapsed") or False - def schema_validations(self): super(ListEntity, self).schema_validations() From 1e0333495ab99ab0f21eb846602bfb70e12bf847 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 24 Feb 2021 11:31:14 +0100 Subject: [PATCH 43/91] added schema validation for label wrappers --- pype/settings/entities/base_entity.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pype/settings/entities/base_entity.py b/pype/settings/entities/base_entity.py index 0a38f92063..9003a66d76 100644 --- a/pype/settings/entities/base_entity.py +++ b/pype/settings/entities/base_entity.py @@ -777,6 +777,15 @@ class ItemEntity(BaseItemEntity): """Call save on root item.""" self.root_item.save() + def schema_validations(self): + if not self.label and self.use_label_wrap: + raise ValueError(( + "{} Entity has set `use_label_wrap` to true but" + " does not have set `label`." + ).format(self.path)) + + super(ItemEntity, self).schema_validations() + def create_schema_object(self, *args, **kwargs): """Reference method for creation of entities defined in RootEntity.""" return self.root_item.create_schema_object(*args, **kwargs) From 7c3ca96448761ba1c5d15489101b4cbbb01aa0d6 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 24 Feb 2021 11:32:17 +0100 Subject: [PATCH 44/91] slightly modified how ui in immutable dict works --- .../settings/settings/widgets/item_widgets.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_widgets.py b/pype/tools/settings/settings/widgets/item_widgets.py index fd33f337d7..76b8a9b27e 100644 --- a/pype/tools/settings/settings/widgets/item_widgets.py +++ b/pype/tools/settings/settings/widgets/item_widgets.py @@ -27,15 +27,19 @@ class DictImmutableKeysWidget(BaseWidget): self._child_style_state = "" self.input_fields = [] self.checkbox_child = None - if not self.entity.is_dynamic_item and not self.entity.label: + label = None + if self.entity.is_dynamic_item: + self._ui_item_or_as_widget() + + elif not self.entity.use_label_wrap: self._ui_item_without_label() + label = self.entity.label else: self._ui_item_or_as_widget() - if not self.entity.is_dynamic_item: - self.checkbox_child = self.entity.non_gui_children.get( - self.entity.checkbox_key - ) + self.checkbox_child = self.entity.non_gui_children.get( + self.entity.checkbox_key + ) self.widget_mapping = {} self.wrapper_widgets_by_id = {} @@ -50,7 +54,7 @@ class DictImmutableKeysWidget(BaseWidget): ) ) - self.entity_widget.add_widget_to_layout(self) + self.entity_widget.add_widget_to_layout(self, label) def _prepare_entity_layouts(self, children, widget): for child in children: From 75365b1f98c1639bed01847965d09afe9d9bc603 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 24 Feb 2021 11:41:21 +0100 Subject: [PATCH 45/91] immutable key dict is more specific about which method is used to create ui --- .../settings/settings/widgets/item_widgets.py | 99 ++++++++++--------- 1 file changed, 55 insertions(+), 44 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_widgets.py b/pype/tools/settings/settings/widgets/item_widgets.py index 76b8a9b27e..dfcf1a272f 100644 --- a/pype/tools/settings/settings/widgets/item_widgets.py +++ b/pype/tools/settings/settings/widgets/item_widgets.py @@ -27,20 +27,26 @@ class DictImmutableKeysWidget(BaseWidget): self._child_style_state = "" self.input_fields = [] self.checkbox_child = None + + self.label_widget = None + self.body_widget = None + self.content_widget = None + self.content_layout = None + label = None if self.entity.is_dynamic_item: - self._ui_item_or_as_widget() + self._ui_as_dynamic_item() - elif not self.entity.use_label_wrap: - self._ui_item_without_label() - label = self.entity.label - - else: - self._ui_item_or_as_widget() + elif self.entity.use_label_wrap: + self._ui_label_wrap() self.checkbox_child = self.entity.non_gui_children.get( self.entity.checkbox_key ) + else: + self._ui_item_base() + label = self.entity.label + self.widget_mapping = {} self.wrapper_widgets_by_id = {} self._prepare_entity_layouts( @@ -79,68 +85,73 @@ class DictImmutableKeysWidget(BaseWidget): self.add_widget_to_layout(wrapper) self._prepare_entity_layouts(child["children"], wrapper) - def _ui_item_without_label(self): + def _ui_item_base(self): self.setObjectName("DictInvisible") - self.body_widget = None self.content_widget = self self.content_layout = QtWidgets.QGridLayout(self) self.content_layout.setContentsMargins(0, 0, 0, 0) self.content_layout.setSpacing(5) - def _ui_item_or_as_widget(self): + def _ui_as_dynamic_item(self): content_widget = QtWidgets.QWidget(self) + content_widget.setObjectName("DictAsWidgetBody") - if self.entity.is_dynamic_item: - content_widget.setObjectName("DictAsWidgetBody") - show_borders = str(int(self.entity.show_borders)) - content_widget.setProperty("show_borders", show_borders) - content_layout_margins = (5, 5, 5, 5) - main_layout_spacing = 5 - body_widget = None - label_widget = QtWidgets.QLabel(self.entity.label) + show_borders = str(int(self.entity.show_borders)) + content_widget.setProperty("show_borders", show_borders) + label_widget = QtWidgets.QLabel(self.entity.label) + + content_layout = QtWidgets.QGridLayout(content_widget) + content_layout.setContentsMargins(5, 5, 5, 5) + + main_layout = QtWidgets.QHBoxLayout(self) + main_layout.setContentsMargins(0, 0, 0, 0) + main_layout.setSpacing(5) + main_layout.addWidget(content_widget) + + self.label_widget = label_widget + self.content_widget = content_widget + self.content_layout = content_layout + + def _ui_label_wrap(self): + content_widget = QtWidgets.QWidget(self) + content_widget.setObjectName("ContentWidget") + + if self.entity.highlight_content: + content_state = "hightlighted" + bottom_margin = 5 else: - content_widget.setObjectName("ContentWidget") - if self.entity.highlight_content: - content_state = "hightlighted" - bottom_margin = 5 - else: - content_state = "" - bottom_margin = 0 - content_widget.setProperty("content_state", content_state) - content_layout_margins = (CHILD_OFFSET, 5, 0, bottom_margin) - main_layout_spacing = 0 + content_state = "" + bottom_margin = 0 + content_widget.setProperty("content_state", content_state) + content_layout_margins = (CHILD_OFFSET, 5, 0, bottom_margin) - body_widget = ExpandingWidget(self.entity.label, self) - label_widget = body_widget.label_widget - body_widget.set_content_widget(content_widget) + body_widget = ExpandingWidget(self.entity.label, self) + label_widget = body_widget.label_widget + body_widget.set_content_widget(content_widget) content_layout = QtWidgets.QGridLayout(content_widget) content_layout.setContentsMargins(*content_layout_margins) main_layout = QtWidgets.QHBoxLayout(self) main_layout.setContentsMargins(0, 0, 0, 0) - main_layout.setSpacing(main_layout_spacing) - if not body_widget: - main_layout.addWidget(content_widget) - else: - main_layout.addWidget(body_widget) + main_layout.setSpacing(0) + main_layout.addWidget(body_widget) self.label_widget = label_widget self.body_widget = body_widget self.content_widget = content_widget self.content_layout = content_layout - if body_widget: - if len(self.input_fields) == 1 and self.checkbox_widget: - body_widget.hide_toolbox(hide_content=True) + if len(self.input_fields) == 1 and self.checkbox_widget: + body_widget.hide_toolbox(hide_content=True) - elif self.entity.collapsible: - if not self.entity.collapsed: - body_widget.toggle_content() - else: - body_widget.hide_toolbox(hide_content=False) + elif self.entity.collapsible: + if not self.entity.collapsed: + body_widget.toggle_content() + else: + body_widget.hide_toolbox(hide_content=False) def add_widget_to_layout(self, widget, label=None): if self.checkbox_child and widget.entity is self.checkbox_child: From 189114feeba012ea594e6073bf1bbc84285d1548 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Wed, 24 Feb 2021 12:12:36 +0100 Subject: [PATCH 46/91] small schema cleanups --- .../schema_project_global.json | 10 +- .../projects_schema/schema_project_nuke.json | 4 +- .../schema_project_standalonepublisher.json | 76 ++--- .../schemas/schema_maya_publish.json | 320 +++++++++--------- .../schemas/schema_nuke_plugins.json | 162 --------- .../schemas/schema_nuke_publish.json | 105 ++++-- 6 files changed, 286 insertions(+), 391 deletions(-) delete mode 100644 pype/settings/entities/schemas/projects_schema/schemas/schema_nuke_plugins.json diff --git a/pype/settings/entities/schemas/projects_schema/schema_project_global.json b/pype/settings/entities/schemas/projects_schema/schema_project_global.json index fa5db9af88..e6a1a6ff96 100644 --- a/pype/settings/entities/schemas/projects_schema/schema_project_global.json +++ b/pype/settings/entities/schemas/projects_schema/schema_project_global.json @@ -14,9 +14,15 @@ "name": "schema_global_tools" }, { - "type": "raw-json", + "type": "collapsible-wrap", "label": "Project Folder Structure", - "key": "project_folder_structure" + "children": [ + { + "type": "raw-json", + "label": " ", + "key": "project_folder_structure" + } + ] }, { "type": "schema", diff --git a/pype/settings/entities/schemas/projects_schema/schema_project_nuke.json b/pype/settings/entities/schemas/projects_schema/schema_project_nuke.json index 2793157c4e..0548bd3544 100644 --- a/pype/settings/entities/schemas/projects_schema/schema_project_nuke.json +++ b/pype/settings/entities/schemas/projects_schema/schema_project_nuke.json @@ -9,7 +9,7 @@ "type": "dict", "collapsible": true, "key": "create", - "label": "Create plugins", + "label": "Creator plugins", "children": [ { "type": "dict", @@ -43,7 +43,7 @@ }, { "type": "schema", - "name": "schema_nuke_plugins", + "name": "schema_nuke_publish", "template_data": [] }, { diff --git a/pype/settings/entities/schemas/projects_schema/schema_project_standalonepublisher.json b/pype/settings/entities/schemas/projects_schema/schema_project_standalonepublisher.json index 735b9611d2..47eea3441c 100644 --- a/pype/settings/entities/schemas/projects_schema/schema_project_standalonepublisher.json +++ b/pype/settings/entities/schemas/projects_schema/schema_project_standalonepublisher.json @@ -5,44 +5,6 @@ "label": "Standalone Publisher", "is_file": true, "children": [ - { - "type": "dict", - "collapsible": true, - "key": "publish", - "label": "Publish plugins", - "is_file": true, - "children": [ - { - "type": "dict", - "collapsible": true, - "key": "ExtractThumbnailSP", - "label": "ExtractThumbnailSP", - "is_group": true, - "children": [ - { - "type": "dict", - "collapsible": 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-modifiable", "collapsible": true, @@ -88,6 +50,44 @@ } ] } + }, + { + "type": "dict", + "collapsible": true, + "key": "publish", + "label": "Publish plugins", + "is_file": true, + "children": [ + { + "type": "dict", + "collapsible": true, + "key": "ExtractThumbnailSP", + "label": "ExtractThumbnailSP", + "is_group": true, + "children": [ + { + "type": "dict", + "collapsible": 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" + } + ] + } + ] + } + ] } ] } diff --git a/pype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json b/pype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json index 1d643b6273..58a21c185a 100644 --- a/pype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json +++ b/pype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json @@ -21,16 +21,66 @@ } ] }, + { + "type": "splitter" + }, + { + "type": "label", + "label": "Validators" + }, + { + "type": "dict", + "collapsible": true, + "key": "ValidateShaderName", + "label": "ValidateShaderName", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "label", + "label": "Shader name regex can use named capture group asset to validate against current asset name.

Example:
^.*(?P=<asset>.+)_SHD

" + }, + { + "type": "text", + "key": "regex", + "label": "Validation regex" + } + ] + }, + + { + "type": "dict", + "collapsible": true, + "key": "ValidateAttributes", + "label": "ValidateAttributes", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "raw-json", + "key": "attributes", + "label": "Attributes" + } + ] + }, { "type": "collapsible-wrap", - "label": "Validators", + "label": "Model", "children": [ { "type": "dict", "collapsible": true, - "key": "ValidateShaderName", - "label": "ValidateShaderName", + "key": "ValidateModelName", + "label": "Validate Model Name", "checkbox_key": "enabled", "children": [ { @@ -40,7 +90,14 @@ }, { "type": "label", - "label": "Shader name regex can use named capture group asset to validate against current asset name.

Example:
^.*(?P=<asset>.+)_SHD

" + "label": "Path to material file defining list of material names to check. This is material name per line simple text file.
It will be checked against named group shader in your Validation regex.

For example:
^.*(?P=<shader>.+)_GEO

" + }, + { + "type": "path-widget", + "key": "material_file", + "label": "Material File", + "multiplatform": true, + "multipath": false }, { "type": "text", @@ -49,12 +106,11 @@ } ] }, - { "type": "dict", "collapsible": true, - "key": "ValidateAttributes", - "label": "ValidateAttributes", + "key": "ValidateTransformNamingSuffix", + "label": "ValidateTransformNamingSuffix", "checkbox_key": "enabled", "children": [ { @@ -62,154 +118,19 @@ "key": "enabled", "label": "Enabled" }, + { + "type": "label", + "label": "Validates transform suffix based on the type of its children shapes." + }, { "type": "raw-json", - "key": "attributes", - "label": "Attributes" - } - ] - }, - { - "type": "collapsible-wrap", - "label": "Model", - "children": [ - { - "type": "dict", - "collapsible": true, - "key": "ValidateModelName", - "label": "Validate Model Name", - "checkbox_key": "enabled", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "label", - "label": "Path to material file defining list of material names to check. This is material name per line simple text file.
It will be checked against named group shader in your Validation regex.

For example:
^.*(?P=<shader>.+)_GEO

" - }, - { - "type": "path-widget", - "key": "material_file", - "label": "Material File", - "multiplatform": true, - "multipath": false - }, - { - "type": "text", - "key": "regex", - "label": "Validation regex" - } - ] + "key": "SUFFIX_NAMING_TABLE", + "label": "Suffix Naming Table" }, { - "type": "dict", - "collapsible": true, - "key": "ValidateTransformNamingSuffix", - "label": "ValidateTransformNamingSuffix", - "checkbox_key": "enabled", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "label", - "label": "Validates transform suffix based on the type of its children shapes." - }, - { - "type": "raw-json", - "key": "SUFFIX_NAMING_TABLE", - "label": "Suffix Naming Table" - }, - { - "type": "boolean", - "key": "ALLOW_IF_NOT_IN_SUFFIX_TABLE", - "label": "Allow if suffix not in table" - } - ] - }, - { - "type": "schema_template", - "name": "template_publish_plugin", - "template_data": [ - { - "key": "ValidateColorSets", - "label": "ValidateColorSets" - }, - { - "key": "ValidateMeshHasOverlappingUVs", - "label": "ValidateMeshHasOverlappingUVs" - }, - { - "key": "ValidateMeshArnoldAttributes", - "label": "ValidateMeshArnoldAttributes" - }, - { - "key": "ValidateMeshShaderConnections", - "label": "ValidateMeshShaderConnections" - }, - { - "key": "ValidateMeshSingleUVSet", - "label": "ValidateMeshSingleUVSet" - }, - { - "key": "ValidateMeshHasUVs", - "label": "ValidateMeshHasUVs" - }, - { - "key": "ValidateMeshLaminaFaces", - "label": "ValidateMeshLaminaFaces" - }, - { - "key": "ValidateMeshNonManifold", - "label": "ValidateMeshNonManifold" - }, - { - "key": "ValidateMeshUVSetMap1", - "label": "ValidateMeshUVSetMap1", - "docstring": "Validate model's default uv set exists and is named 'map1'.

In Maya meshes by default have a uv set named 'map1' that cannot be deleted. It can be renamed, however,
introducing some issues with some renderers. As such we ensure the first (default) UV set index is named 'map1'." - }, - { - "key": "ValidateMeshVerticesHaveEdges", - "label": "ValidateMeshVerticesHaveEdges" - }, - { - "key": "ValidateNoAnimation", - "label": "ValidateNoAnimation", - "docstring": "Ensure no keyframes on nodes in the Instance.
Even though a Model would extract without animCurves correctly this avoids getting different
output from a model when extracted from a different frame than the first frame. (Might be overly restrictive though)." - }, - { - "key": "ValidateNoNamespace", - "label": "ValidateNoNamespace" - }, - { - "key": "ValidateNoNullTransforms", - "label": "ValidateNoNullTransforms" - }, - { - "key": "ValidateNoUnknownNodes", - "label": "ValidateNoUnknownNodes" - }, - { - "key": "ValidateNodeNoGhosting", - "label": "ValidateNodeNoGhosting" - }, - { - "key": "ValidateShapeDefaultNames", - "label": "ValidateShapeDefaultNames" - }, - { - "key": "ValidateShapeRenderStats", - "label": "ValidateShapeRenderStats" - }, - { - "key": "ValidateTransformZero", - "label": "ValidateTransformZero" - } - ] + "type": "boolean", + "key": "ALLOW_IF_NOT_IN_SUFFIX_TABLE", + "label": "Allow if suffix not in table" } ] }, @@ -218,22 +139,105 @@ "name": "template_publish_plugin", "template_data": [ { - "key": "ValidateCameraAttributes", - "label": "Validate Camera Attributes", - "docstring": "" + "key": "ValidateColorSets", + "label": "ValidateColorSets" }, { - "key": "ValidateAssemblyName", - "label": "Validate Assembly Name" + "key": "ValidateMeshHasOverlappingUVs", + "label": "ValidateMeshHasOverlappingUVs" }, { - "key": "ValidateAssRelativePaths", - "label": "ValidateAssRelativePaths" + "key": "ValidateMeshArnoldAttributes", + "label": "ValidateMeshArnoldAttributes" + }, + { + "key": "ValidateMeshShaderConnections", + "label": "ValidateMeshShaderConnections" + }, + { + "key": "ValidateMeshSingleUVSet", + "label": "ValidateMeshSingleUVSet" + }, + { + "key": "ValidateMeshHasUVs", + "label": "ValidateMeshHasUVs" + }, + { + "key": "ValidateMeshLaminaFaces", + "label": "ValidateMeshLaminaFaces" + }, + { + "key": "ValidateMeshNonManifold", + "label": "ValidateMeshNonManifold" + }, + { + "key": "ValidateMeshUVSetMap1", + "label": "ValidateMeshUVSetMap1", + "docstring": "Validate model's default uv set exists and is named 'map1'.

In Maya meshes by default have a uv set named 'map1' that cannot be deleted. It can be renamed, however,
introducing some issues with some renderers. As such we ensure the first (default) UV set index is named 'map1'." + }, + { + "key": "ValidateMeshVerticesHaveEdges", + "label": "ValidateMeshVerticesHaveEdges" + }, + { + "key": "ValidateNoAnimation", + "label": "ValidateNoAnimation", + "docstring": "Ensure no keyframes on nodes in the Instance.
Even though a Model would extract without animCurves correctly this avoids getting different
output from a model when extracted from a different frame than the first frame. (Might be overly restrictive though)." + }, + { + "key": "ValidateNoNamespace", + "label": "ValidateNoNamespace" + }, + { + "key": "ValidateNoNullTransforms", + "label": "ValidateNoNullTransforms" + }, + { + "key": "ValidateNoUnknownNodes", + "label": "ValidateNoUnknownNodes" + }, + { + "key": "ValidateNodeNoGhosting", + "label": "ValidateNodeNoGhosting" + }, + { + "key": "ValidateShapeDefaultNames", + "label": "ValidateShapeDefaultNames" + }, + { + "key": "ValidateShapeRenderStats", + "label": "ValidateShapeRenderStats" + }, + { + "key": "ValidateTransformZero", + "label": "ValidateTransformZero" } ] } ] }, + { + "type": "schema_template", + "name": "template_publish_plugin", + "template_data": [ + { + "key": "ValidateCameraAttributes", + "label": "Validate Camera Attributes", + "docstring": "" + }, + { + "key": "ValidateAssemblyName", + "label": "Validate Assembly Name" + }, + { + "key": "ValidateAssRelativePaths", + "label": "ValidateAssRelativePaths" + } + ] + }, + { + "type": "splitter" + }, { "type": "label", "label": "Extractors" diff --git a/pype/settings/entities/schemas/projects_schema/schemas/schema_nuke_plugins.json b/pype/settings/entities/schemas/projects_schema/schemas/schema_nuke_plugins.json deleted file mode 100644 index aa767144e5..0000000000 --- a/pype/settings/entities/schemas/projects_schema/schemas/schema_nuke_plugins.json +++ /dev/null @@ -1,162 +0,0 @@ -{ - "type": "dict", - "collapsible": true, - "key": "publish", - "label": "Publish plugins", - "children": [ - { - "type": "dict", - "collapsible": true, - "key": "PreCollectNukeInstances", - "label": "PreCollectNukeInstances", - "is_group": true, - "children": [ - { - "type": "boolean", - "key": "sync_workfile_version", - "label": "Sync Version from workfile" - } - ] - }, - { - "type": "dict", - "collapsible": true, - "checkbox_key": "enabled", - "key": "ExtractThumbnail", - "label": "ExtractThumbnail", - "is_group": true, - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "raw-json", - "key": "nodes", - "label": "Nodes" - } - ] - }, - { - "type": "dict", - "collapsible": true, - "checkbox_key": "enabled", - "key": "ValidateKnobs", - "label": "ValidateKnobs", - "is_group": true, - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "raw-json", - "key": "knobs", - "label": "Knobs" - } - ] - }, - { - "type": "dict", - "collapsible": true, - "checkbox_key": "enabled", - "key": "ExtractReviewDataLut", - "label": "ExtractReviewDataLut", - "is_group": true, - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - } - ] - }, - { - "type": "dict", - "collapsible": true, - "checkbox_key": "enabled", - "key": "ExtractReviewDataMov", - "label": "ExtractReviewDataMov", - "is_group": true, - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "boolean", - "key": "viewer_lut_raw", - "label": "Viewer LUT raw" - } - ] - }, - { - "type": "dict", - "collapsible": true, - "key": "ExtractSlateFrame", - "label": "ExtractSlateFrame", - "is_group": true, - "children": [ - { - "type": "boolean", - "key": "viewer_lut_raw", - "label": "Viewer LUT raw" - } - ] - }, - { - "type": "dict", - "collapsible": true, - "key": "NukeSubmitDeadline", - "label": "NukeSubmitDeadline", - "is_group": true, - "children": [ - { - "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": "number", - "key": "deadline_chunk_size", - "label": "deadline_chunk_size" - } - ] - }, - { - "type": "schema_template", - "name": "template_publish_plugin", - "template_data": [ - { - "key": "ValidateOutputResolution", - "label": "Validate Output Resolution" - }, - { - "key": "ValidateGizmo", - "label": "Validate Gizmo (Group)" - }, - { - "key": "ValidateScript", - "label": "Validate script settings" - }, - { - "key": "ValidateNukeWriteBoundingBox", - "label": "Validate and Write Bounding Box" - } - ] - } - ] -} diff --git a/pype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json b/pype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json index e9b3e1c36e..0e3770ac78 100644 --- a/pype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json +++ b/pype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json @@ -4,6 +4,10 @@ "key": "publish", "label": "Publish plugins", "children": [ + { + "type": "label", + "label": "Collectors" + }, { "type": "dict", "collapsible": true, @@ -17,26 +21,13 @@ "label": "Sync Version from workfile" } ] + }, + { + "type": "splitter" }, { - "type": "dict", - "collapsible": true, - "checkbox_key": "enabled", - "key": "ExtractThumbnail", - "label": "ExtractThumbnail", - "is_group": true, - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "raw-json", - "key": "nodes", - "label": "Nodes" - } - ] + "type": "label", + "label": "Validators" }, { "type": "dict", @@ -58,6 +49,55 @@ } ] }, + { + "type": "schema_template", + "name": "template_publish_plugin", + "template_data": [ + { + "key": "ValidateOutputResolution", + "label": "Validate Output Resolution" + }, + { + "key": "ValidateGizmo", + "label": "Validate Gizmo (Group)" + }, + { + "key": "ValidateScript", + "label": "Validate script settings" + }, + { + "key": "ValidateNukeWriteBoundingBox", + "label": "Validate and Write Bounding Box" + } + ] + }, + { + "type": "splitter" + }, + { + "type": "label", + "label": "Extractors" + }, + { + "type": "dict", + "collapsible": true, + "checkbox_key": "enabled", + "key": "ExtractThumbnail", + "label": "ExtractThumbnail", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "raw-json", + "key": "nodes", + "label": "Nodes" + } + ] + }, { "type": "dict", "collapsible": true, @@ -108,24 +148,31 @@ ] }, { - "type": "schema_template", - "name": "template_publish_plugin", - "template_data": [ + "type": "dict", + "collapsible": true, + "key": "NukeSubmitDeadline", + "label": "NukeSubmitDeadline", + "is_group": true, + "children": [ { - "key": "ValidateOutputResolution", - "label": "Validate Output Resolution" + "type": "number", + "key": "deadline_priority", + "label": "deadline_priority" }, { - "key": "ValidateGizmo", - "label": "Validate Gizmo (Group)" + "type": "text", + "key": "deadline_pool", + "label": "deadline_pool" }, { - "key": "ValidateScript", - "label": "Validate script settings" + "type": "text", + "key": "deadline_pool_secondary", + "label": "deadline_pool_secondary" }, { - "key": "ValidateNukeWriteBoundingBox", - "label": "Validate and Write Bounding Box" + "type": "number", + "key": "deadline_chunk_size", + "label": "deadline_chunk_size" } ] } From 98ba3225aa101330c8f2e9ae58eee4f24ad13766 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Wed, 24 Feb 2021 12:14:25 +0100 Subject: [PATCH 47/91] fix wrong chunk_size attribute --- pype/modules/deadline/plugins/publish/submit_nuke_deadline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/modules/deadline/plugins/publish/submit_nuke_deadline.py b/pype/modules/deadline/plugins/publish/submit_nuke_deadline.py index f352a9b5c0..af8694dd22 100644 --- a/pype/modules/deadline/plugins/publish/submit_nuke_deadline.py +++ b/pype/modules/deadline/plugins/publish/submit_nuke_deadline.py @@ -24,7 +24,7 @@ class NukeSubmitDeadline(pyblish.api.InstancePlugin): # presets priority = 50 - deadline_chunk_size = 1 + chunk_size = 1 primary_pool = "" secondary_pool = "" group = "" From 71e8bcc253e6e301966fe1fcf40c426e9702d246 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Wed, 24 Feb 2021 12:26:39 +0100 Subject: [PATCH 48/91] pype 3 implementation of #1053 --- pype/hosts/maya/api/attributes.py | 334 ++++++++++++++++++ .../publish/collect_renderable_camera.py | 4 +- 2 files changed, 336 insertions(+), 2 deletions(-) create mode 100644 pype/hosts/maya/api/attributes.py diff --git a/pype/hosts/maya/api/attributes.py b/pype/hosts/maya/api/attributes.py new file mode 100644 index 0000000000..a98548301a --- /dev/null +++ b/pype/hosts/maya/api/attributes.py @@ -0,0 +1,334 @@ +# -*- coding: utf-8 -*- +"""Code to get attributes from render layer without switching to it. + +https://github.com/Colorbleed/colorbleed-config/blob/acre/colorbleed/maya/lib_rendersetup.py +Credits: Roy Nieterau (BigRoy) / Colorbleed +Modified for use in Pype + +""" + +from maya import cmds +import maya.api.OpenMaya as om +import pymel.core as pm + +import maya.app.renderSetup.model.utils as utils +from maya.app.renderSetup.model import renderSetup +from maya.app.renderSetup.model.override import ( + AbsOverride, + RelOverride, + UniqueOverride +) + +EXACT_MATCH = 0 +PARENT_MATCH = 1 +CLIENT_MATCH = 2 + +DEFAULT_RENDER_LAYER = "defaultRenderLayer" + + +def get_rendersetup_layer(layer): + """Return render setup layer name. + + This also converts names from legacy renderLayer node name to render setup + name. + + Note: `DEFAULT_RENDER_LAYER` is not a renderSetupLayer node but it is + however the valid layer name for Render Setup - so we return that as + is. + + Example: + >>> for legacy_layer in cmds.ls(type="renderLayer"): + >>> layer = get_rendersetup_layer(legacy_layer) + + Returns: + str or None: Returns renderSetupLayer node name if `layer` is a valid + layer name in legacy renderlayers or render setup layers. + Returns None if the layer can't be found or Render Setup is + currently disabled. + """ + if layer == DEFAULT_RENDER_LAYER: + # DEFAULT_RENDER_LAYER doesn't have a `renderSetupLayer` + return layer + + if not cmds.mayaHasRenderSetup(): + return None + + if not cmds.objExists(layer): + return None + + if cmds.nodeType(layer) == "renderSetupLayer": + return layer + + # By default Render Setup renames the legacy renderlayer + # to `rs_` but lets not rely on that as the + # layer node can be renamed manually + connections = cmds.listConnections(layer + ".message", + type="renderSetupLayer", + exactType=True, + source=False, + destination=True, + plugs=True) or [] + return next((conn.split(".", 1)[0] for conn in connections + if conn.endswith(".legacyRenderLayer")), None) + + +def get_attr_in_layer(node_attr, layer): + """Return attribute value in Render Setup layer. + + This will only work for attributes which can be + retrieved with `maya.cmds.getAttr` and for which + Relative and Absolute overrides are applicable. + + Examples: + >>> get_attr_in_layer("defaultResolution.width", layer="layer1") + >>> get_attr_in_layer("defaultRenderGlobals.startFrame", layer="layer") + >>> get_attr_in_layer("transform.translate", layer="layer3") + + Args: + attr (str): attribute name as 'node.attribute' + layer (str): layer name + + Returns: + object: attribute value in layer + + """ + def _layer_needs_update(layer): + """Return whether layer needs updating.""" + # Use `getattr` as e.g. DEFAULT_RENDER_LAYER does not have + # the attribute + return getattr(layer, "needsMembershipUpdate", False) or \ + getattr(layer, "needsApplyUpdate", False) + + def get_default_layer_value(node_attr_): + """Return attribute value in `DEFAULT_RENDER_LAYER`.""" + inputs = cmds.listConnections(node_attr_, + source=True, + destination=False, + type="applyOverride") or [] + if inputs: + override = inputs[0] + history_overrides = cmds.ls(cmds.listHistory(override, + pruneDagObjects=True), + type="applyOverride") + node = history_overrides[-1] if history_overrides else override + node_attr_ = node + ".original" + + return pm.getAttr(node_attr_, asString=True) + + layer = get_rendersetup_layer(layer) + rs = renderSetup.instance() + current_layer = rs.getVisibleRenderLayer() + if current_layer.name() == layer: + + # Ensure layer is up-to-date + if _layer_needs_update(current_layer): + try: + rs.switchToLayer(current_layer) + except RuntimeError: + # Some cases can cause errors on switching + # the first time with Render Setup layers + # e.g. different overrides to compounds + # and its children plugs. So we just force + # it another time. If it then still fails + # we will let it error out. + rs.switchToLayer(current_layer) + + return pm.getAttr(node_attr, asString=True) + + overrides = get_attr_overrides(node_attr, layer) + default_layer_value = get_default_layer_value(node_attr) + if not overrides: + return default_layer_value + + value = default_layer_value + for match, layer_override, index in overrides: + if isinstance(layer_override, AbsOverride): + # Absolute override + value = pm.getAttr(layer_override.name() + ".attrValue") + if match == EXACT_MATCH: + # value = value + pass + if match == PARENT_MATCH: + value = value[index] + if match == CLIENT_MATCH: + value[index] = value + + elif isinstance(layer_override, RelOverride): + # Relative override + # Value = Original * Multiply + Offset + multiply = pm.getAttr(layer_override.name() + ".multiply") + offset = pm.getAttr(layer_override.name() + ".offset") + + if match == EXACT_MATCH: + value = value * multiply + offset + if match == PARENT_MATCH: + value = value * multiply[index] + offset[index] + if match == CLIENT_MATCH: + value[index] = value[index] * multiply + offset + + else: + raise TypeError("Unsupported override: %s" % layer_override) + + return value + + +def get_attr_overrides(node_attr, layer, + skip_disabled=True, + skip_local_render=True, + stop_at_absolute_override=True): + """Return all Overrides applicable to the attribute. + + Overrides are returned as a 3-tuple: + (Match, Override, Index) + Match: + This is any of EXACT_MATCH, PARENT_MATCH, CLIENT_MATCH + and defines whether the override is exactly on the + plug, on the parent or on a child plug. + Override: + This is the RenderSetup Override instance. + Index: + This is the Plug index under the parent or for + the child that matches. The EXACT_MATCH index will + always be None. For PARENT_MATCH the index is which + index the plug is under the parent plug. For CLIENT_MATCH + the index is which child index matches the plug. + + Args: + node_attr (str): attribute name as 'node.attribute' + layer (str): layer name + skip_disabled (bool): exclude disabled overrides + skip_local_render (bool): exclude overrides marked + as local render. + stop_at_absolute_override: exclude overrides prior + to the last absolute override as they have + no influence on the resulting value. + + Returns: + list: Ordered Overrides in order of strength + + """ + def get_mplug_children(plug): + """Return children MPlugs of compound `MPlug`.""" + children = [] + if plug.isCompound: + for i in range(plug.numChildren()): + children.append(plug.child(i)) + return children + + def get_mplug_names(mplug): + """Return long and short name of `MPlug`.""" + long_name = mplug.partialName(useLongNames=True) + short_name = mplug.partialName(useLongNames=False) + return {long_name, short_name} + + def iter_override_targets(override): + try: + for target in override._targets(): + yield target + except AssertionError: + # Workaround: There is a bug where the private `_targets()` method + # fails on some attribute plugs. For example overrides + # to the defaultRenderGlobals.endFrame + # (Tested in Maya 2020.2) + print("Workaround for %s" % override) + from maya.app.renderSetup.common.utils import findPlug + + attr = override.attributeName() + if isinstance(override, UniqueOverride): + node = override.targetNodeName() + yield findPlug(node, attr) + else: + nodes = override.parent().selector().nodes() + for node in nodes: + if cmds.attributeQuery(attr, node=node, exists=True): + yield findPlug(node, attr) + + # Get the MPlug for the node.attr + sel = om.MSelectionList() + sel.add(node_attr) + plug = sel.getPlug(0) + + layer = get_rendersetup_layer(layer) + if layer == DEFAULT_RENDER_LAYER: + # DEFAULT_RENDER_LAYER will never have overrides + # since it's the default layer + return [] + + rs_layer = renderSetup.instance().getRenderLayer(layer) + if rs_layer is None: + # Renderlayer does not exist + return + + # Get any parent or children plugs as we also + # want to include them in the attribute match + # for overrides + parent = plug.parent() if plug.isChild else None + parent_index = None + if parent: + parent_index = get_mplug_children(parent).index(plug) + + children = get_mplug_children(plug) + + # Create lookup for the attribute by both long + # and short names + attr_names = get_mplug_names(plug) + for child in children: + attr_names.update(get_mplug_names(child)) + if parent: + attr_names.update(get_mplug_names(parent)) + + # Get all overrides of the layer + # And find those that are relevant to the attribute + plug_overrides = [] + + # Iterate over the overrides in reverse so we get the last + # overrides first and can "break" whenever an absolute + # override is reached + layer_overrides = list(utils.getOverridesRecursive(rs_layer)) + for layer_override in reversed(layer_overrides): + + if skip_disabled and not layer_override.isEnabled(): + # Ignore disabled overrides + continue + + if skip_local_render and layer_override.isLocalRender(): + continue + + # The targets list can be very large so we'll do + # a quick filter by attribute name to detect whether + # it matches the attribute name, or its parent or child + if layer_override.attributeName() not in attr_names: + continue + + override_match = None + for override_plug in iter_override_targets(layer_override): + + override_match = None + if plug == override_plug: + override_match = (EXACT_MATCH, layer_override, None) + + elif parent and override_plug == parent: + override_match = (PARENT_MATCH, layer_override, parent_index) + + elif children and override_plug in children: + child_index = children.index(override_plug) + override_match = (CLIENT_MATCH, layer_override, child_index) + + if override_match: + plug_overrides.append(override_match) + break + + if ( + override_match and + stop_at_absolute_override and + isinstance(layer_override, AbsOverride) and + # When the override is only on a child plug then it doesn't + # override the entire value so we not stop at this override + not override_match[0] == CLIENT_MATCH + ): + # If override is absolute override, then BREAK out + # of parent loop we don't need to look any further as + # this is the absolute override + break + + return reversed(plug_overrides) diff --git a/pype/hosts/maya/plugins/publish/collect_renderable_camera.py b/pype/hosts/maya/plugins/publish/collect_renderable_camera.py index b90b85e7ec..5b3468424a 100644 --- a/pype/hosts/maya/plugins/publish/collect_renderable_camera.py +++ b/pype/hosts/maya/plugins/publish/collect_renderable_camera.py @@ -2,7 +2,7 @@ import pyblish.api from maya import cmds -from pype.hosts.maya.api import lib +from pype.hosts.maya.api.attributes import get_attr_in_layer class CollectRenderableCamera(pyblish.api.InstancePlugin): @@ -24,7 +24,7 @@ class CollectRenderableCamera(pyblish.api.InstancePlugin): self.log.info("layer: {}".format(layer)) cameras = cmds.ls(type="camera", long=True) renderable = [c for c in cameras if - lib.get_attr_in_layer("%s.renderable" % c, layer=layer)] + get_attr_in_layer("%s.renderable" % c, layer)] self.log.info("Found cameras %s: %s" % (len(renderable), renderable)) From 46fcf3912eb9629cfedd071cd702ea50769a7c27 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 24 Feb 2021 13:08:10 +0100 Subject: [PATCH 49/91] modified update_style of dict immutable key widget --- .../settings/settings/widgets/item_widgets.py | 58 ++++++++----------- 1 file changed, 25 insertions(+), 33 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_widgets.py b/pype/tools/settings/settings/widgets/item_widgets.py index dfcf1a272f..a4d17dda13 100644 --- a/pype/tools/settings/settings/widgets/item_widgets.py +++ b/pype/tools/settings/settings/widgets/item_widgets.py @@ -24,7 +24,6 @@ from .lib import CHILD_OFFSET class DictImmutableKeysWidget(BaseWidget): def create_ui(self): - self._child_style_state = "" self.input_fields = [] self.checkbox_child = None @@ -187,7 +186,7 @@ class DictImmutableKeysWidget(BaseWidget): for input_field in self.input_fields: input_field.hierarchical_style_update() - def update_style(self, is_overriden=None): + def update_style(self): if not self.body_widget and not self.label_widget: return @@ -201,36 +200,8 @@ class DictImmutableKeysWidget(BaseWidget): has_project_override = self.entity.has_project_override has_studio_override = self.entity.has_studio_override - is_invalid = self.is_invalid - if self.body_widget: - child_style_state = self.get_style_state( - is_invalid, - has_unsaved_changes, - has_project_override, - has_studio_override - ) - - if child_style_state: - child_style_state = "child-{}".format(child_style_state) - - if self._child_style_state != child_style_state: - self.body_widget.side_line_widget.setProperty( - "state", child_style_state - ) - self.body_widget.side_line_widget.style().polish( - self.body_widget.side_line_widget - ) - self._child_style_state = child_style_state - - # There is nothing to care if there is no label - if not self.label_widget: - return - # Don't change label if is not group or under group item - if not self.entity.is_group and not self.entity.group_item: - return - style_state = self.get_style_state( - is_invalid, + self.is_invalid, has_unsaved_changes, has_project_override, has_studio_override @@ -238,11 +209,32 @@ class DictImmutableKeysWidget(BaseWidget): if self._style_state == style_state: return + self._style_state = style_state + + if self.body_widget: + if style_state: + child_style_state = "child-{}".format(style_state) + else: + child_style_state = "" + + self.body_widget.side_line_widget.setProperty( + "state", child_style_state + ) + self.body_widget.side_line_widget.style().polish( + self.body_widget.side_line_widget + ) + + # There is nothing to care if there is no label + if not self.label_widget: + return + + # Don't change label if is not group or under group item + if not self.entity.is_group and not self.entity.group_item: + return + self.label_widget.setProperty("state", style_state) self.label_widget.style().polish(self.label_widget) - self._style_state = style_state - def _on_entity_change(self): pass From 6faface6f3a5a34ae08a2a0c15812a55bbf265d5 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 24 Feb 2021 13:08:46 +0100 Subject: [PATCH 50/91] inputs have unified way how to use label wrap --- pype/tools/settings/settings/widgets/base.py | 77 ++++++++++++++++++-- 1 file changed, 72 insertions(+), 5 deletions(-) diff --git a/pype/tools/settings/settings/widgets/base.py b/pype/tools/settings/settings/widgets/base.py index 692e9a9859..4010b8ab20 100644 --- a/pype/tools/settings/settings/widgets/base.py +++ b/pype/tools/settings/settings/widgets/base.py @@ -1,4 +1,6 @@ from Qt import QtWidgets, QtGui, QtCore +from .lib import CHILD_OFFSET +from .widgets import ExpandingWidget class BaseWidget(QtWidgets.QWidget): @@ -161,27 +163,92 @@ class BaseWidget(QtWidgets.QWidget): class InputWidget(BaseWidget): + def create_ui(self): + if self.entity.use_label_wrap: + label = None + self._create_label_wrap_ui() + else: + label = self.entity.label + self.label_widget = None + self.body_widget = None + self.content_widget = self + self.content_layout = self._create_layout(self) + + self._add_inputs_to_layout() + + self.entity_widget.add_widget_to_layout(self, label) + + def _create_label_wrap_ui(self): + content_widget = QtWidgets.QWidget(self) + content_widget.setObjectName("ContentWidget") + + content_widget.setProperty("content_state", "") + content_layout_margins = (CHILD_OFFSET, 5, 0, 0) + + body_widget = ExpandingWidget(self.entity.label, self) + label_widget = body_widget.label_widget + body_widget.set_content_widget(content_widget) + + content_layout = self._create_layout(content_widget) + content_layout.setContentsMargins(*content_layout_margins) + + main_layout = QtWidgets.QHBoxLayout(self) + main_layout.setContentsMargins(0, 0, 0, 0) + main_layout.setSpacing(0) + main_layout.addWidget(body_widget) + + self.label_widget = label_widget + self.body_widget = body_widget + self.content_widget = content_widget + self.content_layout = content_layout + + def _create_layout(self, parent_widget): + layout = QtWidgets.QHBoxLayout(parent_widget) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(5) + return layout + + def _add_inputs_to_layout(self): + raise NotImplementedError( + "Method `_add_inputs_to_layout` not implemented {}".format( + self.__class__.__name__ + ) + ) + def update_style(self): has_unsaved_changes = self.entity.has_unsaved_changes if not has_unsaved_changes and self.entity.group_item: has_unsaved_changes = self.entity.group_item.has_unsaved_changes - state = self.get_style_state( + style_state = self.get_style_state( self.is_invalid, has_unsaved_changes, self.entity.has_project_override, self.entity.has_studio_override ) - if self._style_state == state: + if self._style_state == style_state: return - self._style_state = state + self._style_state = style_state - self.input_field.setProperty("input-state", state) + self.input_field.setProperty("input-state", style_state) self.input_field.style().polish(self.input_field) if self.label_widget: - self.label_widget.setProperty("state", state) + self.label_widget.setProperty("state", style_state) self.label_widget.style().polish(self.label_widget) + if self.body_widget: + if style_state: + child_style_state = "child-{}".format(style_state) + else: + child_style_state = "" + + self.body_widget.side_line_widget.setProperty( + "state", child_style_state + ) + self.body_widget.side_line_widget.style().polish( + self.body_widget.side_line_widget + ) + @property def child_invalid(self): return self.is_invalid From eb0db5f3d84efe000abe0e20333ec0342cf88a67 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 24 Feb 2021 13:09:38 +0100 Subject: [PATCH 51/91] input widgets modified to be able use wrapper --- .../settings/settings/widgets/item_widgets.py | 82 ++++++------------- 1 file changed, 27 insertions(+), 55 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_widgets.py b/pype/tools/settings/settings/widgets/item_widgets.py index a4d17dda13..4299d3dee7 100644 --- a/pype/tools/settings/settings/widgets/item_widgets.py +++ b/pype/tools/settings/settings/widgets/item_widgets.py @@ -257,26 +257,23 @@ class DictImmutableKeysWidget(BaseWidget): class BoolWidget(InputWidget): - def create_ui(self): + def _add_inputs_to_layout(self): checkbox_height = self.style().pixelMetric( QtWidgets.QStyle.PM_IndicatorHeight ) - self.input_field = NiceCheckbox(height=checkbox_height, parent=self) + self.input_field = NiceCheckbox( + height=checkbox_height, parent=self.content_widget + ) - spacer = QtWidgets.QWidget(self) + spacer = QtWidgets.QWidget(self.content_widget) spacer.setAttribute(QtCore.Qt.WA_TranslucentBackground) - layout = QtWidgets.QHBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(5) - - layout.addWidget(self.input_field, 0) - layout.addWidget(spacer, 1) + self.content_layout.addWidget(self.input_field, 0) + self.content_layout.addWidget(spacer, 1) self.setFocusProxy(self.input_field) self.input_field.stateChanged.connect(self._on_value_change) - self.entity_widget.add_widget_to_layout(self, self.entity.label) def _on_entity_change(self): if self.entity.value != self.input_field.isChecked(): @@ -292,12 +289,12 @@ class BoolWidget(InputWidget): class TextWidget(InputWidget): - def create_ui(self): + def _add_inputs_to_layout(self): multiline = self.entity.multiline if multiline: - self.input_field = QtWidgets.QPlainTextEdit(self) + self.input_field = QtWidgets.QPlainTextEdit(self.content_widget) else: - self.input_field = QtWidgets.QLineEdit(self) + self.input_field = QtWidgets.QLineEdit(self.content_widget) placeholder_text = self.entity.placeholder_text if placeholder_text: @@ -309,15 +306,10 @@ class TextWidget(InputWidget): if multiline: layout_kwargs["alignment"] = QtCore.Qt.AlignTop - layout = QtWidgets.QHBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(5) - layout.addWidget(self.input_field, 1, **layout_kwargs) + self.content_layout.addWidget(self.input_field, 1, **layout_kwargs) self.input_field.textChanged.connect(self._on_value_change) - self.entity_widget.add_widget_to_layout(self, self.entity.label) - def _on_entity_change(self): if self.entity.value != self.input_value(): self.set_entity_value() @@ -342,26 +334,20 @@ class TextWidget(InputWidget): class NumberWidget(InputWidget): - def create_ui(self): - layout = QtWidgets.QHBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(5) - + def _add_inputs_to_layout(self): kwargs = { "minimum": self.entity.minimum, "maximum": self.entity.maximum, "decimal": self.entity.decimal } - self.input_field = NumberSpinBox(self, **kwargs) + self.input_field = NumberSpinBox(self.content_widget, **kwargs) self.setFocusProxy(self.input_field) - layout.addWidget(self.input_field, 1) + self.content_layout.addWidget(self.input_field, 1) self.input_field.valueChanged.connect(self._on_value_change) - self.entity_widget.add_widget_to_layout(self, self.entity.label) - def _on_entity_change(self): if self.entity.value != self.input_field.value(): self.set_entity_value() @@ -426,12 +412,8 @@ class RawJsonInput(QtWidgets.QPlainTextEdit): class RawJsonWidget(InputWidget): - def create_ui(self): - layout = QtWidgets.QHBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(5) - - self.input_field = RawJsonInput(self) + def _add_inputs_to_layout(self): + self.input_field = RawJsonInput(self.content_widget) self.input_field.setSizePolicy( QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.MinimumExpanding @@ -439,10 +421,11 @@ class RawJsonWidget(InputWidget): self.setFocusProxy(self.input_field) - layout.addWidget(self.input_field, 1, alignment=QtCore.Qt.AlignTop) + self.content_layout.addWidget( + self.input_field, 1, alignment=QtCore.Qt.AlignTop + ) self.input_field.textChanged.connect(self._on_value_change) - self.entity_widget.add_widget_to_layout(self, self.entity.label) def set_entity_value(self): self.input_field.set_value(self.entity.value) @@ -470,31 +453,26 @@ class RawJsonWidget(InputWidget): class EnumeratorWidget(InputWidget): - def create_ui(self): - layout = QtWidgets.QHBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(5) - + def _add_inputs_to_layout(self): if self.entity.multiselection: self.input_field = MultiSelectionComboBox( - placeholder=self.entity.placeholder, parent=self + placeholder=self.entity.placeholder, parent=self.content_widget ) model = self.input_field.model() for idx in range(self.input_field.count()): model.item(idx).setCheckable(True) else: - self.input_field = ComboBox(self) + self.input_field = ComboBox(self.content_widget) for enum_item in self.entity.enum_items: for value, label in enum_item.items(): self.input_field.addItem(label, value) - layout.addWidget(self.input_field, 0) + self.content_layout.addWidget(self.input_field, 0) self.setFocusProxy(self.input_field) self.input_field.value_changed.connect(self._on_value_change) - self.entity_widget.add_widget_to_layout(self, self.entity.label) def _on_entity_change(self): if self.entity.value != self.input_field.value(): @@ -579,12 +557,8 @@ class PathWidget(BaseWidget): class PathInputWidget(InputWidget): - def create_ui(self, label_widget=None): - layout = QtWidgets.QHBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(5) - - self.input_field = QtWidgets.QLineEdit(self) + def _add_inputs_to_layout(self): + self.input_field = QtWidgets.QLineEdit(self.content_widget) self.args_input_field = None if self.entity.with_arguments: self.input_field.setPlaceholderText("Executable path") @@ -592,15 +566,13 @@ class PathInputWidget(InputWidget): self.args_input_field.setPlaceholderText("Arguments") self.setFocusProxy(self.input_field) - layout.addWidget(self.input_field, 8) + self.content_layout.addWidget(self.input_field, 8) self.input_field.textChanged.connect(self._on_value_change) if self.args_input_field: - layout.addWidget(self.args_input_field, 2) + self.content_layout.addWidget(self.args_input_field, 2) self.args_input_field.textChanged.connect(self._on_value_change) - self.entity_widget.add_widget_to_layout(self, self.entity.label) - def _on_entity_change(self): if self.entity.value != self.input_value(): self.set_entity_value() From 4041377e9f01aa37cc5121c6ac4eddf4acdcec96 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 24 Feb 2021 13:09:53 +0100 Subject: [PATCH 52/91] modified projects folder to use label wrapper --- .../schemas/projects_schema/schema_project_global.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pype/settings/entities/schemas/projects_schema/schema_project_global.json b/pype/settings/entities/schemas/projects_schema/schema_project_global.json index fa5db9af88..1733e04f67 100644 --- a/pype/settings/entities/schemas/projects_schema/schema_project_global.json +++ b/pype/settings/entities/schemas/projects_schema/schema_project_global.json @@ -16,7 +16,8 @@ { "type": "raw-json", "label": "Project Folder Structure", - "key": "project_folder_structure" + "key": "project_folder_structure", + "use_label_wrap": true }, { "type": "schema", From 112024c88d7b093247397a06dc82656a996bca12 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 24 Feb 2021 14:43:55 +0100 Subject: [PATCH 53/91] fix project_folder_structure type --- .../entities/schemas/projects_schema/schema_project_global.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/settings/entities/schemas/projects_schema/schema_project_global.json b/pype/settings/entities/schemas/projects_schema/schema_project_global.json index 221cf0b588..1733e04f67 100644 --- a/pype/settings/entities/schemas/projects_schema/schema_project_global.json +++ b/pype/settings/entities/schemas/projects_schema/schema_project_global.json @@ -14,7 +14,7 @@ "name": "schema_global_tools" }, { - "type": "collapsible-wrap", + "type": "raw-json", "label": "Project Folder Structure", "key": "project_folder_structure", "use_label_wrap": true From 58add7268ab2906925efc21dacaae0c22eaea6e5 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 24 Feb 2021 14:51:18 +0100 Subject: [PATCH 54/91] fix filter of apps to display --- pype/tools/settings/settings/widgets/local_settings.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pype/tools/settings/settings/widgets/local_settings.py b/pype/tools/settings/settings/widgets/local_settings.py index d4c5a9a5f8..09c99c5063 100644 --- a/pype/tools/settings/settings/widgets/local_settings.py +++ b/pype/tools/settings/settings/widgets/local_settings.py @@ -285,7 +285,7 @@ class AppGroupWidget(QtWidgets.QWidget): valid_variants = {} for key, entity in group_entity["variants"].items(): - if entity["enabled"]: + if entity["enabled"].value: valid_variants[key] = entity group_label = group_entity.label @@ -340,13 +340,13 @@ class LocalApplicationsWidgets(QtWidgets.QWidget): for key, entity in system_settings_entity["applications"].items(): # Filter not enabled app groups - if not entity["enabled"]: + if not entity["enabled"].value: continue # Check if has enabled any variant enabled_variant = False for variant_entity in entity["variants"].values(): - if variant_entity["enabled"]: + if variant_entity["enabled"].value: enabled_variant = True break From 40923f8a05d2fa1a43eee25738390dd6471e1cb4 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 24 Feb 2021 14:54:21 +0100 Subject: [PATCH 55/91] show only executable paths from studio settings --- .../settings/widgets/local_settings.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/pype/tools/settings/settings/widgets/local_settings.py b/pype/tools/settings/settings/widgets/local_settings.py index 09c99c5063..8713cc49df 100644 --- a/pype/tools/settings/settings/widgets/local_settings.py +++ b/pype/tools/settings/settings/widgets/local_settings.py @@ -243,20 +243,25 @@ class AppVariantWidget(QtWidgets.QWidget): ) content_layout.addWidget(executable_input_widget) + self.executable_input_widget = executable_input_widget + studio_executables = ( variant_entity["executables"][platform.system().lower()] ) - if len(studio_executables) > 0: - content_layout.addWidget(Separator(parent=self)) + if len(studio_executables) < 1: + return + + content_layout.addWidget(Separator(parent=self)) + content_layout.addWidget( + QtWidgets.QLabel("Studio paths:", self) + ) for item in studio_executables: - path_widget = PathInput(content_widget) - path_widget.set_read_only() - path_widget.set_value(item.value) + path_widget = QtWidgets.QLineEdit(content_widget) + path_widget.setText(item.value[0]) + path_widget.setEnabled(False) content_layout.addWidget(path_widget) - self.executable_input_widget = executable_input_widget - def set_value(self, value): if not self.executable_input_widget: return From 1b4f57ddbcd1acf8202439bb40e93ca838d334b1 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 24 Feb 2021 14:54:56 +0100 Subject: [PATCH 56/91] dont show studio roots value but use it as placeholder --- pype/tools/settings/settings/widgets/local_settings.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/pype/tools/settings/settings/widgets/local_settings.py b/pype/tools/settings/settings/widgets/local_settings.py index 8713cc49df..1f22018be8 100644 --- a/pype/tools/settings/settings/widgets/local_settings.py +++ b/pype/tools/settings/settings/widgets/local_settings.py @@ -405,7 +405,6 @@ class RootsWidget(QtWidgets.QWidget): self.widgts_by_root_name.clear() - default_placeholder = "< Root overrides for this machine >" default_root_values = self.local_default_project_values() or {} roots_entity = ( @@ -429,17 +428,11 @@ class RootsWidget(QtWidgets.QWidget): placeholder = "< {} >".format(placeholder) if not placeholder: - placeholder = default_placeholder + placeholder = platform_entity.value value_input.setPlaceholderText(placeholder) value_input.textChanged.connect(self._on_root_value_change) - studio_input = QtWidgets.QLineEdit(root_input_widget) - studio_input.setText(platform_entity.value) - studio_input.setReadOnly(True) - root_input_layout.addWidget(value_input) - root_input_layout.addWidget(Separator(parent=self)) - root_input_layout.addWidget(studio_input) root_layout = QtWidgets.QHBoxLayout(root_widget) root_layout.addWidget(key_label) From 5fe7db24770febc5445febb3850a26bfa45eb0ec Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 24 Feb 2021 15:05:02 +0100 Subject: [PATCH 57/91] app executables are without arguments --- .../settings/widgets/local_settings.py | 82 +++++-------------- 1 file changed, 19 insertions(+), 63 deletions(-) diff --git a/pype/tools/settings/settings/widgets/local_settings.py b/pype/tools/settings/settings/widgets/local_settings.py index 1f22018be8..c9d63fa032 100644 --- a/pype/tools/settings/settings/widgets/local_settings.py +++ b/pype/tools/settings/settings/widgets/local_settings.py @@ -151,65 +151,8 @@ class LocalGeneralWidgets(QtWidgets.QWidget): return output -class PathInput(QtWidgets.QWidget): - def __init__( - self, - parent, - executable_placeholder=None, - argument_placeholder=None - ): - super(PathInput, self).__init__(parent) - - executable_input = QtWidgets.QLineEdit(self) - if executable_placeholder: - executable_input.setPlaceholderText(executable_placeholder) - - arguments_input = QtWidgets.QLineEdit(self) - if argument_placeholder: - arguments_input.setPlaceholderText(argument_placeholder) - - layout = QtWidgets.QHBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(5) - - layout.addWidget(executable_input) - layout.addWidget(arguments_input) - - self.executable_input = executable_input - self.arguments_input = arguments_input - - def set_read_only(self, readonly=True): - self.executable_input.setReadOnly(readonly) - self.arguments_input.setReadOnly(readonly) - - def set_value(self, arguments): - executable = "" - args = "" - if arguments: - if isinstance(arguments, str): - executable = arguments - elif isinstance(arguments, list): - executable = arguments[0] - if len(arguments) > 1: - args = " ".join(arguments[1:]) - self.executable_input.setText(executable) - self.arguments_input.setText(args) - - def settings_value(self): - executable = self.executable_input.text() - if not executable: - return None - - output = [executable] - args = self.arguments_input.text() - if args: - output.append(args) - return output - - class AppVariantWidget(QtWidgets.QWidget): exec_placeholder = "< Specific path for this machine >" - args_placeholder = "< Launch arguments >" def __init__(self, group_label, variant_entity, parent): super(AppVariantWidget, self).__init__(parent) @@ -238,9 +181,8 @@ class AppVariantWidget(QtWidgets.QWidget): content_layout.addWidget(warn_label) return - executable_input_widget = PathInput( - content_widget, self.exec_placeholder, self.args_placeholder - ) + executable_input_widget = QtWidgets.QLineEdit(content_widget) + executable_input_widget.setPlaceholderText(self.exec_placeholder) content_layout.addWidget(executable_input_widget) self.executable_input_widget = executable_input_widget @@ -273,15 +215,29 @@ class AppVariantWidget(QtWidgets.QWidget): type(value), dict )) value = {} - self.executable_input_widget.set_value(value.get("executable")) + + executable_path = value.get("executable") + if not executable_path: + executable_path = "" + elif isinstance(executable_path, list): + print("Got list in executable path so using first item as value") + executable_path = executable_path[0] + + if not isinstance(executable_path, str): + executable_path = "" + print(( + "Got invalid value type of app executable {}. Expected {}" + ).format(type(value), str)) + + self.executable_input_widget.setText(executable_path) def settings_value(self): if not self.executable_input_widget: return None - value = self.executable_input_widget.settings_value() + value = self.executable_input_widget.text() if not value: return None - return {"executable": self.executable_input_widget.settings_value()} + return {"executable": value} class AppGroupWidget(QtWidgets.QWidget): From 0c4fb521806793fb039547ddc20e334bf2a09057 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 24 Feb 2021 16:07:41 +0100 Subject: [PATCH 58/91] added helper mardown document --- pype/settings/local_settings.md | 79 +++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 pype/settings/local_settings.md diff --git a/pype/settings/local_settings.md b/pype/settings/local_settings.md new file mode 100644 index 0000000000..66c5b753d5 --- /dev/null +++ b/pype/settings/local_settings.md @@ -0,0 +1,79 @@ +# Structure of local settings +- local settings do not have any validation schemas right now this should help to see what is stored to local settings and how it works +- they are stored by identifier site_id which should be unified identifier of workstation +- all keys may and may not available on load +- contain main categories: `general`, `applications`, `projects` + +## Categories +### General +- ATM contain only label of site +```json +{ + "general": { + "site_name": "MySite" + } +} +``` + +### Applications +- modifications of application executables +- output should match application groups and variants +```json +{ + "applications": { + "": { + "": { + "executable": "/my/path/to/nuke_12_2" + } + } + } +} +``` + +### Projects +- project specific modifications +- default project is stored under constant key defined in `pype.settings.contants` +```json +{ + "projects": { + "": { + "active_site": "", + "remote_site": "", + "roots": { + "": { + "": "" + } + } + } + } +} +``` + +## Final document +```json +{ + "_id": "", + "site_id": "", + "general": { + "site_name": "MySite" + }, + "applications": { + "": { + "": { + "executable": "" + } + } + }, + "projects": { + "": { + "active_site": "", + "remote_site": "", + "roots": { + "": { + "": "" + } + } + } + } +} +``` From 45217ed2d6d3a142104b196a8717ad5cf09e1a04 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 24 Feb 2021 16:27:36 +0100 Subject: [PATCH 59/91] moved local settings gui out of settings widgets --- pype/tools/settings/__init__.py | 8 +- .../tools/settings/local_settings/__init__.py | 1 + .../window.py} | 81 ++++++++++++++----- pype/tools/settings/settings/__init__.py | 2 - .../settings/settings/widgets/__init__.py | 4 +- 5 files changed, 68 insertions(+), 28 deletions(-) create mode 100644 pype/tools/settings/local_settings/__init__.py rename pype/tools/settings/{settings/widgets/local_settings.py => local_settings/window.py} (90%) diff --git a/pype/tools/settings/__init__.py b/pype/tools/settings/__init__.py index 578c6ef82c..0a5d6933a1 100644 --- a/pype/tools/settings/__init__.py +++ b/pype/tools/settings/__init__.py @@ -1,11 +1,10 @@ import sys from Qt import QtWidgets, QtGui - +from .local_settings import LocalSettingsWindow from .settings import ( style, MainWidget, - ProjectListWidget, - LocalSettingsWindow + ProjectListWidget ) @@ -23,7 +22,8 @@ def main(user_role=None): app = QtWidgets.QApplication(sys.argv) app.setWindowIcon(QtGui.QIcon(style.app_icon_path())) - widget = MainWidget(user_role) + # widget = MainWidget(user_role) + widget = LocalSettingsWindow() widget.show() widget.reset() diff --git a/pype/tools/settings/local_settings/__init__.py b/pype/tools/settings/local_settings/__init__.py new file mode 100644 index 0000000000..ddb9741b79 --- /dev/null +++ b/pype/tools/settings/local_settings/__init__.py @@ -0,0 +1 @@ +from .window import LocalSettingsWindow diff --git a/pype/tools/settings/settings/widgets/local_settings.py b/pype/tools/settings/local_settings/window.py similarity index 90% rename from pype/tools/settings/settings/widgets/local_settings.py rename to pype/tools/settings/local_settings/window.py index c9d63fa032..be31994425 100644 --- a/pype/tools/settings/settings/widgets/local_settings.py +++ b/pype/tools/settings/local_settings/window.py @@ -4,13 +4,13 @@ import traceback import platform import logging from Qt import QtWidgets, QtCore, QtGui -from .widgets import ( +from pype.tools.settings.settings.widgets.widgets import ( ExpandingWidget, SpacerWidget, ProjectListWidget ) -from .. import style -from .lib import CHILD_OFFSET +from ..settings import style +from pype.tools.settings.settings.widgets.lib import CHILD_OFFSET from pype.settings.constants import PROJECT_ANATOMY_KEY from pype.settings.lib import ( get_local_settings, @@ -353,6 +353,9 @@ class RootsWidget(QtWidgets.QWidget): self.content_layout = main_layout + def _on_root_value_change(self): + self.value_changed.emit() + def refresh(self): while self.content_layout.count(): item = self.content_layout.itemAt(0) @@ -399,9 +402,6 @@ class RootsWidget(QtWidgets.QWidget): self.content_layout.addWidget(SpacerWidget(self), 1) - def _on_root_value_change(self): - self.value_changed.emit() - def local_default_project_values(self): default_project = self._parent_widget.per_project_settings.get(None) if default_project: @@ -427,6 +427,53 @@ class RootsWidget(QtWidgets.QWidget): return output +class ProjectSpecificWidget(QtWidgets.QWidget): + value_changed = QtCore.Signal() + + def __init__(self, project_settings, parent): + self._parent_widget = parent + super(ProjectSpecificWidget, self).__init__(parent) + + self.project_settings = project_settings + self.widgts_by_root_name = {} + + sites_widget = QtWidgets.QWidget(self) + sites_layout = QtWidgets.QHBoxLayout(sites_widget) + active_site_combo = QtWidgets.QComboBox(sites_widget) + remote_site_combo = QtWidgets.QComboBox(sites_widget) + sites_layout.addWidget(QtWidgets.QLabel("Active Site", sites_widget)) + sites_layout.addWidget(active_site_combo) + sites_layout.addWidget(QtWidgets.QLabel("Remote Site", sites_widget)) + sites_layout.addWidget(remote_site_combo) + + main_layout = QtWidgets.QVBoxLayout(self) + main_layout.addWidget(sites_widget) + main_layout.addWidget(SpacerWidget(self), 1) + + def set_value(self, value): + pass + # if not value: + # value = {} + # + # for root_name, widget in self.widgts_by_root_name.items(): + # root_value = value.get(root_name) or "" + # widget.setText(root_value) + + def settings_value(self): + return {} + # output = {} + # for root_name, widget in self.widgts_by_root_name.items(): + # value = widget.text() + # if value: + # output[root_name] = value + # if not output: + # return None + # return output + + def change_project(self, project_name): + pass + + class _ProjectListWidget(ProjectListWidget): def on_item_clicked(self, new_index): new_project_name = new_index.data(QtCore.Qt.DisplayRole) @@ -448,23 +495,25 @@ class ProjectSettingsWidget(QtWidgets.QWidget): self.per_project_settings = {} projects_widget = _ProjectListWidget(self) - roots_widget = RootsWidget(project_settings, self) + project_specific_widget = ProjectSpecificWidget(project_settings, self) main_layout = QtWidgets.QHBoxLayout(self) main_layout.setContentsMargins(0, 0, 0, 0) main_layout.addWidget(projects_widget, 0) - main_layout.addWidget(roots_widget, 1) + main_layout.addWidget(project_specific_widget, 1) projects_widget.project_changed.connect(self._on_project_change) - roots_widget.value_changed.connect(self._on_root_value_change) + # project_specific_widget.value_changed.connect( + # self._on_root_value_change + # ) self.project_settings = project_settings self.projects_widget = projects_widget - self.roots_widget = roots_widget + self.project_specific_widget = project_specific_widget def _current_value(self): - roots_value = self.roots_widget.settings_value() + roots_value = self.project_specific_widget.settings_value() current_value = {} if roots_value: current_value[LOCAL_ROOTS_KEY] = roots_value @@ -477,10 +526,7 @@ class ProjectSettingsWidget(QtWidgets.QWidget): project_name = self.project_name() self.project_settings.change_project(project_name) - self.roots_widget.refresh() - - project_value = self.per_project_settings.get(project_name) or {} - self.roots_widget.set_value(project_value.get(LOCAL_ROOTS_KEY)) + self.project_specific_widget.change_project(project_name) def _on_root_value_change(self): self.per_project_settings[self.project_name()] = ( @@ -493,11 +539,8 @@ class ProjectSettingsWidget(QtWidgets.QWidget): self.per_project_settings = value self.projects_widget.refresh() - self.roots_widget.refresh() - project_name = self.project_name() - project_value = self.per_project_settings.get(project_name) or {} - self.roots_widget.set_value(project_value.get(LOCAL_ROOTS_KEY)) + self.project_specific_widget.set_value(self.per_project_settings) def settings_value(self): output = {} diff --git a/pype/tools/settings/settings/__init__.py b/pype/tools/settings/settings/__init__.py index 4d930b3fda..3c12a73639 100644 --- a/pype/tools/settings/settings/__init__.py +++ b/pype/tools/settings/settings/__init__.py @@ -1,7 +1,6 @@ from . import style from .widgets import ( MainWidget, - LocalSettingsWindow, ProjectListWidget ) @@ -9,6 +8,5 @@ from .widgets import ( __all__ = ( "style", "MainWidget", - "LocalSettingsWindow", "ProjectListWidget" ) diff --git a/pype/tools/settings/settings/widgets/__init__.py b/pype/tools/settings/settings/widgets/__init__.py index 6e31232818..c9fec16f6e 100644 --- a/pype/tools/settings/settings/widgets/__init__.py +++ b/pype/tools/settings/settings/widgets/__init__.py @@ -1,10 +1,8 @@ from .window import MainWidget from .widgets import ProjectListWidget -from .local_settings import LocalSettingsWindow __all__ = [ "MainWidget", - "ProjectListWidget", - "LocalSettingsWindow" + "ProjectListWidget" ] From 33927afc1f4d212e99fd24ba1bf199b6933bb736 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 24 Feb 2021 16:38:20 +0100 Subject: [PATCH 60/91] split local settings gui into smaller parts --- .../settings/local_settings/apps_widget.py | 195 +++++++ .../settings/local_settings/mongo_widget.py | 80 +++ .../local_settings/projects_widget.py | 217 ++++++++ pype/tools/settings/local_settings/widgets.py | 22 + pype/tools/settings/local_settings/window.py | 516 +----------------- 5 files changed, 527 insertions(+), 503 deletions(-) create mode 100644 pype/tools/settings/local_settings/apps_widget.py create mode 100644 pype/tools/settings/local_settings/mongo_widget.py create mode 100644 pype/tools/settings/local_settings/projects_widget.py create mode 100644 pype/tools/settings/local_settings/widgets.py diff --git a/pype/tools/settings/local_settings/apps_widget.py b/pype/tools/settings/local_settings/apps_widget.py new file mode 100644 index 0000000000..52fbc45fd6 --- /dev/null +++ b/pype/tools/settings/local_settings/apps_widget.py @@ -0,0 +1,195 @@ +import platform +from Qt import QtWidgets +from .widgets import ( + Separator, + ExpandingWidget, + CHILD_OFFSET +) + + +class AppVariantWidget(QtWidgets.QWidget): + exec_placeholder = "< Specific path for this machine >" + + def __init__(self, group_label, variant_entity, parent): + super(AppVariantWidget, self).__init__(parent) + + self.executable_input_widget = None + + label = " ".join([group_label, variant_entity.label]) + + expading_widget = ExpandingWidget(label, self) + content_widget = QtWidgets.QWidget(expading_widget) + content_layout = QtWidgets.QVBoxLayout(content_widget) + content_layout.setContentsMargins(CHILD_OFFSET, 5, 0, 0) + + expading_widget.set_content_widget(content_widget) + + # Add expanding widget to main layout + layout = QtWidgets.QHBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.addWidget(expading_widget) + + # TODO For celaction - not sure what is "Celaction publish" for + if not variant_entity["executables"].multiplatform: + warn_label = QtWidgets.QLabel( + "Application without multiplatform paths" + ) + content_layout.addWidget(warn_label) + return + + executable_input_widget = QtWidgets.QLineEdit(content_widget) + executable_input_widget.setPlaceholderText(self.exec_placeholder) + content_layout.addWidget(executable_input_widget) + + self.executable_input_widget = executable_input_widget + + studio_executables = ( + variant_entity["executables"][platform.system().lower()] + ) + if len(studio_executables) < 1: + return + + content_layout.addWidget(Separator(parent=self)) + content_layout.addWidget( + QtWidgets.QLabel("Studio paths:", self) + ) + + for item in studio_executables: + path_widget = QtWidgets.QLineEdit(content_widget) + path_widget.setText(item.value[0]) + path_widget.setEnabled(False) + content_layout.addWidget(path_widget) + + def set_value(self, value): + if not self.executable_input_widget: + return + + if not value: + value = {} + elif not isinstance(value, dict): + print("Got invalid value type {}. Expected {}".format( + type(value), dict + )) + value = {} + + executable_path = value.get("executable") + if not executable_path: + executable_path = "" + elif isinstance(executable_path, list): + print("Got list in executable path so using first item as value") + executable_path = executable_path[0] + + if not isinstance(executable_path, str): + executable_path = "" + print(( + "Got invalid value type of app executable {}. Expected {}" + ).format(type(value), str)) + + self.executable_input_widget.setText(executable_path) + + def settings_value(self): + if not self.executable_input_widget: + return None + value = self.executable_input_widget.text() + if not value: + return None + return {"executable": value} + + +class AppGroupWidget(QtWidgets.QWidget): + def __init__(self, group_entity, parent): + super(AppGroupWidget, self).__init__(parent) + + valid_variants = {} + for key, entity in group_entity["variants"].items(): + if entity["enabled"].value: + valid_variants[key] = entity + + group_label = group_entity.label + expading_widget = ExpandingWidget(group_label, self) + content_widget = QtWidgets.QWidget(expading_widget) + content_layout = QtWidgets.QVBoxLayout(content_widget) + content_layout.setContentsMargins(CHILD_OFFSET, 5, 0, 0) + + widgets_by_variant_name = {} + for variant_name, variant_entity in valid_variants.items(): + variant_widget = AppVariantWidget( + group_label, variant_entity, content_widget + ) + widgets_by_variant_name[variant_name] = variant_widget + content_layout.addWidget(variant_widget) + + expading_widget.set_content_widget(content_widget) + + layout = QtWidgets.QHBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.addWidget(expading_widget) + + self.widgets_by_variant_name = widgets_by_variant_name + + def set_value(self, value): + if not value: + value = {} + + for variant_name, widget in self.widgets_by_variant_name.items(): + widget.set_value(value.get(variant_name)) + + def settings_value(self): + output = {} + for variant_name, widget in self.widgets_by_variant_name.items(): + value = widget.settings_value() + if value: + output[variant_name] = value + + if not output: + return None + return output + + +class LocalApplicationsWidgets(QtWidgets.QWidget): + def __init__(self, system_settings_entity, parent): + super(LocalApplicationsWidgets, self).__init__(parent) + + widgets_by_group_name = {} + + layout = QtWidgets.QVBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + + for key, entity in system_settings_entity["applications"].items(): + # Filter not enabled app groups + if not entity["enabled"].value: + continue + + # Check if has enabled any variant + enabled_variant = False + for variant_entity in entity["variants"].values(): + if variant_entity["enabled"].value: + enabled_variant = True + break + + if not enabled_variant: + continue + + # Create App group specific widget and store it by the key + group_widget = AppGroupWidget(entity, self) + widgets_by_group_name[key] = group_widget + layout.addWidget(group_widget) + + self.widgets_by_group_name = widgets_by_group_name + + def set_value(self, value): + if not value: + value = {} + + for group_name, widget in self.widgets_by_group_name.items(): + widget.set_value(value.get(group_name)) + + def settings_value(self): + output = {} + for group_name, widget in self.widgets_by_group_name.items(): + value = widget.settings_value() + if value: + output[group_name] = value + if not output: + return None + return output diff --git a/pype/tools/settings/local_settings/mongo_widget.py b/pype/tools/settings/local_settings/mongo_widget.py new file mode 100644 index 0000000000..c6f6ab1591 --- /dev/null +++ b/pype/tools/settings/local_settings/mongo_widget.py @@ -0,0 +1,80 @@ +import os +import sys +import traceback + +from Qt import QtWidgets +from pymongo.errors import ServerSelectionTimeoutError + +from pype.api import change_pype_mongo_url + + +class PypeMongoWidget(QtWidgets.QWidget): + def __init__(self, parent): + super(PypeMongoWidget, self).__init__(parent) + + # Warning label + warning_label = QtWidgets.QLabel(( + "WARNING: Requires restart. Change of Pype Mongo requires to" + " restart of all running Pype processes and process using Pype" + " (Including this)." + "\n- all changes in different categories won't be saved." + ), self) + warning_label.setStyleSheet("font-weight: bold;") + + # Label + mongo_url_label = QtWidgets.QLabel("Pype Mongo URL", self) + + # Input + mongo_url_input = QtWidgets.QLineEdit(self) + mongo_url_input.setPlaceholderText("< Pype Mongo URL >") + mongo_url_input.setText(os.environ["PYPE_MONGO"]) + + # Confirm button + mongo_url_change_btn = QtWidgets.QPushButton("Confirm Change", self) + + layout = QtWidgets.QGridLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.addWidget(warning_label, 0, 0, 1, 3) + layout.addWidget(mongo_url_label, 1, 0) + layout.addWidget(mongo_url_input, 1, 1) + layout.addWidget(mongo_url_change_btn, 1, 2) + + mongo_url_change_btn.clicked.connect(self._on_confirm_click) + + self.mongo_url_input = mongo_url_input + + def _on_confirm_click(self): + value = self.mongo_url_input.text() + + dialog = QtWidgets.QMessageBox(self) + + title = "Pype mongo changed" + message = ( + "Pype mongo url was successfully changed. Restart Pype please." + ) + details = None + + try: + change_pype_mongo_url(value) + except Exception as exc: + if isinstance(exc, ServerSelectionTimeoutError): + error_message = ( + "Connection timeout passed." + " Probably can't connect to the Mongo server." + ) + else: + error_message = str(exc) + + title = "Pype mongo change failed" + # TODO catch exception message more gracefully + message = ( + "Pype mongo change was not successful." + " Full traceback can be found in details section.\n\n" + "Error message:\n{}" + ).format(error_message) + details = "\n".join(traceback.format_exception(*sys.exc_info())) + dialog.setWindowTitle(title) + dialog.setText(message) + if details: + dialog.setDetailedText(details) + dialog.exec_() diff --git a/pype/tools/settings/local_settings/projects_widget.py b/pype/tools/settings/local_settings/projects_widget.py new file mode 100644 index 0000000000..4426422299 --- /dev/null +++ b/pype/tools/settings/local_settings/projects_widget.py @@ -0,0 +1,217 @@ +from Qt import QtWidgets, QtCore +from pype.tools.settings.settings import ProjectListWidget +from .widgets import SpacerWidget +from pype.settings.constants import PROJECT_ANATOMY_KEY + + +class _ProjectListWidget(ProjectListWidget): + 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 + + self.select_project(new_project_name) + self.current_project = new_project_name + self.project_changed.emit() + + +class RootsWidget(QtWidgets.QWidget): + value_changed = QtCore.Signal() + + def __init__(self, project_settings, parent): + self._parent_widget = parent + super(RootsWidget, self).__init__(parent) + + self.project_settings = project_settings + self.widgts_by_root_name = {} + + main_layout = QtWidgets.QVBoxLayout(self) + + self.content_layout = main_layout + + def _on_root_value_change(self): + self.value_changed.emit() + + def refresh(self): + while self.content_layout.count(): + item = self.content_layout.itemAt(0) + item.widget().hide() + self.content_layout.removeItem(item) + + self.widgts_by_root_name.clear() + + default_root_values = self.local_default_project_values() or {} + + roots_entity = ( + self.project_settings[PROJECT_ANATOMY_KEY][LOCAL_ROOTS_KEY] + ) + is_in_default = self.project_settings.project_name is None + for root_name, path_entity in roots_entity.items(): + platform_entity = path_entity[platform.system().lower()] + root_widget = QtWidgets.QWidget(self) + + key_label = QtWidgets.QLabel(root_name, root_widget) + + root_input_widget = QtWidgets.QWidget(root_widget) + root_input_layout = QtWidgets.QVBoxLayout(root_input_widget) + + value_input = QtWidgets.QLineEdit(root_input_widget) + placeholder = None + if not is_in_default: + placeholder = default_root_values.get(root_name) + if placeholder: + placeholder = "< {} >".format(placeholder) + + if not placeholder: + placeholder = platform_entity.value + value_input.setPlaceholderText(placeholder) + value_input.textChanged.connect(self._on_root_value_change) + + root_input_layout.addWidget(value_input) + + root_layout = QtWidgets.QHBoxLayout(root_widget) + root_layout.addWidget(key_label) + root_layout.addWidget(root_input_widget) + + self.content_layout.addWidget(root_widget) + self.widgts_by_root_name[root_name] = value_input + + self.content_layout.addWidget(SpacerWidget(self), 1) + + def local_default_project_values(self): + default_project = self._parent_widget.per_project_settings.get(None) + if default_project: + return default_project.get(LOCAL_ROOTS_KEY) + return None + + def set_value(self, value): + if not value: + value = {} + + for root_name, widget in self.widgts_by_root_name.items(): + root_value = value.get(root_name) or "" + widget.setText(root_value) + + def settings_value(self): + output = {} + for root_name, widget in self.widgts_by_root_name.items(): + value = widget.text() + if value: + output[root_name] = value + if not output: + return None + return output + + +class ProjectSpecificWidget(QtWidgets.QWidget): + value_changed = QtCore.Signal() + + def __init__(self, project_settings, parent): + self._parent_widget = parent + super(ProjectSpecificWidget, self).__init__(parent) + + self.project_settings = project_settings + self.widgts_by_root_name = {} + + sites_widget = QtWidgets.QWidget(self) + sites_layout = QtWidgets.QHBoxLayout(sites_widget) + active_site_combo = QtWidgets.QComboBox(sites_widget) + remote_site_combo = QtWidgets.QComboBox(sites_widget) + sites_layout.addWidget(QtWidgets.QLabel("Active Site", sites_widget)) + sites_layout.addWidget(active_site_combo) + sites_layout.addWidget(QtWidgets.QLabel("Remote Site", sites_widget)) + sites_layout.addWidget(remote_site_combo) + + main_layout = QtWidgets.QVBoxLayout(self) + main_layout.addWidget(sites_widget) + main_layout.addWidget(SpacerWidget(self), 1) + + def set_value(self, value): + pass + # if not value: + # value = {} + # + # for root_name, widget in self.widgts_by_root_name.items(): + # root_value = value.get(root_name) or "" + # widget.setText(root_value) + + def settings_value(self): + return {} + # output = {} + # for root_name, widget in self.widgts_by_root_name.items(): + # value = widget.text() + # if value: + # output[root_name] = value + # if not output: + # return None + # return output + + def change_project(self, project_name): + pass + + +class ProjectSettingsWidget(QtWidgets.QWidget): + def __init__(self, project_settings, parent): + super(ProjectSettingsWidget, self).__init__(parent) + + self.per_project_settings = {} + + projects_widget = _ProjectListWidget(self) + project_specific_widget = ProjectSpecificWidget(project_settings, self) + + main_layout = QtWidgets.QHBoxLayout(self) + main_layout.setContentsMargins(0, 0, 0, 0) + main_layout.addWidget(projects_widget, 0) + main_layout.addWidget(project_specific_widget, 1) + + projects_widget.project_changed.connect(self._on_project_change) + # project_specific_widget.value_changed.connect( + # self._on_root_value_change + # ) + + self.project_settings = project_settings + + self.projects_widget = projects_widget + self.project_specific_widget = project_specific_widget + + def _current_value(self): + roots_value = self.project_specific_widget.settings_value() + current_value = {} + if roots_value: + current_value[LOCAL_ROOTS_KEY] = roots_value + return current_value + + def project_name(self): + return self.projects_widget.project_name() + + def _on_project_change(self): + project_name = self.project_name() + + self.project_settings.change_project(project_name) + self.project_specific_widget.change_project(project_name) + + def _on_root_value_change(self): + self.per_project_settings[self.project_name()] = ( + self._current_value() + ) + + def set_value(self, value): + if not value: + value = {} + self.per_project_settings = value + + self.projects_widget.refresh() + + self.project_specific_widget.set_value(self.per_project_settings) + + def settings_value(self): + output = {} + for project_name, value in self.per_project_settings.items(): + if value: + output[project_name] = value + if not output: + return None + return output diff --git a/pype/tools/settings/local_settings/widgets.py b/pype/tools/settings/local_settings/widgets.py new file mode 100644 index 0000000000..ebb14a3102 --- /dev/null +++ b/pype/tools/settings/local_settings/widgets.py @@ -0,0 +1,22 @@ +from Qt import QtWidgets +from pype.tools.settings.settings.widgets.widgets import ( + ExpandingWidget, + SpacerWidget +) +from pype.tools.settings.settings.widgets.lib import CHILD_OFFSET + + +class Separator(QtWidgets.QFrame): + def __init__(self, height=None, parent=None): + super(Separator, self).__init__(parent) + if height is None: + height = 2 + + splitter_item = QtWidgets.QWidget(self) + splitter_item.setStyleSheet("background-color: #21252B;") + splitter_item.setMinimumHeight(height) + splitter_item.setMaximumHeight(height) + + layout = QtWidgets.QHBoxLayout(self) + layout.setContentsMargins(5, 5, 5, 5) + layout.addWidget(splitter_item) diff --git a/pype/tools/settings/local_settings/window.py b/pype/tools/settings/local_settings/window.py index be31994425..1fd2ca8e06 100644 --- a/pype/tools/settings/local_settings/window.py +++ b/pype/tools/settings/local_settings/window.py @@ -1,27 +1,26 @@ -import os -import sys -import traceback -import platform import logging from Qt import QtWidgets, QtCore, QtGui -from pype.tools.settings.settings.widgets.widgets import ( - ExpandingWidget, - SpacerWidget, - ProjectListWidget -) + from ..settings import style -from pype.tools.settings.settings.widgets.lib import CHILD_OFFSET -from pype.settings.constants import PROJECT_ANATOMY_KEY + from pype.settings.lib import ( get_local_settings, save_local_settings ) from pype.api import ( SystemSettings, - ProjectSettings, - change_pype_mongo_url + ProjectSettings ) -from pymongo.errors import ServerSelectionTimeoutError + +from .widgets import ( + Separator, + SpacerWidget, + ExpandingWidget, + CHILD_OFFSET +) +from .mongo_widget import PypeMongoWidget +from .apps_widget import LocalApplicationsWidgets +from .projects_widget import ProjectSettingsWidget log = logging.getLogger(__name__) @@ -32,94 +31,6 @@ LOCAL_APPS_KEY = "applications" LOCAL_ROOTS_KEY = "roots" -class Separator(QtWidgets.QFrame): - def __init__(self, height=None, parent=None): - super(Separator, self).__init__(parent) - if height is None: - height = 2 - - splitter_item = QtWidgets.QWidget(self) - splitter_item.setStyleSheet("background-color: #21252B;") - splitter_item.setMinimumHeight(height) - splitter_item.setMaximumHeight(height) - - layout = QtWidgets.QHBoxLayout(self) - layout.setContentsMargins(5, 5, 5, 5) - layout.addWidget(splitter_item) - - -class PypeMongoWidget(QtWidgets.QWidget): - def __init__(self, parent): - super(PypeMongoWidget, self).__init__(parent) - - # Warning label - warning_label = QtWidgets.QLabel(( - "WARNING: Requires restart. Change of Pype Mongo requires to" - " restart of all running Pype processes and process using Pype" - " (Including this)." - "\n- all changes in different categories won't be saved." - ), self) - warning_label.setStyleSheet("font-weight: bold;") - - # Label - mongo_url_label = QtWidgets.QLabel("Pype Mongo URL", self) - - # Input - mongo_url_input = QtWidgets.QLineEdit(self) - mongo_url_input.setPlaceholderText("< Pype Mongo URL >") - mongo_url_input.setText(os.environ["PYPE_MONGO"]) - - # Confirm button - mongo_url_change_btn = QtWidgets.QPushButton("Confirm Change", self) - - layout = QtWidgets.QGridLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - layout.addWidget(warning_label, 0, 0, 1, 3) - layout.addWidget(mongo_url_label, 1, 0) - layout.addWidget(mongo_url_input, 1, 1) - layout.addWidget(mongo_url_change_btn, 1, 2) - - mongo_url_change_btn.clicked.connect(self._on_confirm_click) - - self.mongo_url_input = mongo_url_input - - def _on_confirm_click(self): - value = self.mongo_url_input.text() - - dialog = QtWidgets.QMessageBox(self) - - title = "Pype mongo changed" - message = ( - "Pype mongo url was successfully changed. Restart Pype please." - ) - details = None - - try: - change_pype_mongo_url(value) - except Exception as exc: - if isinstance(exc, ServerSelectionTimeoutError): - error_message = ( - "Connection timeout passed." - " Probably can't connect to the Mongo server." - ) - else: - error_message = str(exc) - - title = "Pype mongo change failed" - # TODO catch exception message more gracefully - message = ( - "Pype mongo change was not successful." - " Full traceback can be found in details section.\n\n" - "Error message:\n{}" - ).format(error_message) - details = "\n".join(traceback.format_exception(*sys.exc_info())) - dialog.setWindowTitle(title) - dialog.setText(message) - if details: - dialog.setDetailedText(details) - dialog.exec_() - - class LocalGeneralWidgets(QtWidgets.QWidget): def __init__(self, parent): super(LocalGeneralWidgets, self).__init__(parent) @@ -151,407 +62,6 @@ class LocalGeneralWidgets(QtWidgets.QWidget): return output -class AppVariantWidget(QtWidgets.QWidget): - exec_placeholder = "< Specific path for this machine >" - - def __init__(self, group_label, variant_entity, parent): - super(AppVariantWidget, self).__init__(parent) - - self.executable_input_widget = None - - label = " ".join([group_label, variant_entity.label]) - - expading_widget = ExpandingWidget(label, self) - content_widget = QtWidgets.QWidget(expading_widget) - content_layout = QtWidgets.QVBoxLayout(content_widget) - content_layout.setContentsMargins(CHILD_OFFSET, 5, 0, 0) - - expading_widget.set_content_widget(content_widget) - - # Add expanding widget to main layout - layout = QtWidgets.QHBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - layout.addWidget(expading_widget) - - # TODO For celaction - not sure what is "Celaction publish" for - if not variant_entity["executables"].multiplatform: - warn_label = QtWidgets.QLabel( - "Application without multiplatform paths" - ) - content_layout.addWidget(warn_label) - return - - executable_input_widget = QtWidgets.QLineEdit(content_widget) - executable_input_widget.setPlaceholderText(self.exec_placeholder) - content_layout.addWidget(executable_input_widget) - - self.executable_input_widget = executable_input_widget - - studio_executables = ( - variant_entity["executables"][platform.system().lower()] - ) - if len(studio_executables) < 1: - return - - content_layout.addWidget(Separator(parent=self)) - content_layout.addWidget( - QtWidgets.QLabel("Studio paths:", self) - ) - - for item in studio_executables: - path_widget = QtWidgets.QLineEdit(content_widget) - path_widget.setText(item.value[0]) - path_widget.setEnabled(False) - content_layout.addWidget(path_widget) - - def set_value(self, value): - if not self.executable_input_widget: - return - - if not value: - value = {} - elif not isinstance(value, dict): - print("Got invalid value type {}. Expected {}".format( - type(value), dict - )) - value = {} - - executable_path = value.get("executable") - if not executable_path: - executable_path = "" - elif isinstance(executable_path, list): - print("Got list in executable path so using first item as value") - executable_path = executable_path[0] - - if not isinstance(executable_path, str): - executable_path = "" - print(( - "Got invalid value type of app executable {}. Expected {}" - ).format(type(value), str)) - - self.executable_input_widget.setText(executable_path) - - def settings_value(self): - if not self.executable_input_widget: - return None - value = self.executable_input_widget.text() - if not value: - return None - return {"executable": value} - - -class AppGroupWidget(QtWidgets.QWidget): - def __init__(self, group_entity, parent): - super(AppGroupWidget, self).__init__(parent) - - valid_variants = {} - for key, entity in group_entity["variants"].items(): - if entity["enabled"].value: - valid_variants[key] = entity - - group_label = group_entity.label - expading_widget = ExpandingWidget(group_label, self) - content_widget = QtWidgets.QWidget(expading_widget) - content_layout = QtWidgets.QVBoxLayout(content_widget) - content_layout.setContentsMargins(CHILD_OFFSET, 5, 0, 0) - - widgets_by_variant_name = {} - for variant_name, variant_entity in valid_variants.items(): - variant_widget = AppVariantWidget( - group_label, variant_entity, content_widget - ) - widgets_by_variant_name[variant_name] = variant_widget - content_layout.addWidget(variant_widget) - - expading_widget.set_content_widget(content_widget) - - layout = QtWidgets.QHBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - layout.addWidget(expading_widget) - - self.widgets_by_variant_name = widgets_by_variant_name - - def set_value(self, value): - if not value: - value = {} - - for variant_name, widget in self.widgets_by_variant_name.items(): - widget.set_value(value.get(variant_name)) - - def settings_value(self): - output = {} - for variant_name, widget in self.widgets_by_variant_name.items(): - value = widget.settings_value() - if value: - output[variant_name] = value - - if not output: - return None - return output - - -class LocalApplicationsWidgets(QtWidgets.QWidget): - def __init__(self, system_settings_entity, parent): - super(LocalApplicationsWidgets, self).__init__(parent) - - widgets_by_group_name = {} - - layout = QtWidgets.QVBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - - for key, entity in system_settings_entity["applications"].items(): - # Filter not enabled app groups - if not entity["enabled"].value: - continue - - # Check if has enabled any variant - enabled_variant = False - for variant_entity in entity["variants"].values(): - if variant_entity["enabled"].value: - enabled_variant = True - break - - if not enabled_variant: - continue - - # Create App group specific widget and store it by the key - group_widget = AppGroupWidget(entity, self) - widgets_by_group_name[key] = group_widget - layout.addWidget(group_widget) - - self.widgets_by_group_name = widgets_by_group_name - - def set_value(self, value): - if not value: - value = {} - - for group_name, widget in self.widgets_by_group_name.items(): - widget.set_value(value.get(group_name)) - - def settings_value(self): - output = {} - for group_name, widget in self.widgets_by_group_name.items(): - value = widget.settings_value() - if value: - output[group_name] = value - if not output: - return None - return output - - -class RootsWidget(QtWidgets.QWidget): - value_changed = QtCore.Signal() - - def __init__(self, project_settings, parent): - self._parent_widget = parent - super(RootsWidget, self).__init__(parent) - - self.project_settings = project_settings - self.widgts_by_root_name = {} - - main_layout = QtWidgets.QVBoxLayout(self) - - self.content_layout = main_layout - - def _on_root_value_change(self): - self.value_changed.emit() - - def refresh(self): - while self.content_layout.count(): - item = self.content_layout.itemAt(0) - item.widget().hide() - self.content_layout.removeItem(item) - - self.widgts_by_root_name.clear() - - default_root_values = self.local_default_project_values() or {} - - roots_entity = ( - self.project_settings[PROJECT_ANATOMY_KEY][LOCAL_ROOTS_KEY] - ) - is_in_default = self.project_settings.project_name is None - for root_name, path_entity in roots_entity.items(): - platform_entity = path_entity[platform.system().lower()] - root_widget = QtWidgets.QWidget(self) - - key_label = QtWidgets.QLabel(root_name, root_widget) - - root_input_widget = QtWidgets.QWidget(root_widget) - root_input_layout = QtWidgets.QVBoxLayout(root_input_widget) - - value_input = QtWidgets.QLineEdit(root_input_widget) - placeholder = None - if not is_in_default: - placeholder = default_root_values.get(root_name) - if placeholder: - placeholder = "< {} >".format(placeholder) - - if not placeholder: - placeholder = platform_entity.value - value_input.setPlaceholderText(placeholder) - value_input.textChanged.connect(self._on_root_value_change) - - root_input_layout.addWidget(value_input) - - root_layout = QtWidgets.QHBoxLayout(root_widget) - root_layout.addWidget(key_label) - root_layout.addWidget(root_input_widget) - - self.content_layout.addWidget(root_widget) - self.widgts_by_root_name[root_name] = value_input - - self.content_layout.addWidget(SpacerWidget(self), 1) - - def local_default_project_values(self): - default_project = self._parent_widget.per_project_settings.get(None) - if default_project: - return default_project.get(LOCAL_ROOTS_KEY) - return None - - def set_value(self, value): - if not value: - value = {} - - for root_name, widget in self.widgts_by_root_name.items(): - root_value = value.get(root_name) or "" - widget.setText(root_value) - - def settings_value(self): - output = {} - for root_name, widget in self.widgts_by_root_name.items(): - value = widget.text() - if value: - output[root_name] = value - if not output: - return None - return output - - -class ProjectSpecificWidget(QtWidgets.QWidget): - value_changed = QtCore.Signal() - - def __init__(self, project_settings, parent): - self._parent_widget = parent - super(ProjectSpecificWidget, self).__init__(parent) - - self.project_settings = project_settings - self.widgts_by_root_name = {} - - sites_widget = QtWidgets.QWidget(self) - sites_layout = QtWidgets.QHBoxLayout(sites_widget) - active_site_combo = QtWidgets.QComboBox(sites_widget) - remote_site_combo = QtWidgets.QComboBox(sites_widget) - sites_layout.addWidget(QtWidgets.QLabel("Active Site", sites_widget)) - sites_layout.addWidget(active_site_combo) - sites_layout.addWidget(QtWidgets.QLabel("Remote Site", sites_widget)) - sites_layout.addWidget(remote_site_combo) - - main_layout = QtWidgets.QVBoxLayout(self) - main_layout.addWidget(sites_widget) - main_layout.addWidget(SpacerWidget(self), 1) - - def set_value(self, value): - pass - # if not value: - # value = {} - # - # for root_name, widget in self.widgts_by_root_name.items(): - # root_value = value.get(root_name) or "" - # widget.setText(root_value) - - def settings_value(self): - return {} - # output = {} - # for root_name, widget in self.widgts_by_root_name.items(): - # value = widget.text() - # if value: - # output[root_name] = value - # if not output: - # return None - # return output - - def change_project(self, project_name): - pass - - -class _ProjectListWidget(ProjectListWidget): - 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 - - self.select_project(new_project_name) - self.current_project = new_project_name - self.project_changed.emit() - - -class ProjectSettingsWidget(QtWidgets.QWidget): - def __init__(self, project_settings, parent): - super(ProjectSettingsWidget, self).__init__(parent) - - self.per_project_settings = {} - - projects_widget = _ProjectListWidget(self) - project_specific_widget = ProjectSpecificWidget(project_settings, self) - - main_layout = QtWidgets.QHBoxLayout(self) - main_layout.setContentsMargins(0, 0, 0, 0) - main_layout.addWidget(projects_widget, 0) - main_layout.addWidget(project_specific_widget, 1) - - projects_widget.project_changed.connect(self._on_project_change) - # project_specific_widget.value_changed.connect( - # self._on_root_value_change - # ) - - self.project_settings = project_settings - - self.projects_widget = projects_widget - self.project_specific_widget = project_specific_widget - - def _current_value(self): - roots_value = self.project_specific_widget.settings_value() - current_value = {} - if roots_value: - current_value[LOCAL_ROOTS_KEY] = roots_value - return current_value - - def project_name(self): - return self.projects_widget.project_name() - - def _on_project_change(self): - project_name = self.project_name() - - self.project_settings.change_project(project_name) - self.project_specific_widget.change_project(project_name) - - def _on_root_value_change(self): - self.per_project_settings[self.project_name()] = ( - self._current_value() - ) - - def set_value(self, value): - if not value: - value = {} - self.per_project_settings = value - - self.projects_widget.refresh() - - self.project_specific_widget.set_value(self.per_project_settings) - - def settings_value(self): - output = {} - for project_name, value in self.per_project_settings.items(): - if value: - output[project_name] = value - if not output: - return None - return output - - class LocalSettingsWidget(QtWidgets.QWidget): def __init__(self, parent=None): super(LocalSettingsWidget, self).__init__(parent) From f061e604296398c853733351856695ee19563e7f Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 24 Feb 2021 16:42:21 +0100 Subject: [PATCH 61/91] fixed smaller bugs --- pype/tools/settings/local_settings/projects_widget.py | 3 +++ pype/tools/settings/local_settings/window.py | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pype/tools/settings/local_settings/projects_widget.py b/pype/tools/settings/local_settings/projects_widget.py index 4426422299..d6468cc0af 100644 --- a/pype/tools/settings/local_settings/projects_widget.py +++ b/pype/tools/settings/local_settings/projects_widget.py @@ -1,8 +1,11 @@ +import platform from Qt import QtWidgets, QtCore from pype.tools.settings.settings import ProjectListWidget from .widgets import SpacerWidget from pype.settings.constants import PROJECT_ANATOMY_KEY +LOCAL_ROOTS_KEY = "roots" + class _ProjectListWidget(ProjectListWidget): def on_item_clicked(self, new_index): diff --git a/pype/tools/settings/local_settings/window.py b/pype/tools/settings/local_settings/window.py index 1fd2ca8e06..80ad811b60 100644 --- a/pype/tools/settings/local_settings/window.py +++ b/pype/tools/settings/local_settings/window.py @@ -24,11 +24,9 @@ from .projects_widget import ProjectSettingsWidget log = logging.getLogger(__name__) - LOCAL_GENERAL_KEY = "general" LOCAL_PROJECTS_KEY = "projects" LOCAL_APPS_KEY = "applications" -LOCAL_ROOTS_KEY = "roots" class LocalGeneralWidgets(QtWidgets.QWidget): From 8ba54577fbf85e11b51e7ad4a2d9cb87348f7a60 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 24 Feb 2021 16:50:23 +0100 Subject: [PATCH 62/91] cleaned up projects widget --- .../local_settings/projects_widget.py | 84 +++++-------------- 1 file changed, 21 insertions(+), 63 deletions(-) diff --git a/pype/tools/settings/local_settings/projects_widget.py b/pype/tools/settings/local_settings/projects_widget.py index d6468cc0af..ada8da6bc1 100644 --- a/pype/tools/settings/local_settings/projects_widget.py +++ b/pype/tools/settings/local_settings/projects_widget.py @@ -29,6 +29,7 @@ class RootsWidget(QtWidgets.QWidget): super(RootsWidget, self).__init__(parent) self.project_settings = project_settings + self.local_project_settings = local_project_settings self.widgts_by_root_name = {} main_layout = QtWidgets.QVBoxLayout(self) @@ -90,31 +91,16 @@ class RootsWidget(QtWidgets.QWidget): return default_project.get(LOCAL_ROOTS_KEY) return None - def set_value(self, value): - if not value: - value = {} - - for root_name, widget in self.widgts_by_root_name.items(): - root_value = value.get(root_name) or "" - widget.setText(root_value) - - def settings_value(self): - output = {} - for root_name, widget in self.widgts_by_root_name.items(): - value = widget.text() - if value: - output[root_name] = value - if not output: - return None - return output + def set_value(self, local_project_settings): + self.local_project_settings = local_project_settings -class ProjectSpecificWidget(QtWidgets.QWidget): +class RootSiteWidget(QtWidgets.QWidget): value_changed = QtCore.Signal() def __init__(self, project_settings, parent): self._parent_widget = parent - super(ProjectSpecificWidget, self).__init__(parent) + super(RootSiteWidget, self).__init__(parent) self.project_settings = project_settings self.widgts_by_root_name = {} @@ -132,87 +118,59 @@ class ProjectSpecificWidget(QtWidgets.QWidget): main_layout.addWidget(sites_widget) main_layout.addWidget(SpacerWidget(self), 1) - def set_value(self, value): - pass - # if not value: - # value = {} - # - # for root_name, widget in self.widgts_by_root_name.items(): - # root_value = value.get(root_name) or "" - # widget.setText(root_value) - - def settings_value(self): - return {} - # output = {} - # for root_name, widget in self.widgts_by_root_name.items(): - # value = widget.text() - # if value: - # output[root_name] = value - # if not output: - # return None - # return output + def set_value(self, local_project_settings): + self.local_project_settings = local_project_settings def change_project(self, project_name): + # TODO update values pass +class ProjectValue(dict): + pass + + class ProjectSettingsWidget(QtWidgets.QWidget): def __init__(self, project_settings, parent): super(ProjectSettingsWidget, self).__init__(parent) - self.per_project_settings = {} + self.local_project_settings = {} projects_widget = _ProjectListWidget(self) - project_specific_widget = ProjectSpecificWidget(project_settings, self) + roos_site_widget = RootSiteWidget(project_settings, self) main_layout = QtWidgets.QHBoxLayout(self) main_layout.setContentsMargins(0, 0, 0, 0) main_layout.addWidget(projects_widget, 0) - main_layout.addWidget(project_specific_widget, 1) + main_layout.addWidget(roos_site_widget, 1) projects_widget.project_changed.connect(self._on_project_change) - # project_specific_widget.value_changed.connect( - # self._on_root_value_change - # ) self.project_settings = project_settings self.projects_widget = projects_widget - self.project_specific_widget = project_specific_widget - - def _current_value(self): - roots_value = self.project_specific_widget.settings_value() - current_value = {} - if roots_value: - current_value[LOCAL_ROOTS_KEY] = roots_value - return current_value + self.roos_site_widget = roos_site_widget def project_name(self): return self.projects_widget.project_name() def _on_project_change(self): project_name = self.project_name() - self.project_settings.change_project(project_name) - self.project_specific_widget.change_project(project_name) - - def _on_root_value_change(self): - self.per_project_settings[self.project_name()] = ( - self._current_value() - ) + self.roos_site_widget.change_project(project_name) def set_value(self, value): if not value: value = {} - self.per_project_settings = value + self.local_project_settings = ProjectValue(value) + + self.roos_site_widget.set_value(self.local_project_settings) self.projects_widget.refresh() - self.project_specific_widget.set_value(self.per_project_settings) - def settings_value(self): output = {} - for project_name, value in self.per_project_settings.items(): + for project_name, value in self.local_project_settings.items(): if value: output[project_name] = value if not output: From 367b6c8eec99f60a8ba7170dadfb93c2e383e258 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 24 Feb 2021 17:11:50 +0100 Subject: [PATCH 63/91] prepare structure for project overrides of active/remove site --- .../local_settings/projects_widget.py | 75 +++++++++++++++---- 1 file changed, 61 insertions(+), 14 deletions(-) diff --git a/pype/tools/settings/local_settings/projects_widget.py b/pype/tools/settings/local_settings/projects_widget.py index ada8da6bc1..831dbfcbd7 100644 --- a/pype/tools/settings/local_settings/projects_widget.py +++ b/pype/tools/settings/local_settings/projects_widget.py @@ -1,8 +1,8 @@ import platform from Qt import QtWidgets, QtCore from pype.tools.settings.settings import ProjectListWidget -from .widgets import SpacerWidget from pype.settings.constants import PROJECT_ANATOMY_KEY +from .widgets import SpacerWidget LOCAL_ROOTS_KEY = "roots" @@ -25,28 +25,32 @@ class RootsWidget(QtWidgets.QWidget): value_changed = QtCore.Signal() def __init__(self, project_settings, parent): - self._parent_widget = parent super(RootsWidget, self).__init__(parent) self.project_settings = project_settings - self.local_project_settings = local_project_settings + self.local_project_settings = {} self.widgts_by_root_name = {} + self._project_name = None + self._site_name = None - main_layout = QtWidgets.QVBoxLayout(self) + self.content_layout = QtWidgets.QVBoxLayout(self) - self.content_layout = main_layout + def _on_root_value_change(self, root_key): + print("root value or key {} changed".format(root_key)) - def _on_root_value_change(self): - self.value_changed.emit() - - def refresh(self): + def _clear_widgets(self): while self.content_layout.count(): item = self.content_layout.itemAt(0) item.widget().hide() self.content_layout.removeItem(item) - self.widgts_by_root_name.clear() + def refresh(self): + self._clear_widgets() + + if self._project_name is None or self._site_name is None: + return + default_root_values = self.local_default_project_values() or {} roots_entity = ( @@ -72,7 +76,11 @@ class RootsWidget(QtWidgets.QWidget): if not placeholder: placeholder = platform_entity.value value_input.setPlaceholderText(placeholder) - value_input.textChanged.connect(self._on_root_value_change) + + def _on_root_change(): + self._on_root_value_change(root_name) + + value_input.textChanged.connect(_on_root_change) root_input_layout.addWidget(value_input) @@ -86,7 +94,7 @@ class RootsWidget(QtWidgets.QWidget): self.content_layout.addWidget(SpacerWidget(self), 1) def local_default_project_values(self): - default_project = self._parent_widget.per_project_settings.get(None) + default_project = self.local_project_settings.get(None) if default_project: return default_project.get(LOCAL_ROOTS_KEY) return None @@ -94,6 +102,14 @@ class RootsWidget(QtWidgets.QWidget): def set_value(self, local_project_settings): self.local_project_settings = local_project_settings + def change_site(self, site_name): + self._site_name = site_name + self.refresh() + + def change_project(self, project_name): + self._project_name = project_name + self.refresh() + class RootSiteWidget(QtWidgets.QWidget): value_changed = QtCore.Signal() @@ -104,6 +120,7 @@ class RootSiteWidget(QtWidgets.QWidget): self.project_settings = project_settings self.widgts_by_root_name = {} + self._project_name = None sites_widget = QtWidgets.QWidget(self) sites_layout = QtWidgets.QHBoxLayout(sites_widget) @@ -114,16 +131,46 @@ class RootSiteWidget(QtWidgets.QWidget): sites_layout.addWidget(QtWidgets.QLabel("Remote Site", sites_widget)) sites_layout.addWidget(remote_site_combo) + roots_widget = RootsWidget(project_settings, self) + main_layout = QtWidgets.QVBoxLayout(self) main_layout.addWidget(sites_widget) + main_layout.addWidget(roots_widget) main_layout.addWidget(SpacerWidget(self), 1) + self.active_site_combo = active_site_combo + self.remote_site_combo = remote_site_combo + self.roots_widget = roots_widget + + def _fill_active_site_combo(self): + if self._project_name is None: + return + + def _fill_remote_site_combo(self): + if self._project_name is None: + return + + def _change_combobox_values(self): + # Set sites from local settings in comboboxes + pass + def set_value(self, local_project_settings): self.local_project_settings = local_project_settings + def _change_active_site(self, site_name): + self.roots_widget.change_site(site_name) + def change_project(self, project_name): - # TODO update values - pass + self._project_name = project_name + # Set roots project to None so all changes below are ignored + self.roots_widget.change_project(None) + + self._fill_active_site_combo() + self._fill_remote_site_combo() + self._change_combobox_values() + + # Change project name in roots widget + self.roots_widget.change_project(project_name) class ProjectValue(dict): From ff1a694cad98936a8a9b41f673c9ecd6137e733f Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 24 Feb 2021 17:53:02 +0100 Subject: [PATCH 64/91] added temporary fix of application executables --- pype/settings/lib.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pype/settings/lib.py b/pype/settings/lib.py index 24e16dd0ff..fb0bb733c9 100644 --- a/pype/settings/lib.py +++ b/pype/settings/lib.py @@ -363,7 +363,10 @@ def apply_local_settings_on_system_settings(system_settings, local_settings): platform_executables = variants[app_name]["executables"].get( current_platform ) - new_executables = [executable] + # TODO This is temporary fix until launch arguments will be stored + # per platform and not per executable. + # - local settings store only executable + new_executables = [[executable, ""]] new_executables.extend(platform_executables) variants[app_name]["executables"] = new_executables From 7b6d0d20df354c84d2dc66324d0c95af066924cf Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 24 Feb 2021 17:53:33 +0100 Subject: [PATCH 65/91] default project is stored under constant key in local settings --- pype/settings/constants.py | 1 + pype/settings/handlers.py | 20 -------------------- pype/settings/lib.py | 7 ++++--- 3 files changed, 5 insertions(+), 23 deletions(-) diff --git a/pype/settings/constants.py b/pype/settings/constants.py index fcc3a1d2dc..ce19ad3f93 100644 --- a/pype/settings/constants.py +++ b/pype/settings/constants.py @@ -17,6 +17,7 @@ PROJECT_SETTINGS_KEY = "project_settings" PROJECT_ANATOMY_KEY = "project_anatomy" LOCAL_SETTING_KEY = "local_settings" +DEFAULT_PROJECT_KEY = "__default_project__" __all__ = ( "M_OVERRIDEN_KEY", diff --git a/pype/settings/handlers.py b/pype/settings/handlers.py index 135201048a..89f9645be7 100644 --- a/pype/settings/handlers.py +++ b/pype/settings/handlers.py @@ -526,13 +526,7 @@ class MongoLocalSettingsHandler(LocalSettingsHandler): Data have 2 query criteria. First is key "type" stored in constant `LOCAL_SETTING_KEY`. Second is key "site_id" which value can be obstained with `get_local_site_id` function. - - Because project specific values are stored by project name the default - project would have key `None` which is not allowed. Because of that is - `None` replaced with `default_project_key` value on save and the key - is replaced with `None` of load. """ - default_project_key = "__default_project__" def __init__(self, local_site_id=None): # Get mongo connection @@ -571,12 +565,6 @@ class MongoLocalSettingsHandler(LocalSettingsHandler): """ data = data or {} - # Replace key `None` (default project values) with constant string - if "projects" in data and None in data["projects"]: - data["projects"][self.default_project_key] = ( - data["projects"].pop(None) - ) - self.local_settings_cache.update_data(data) self.collection.replace_one( @@ -601,13 +589,5 @@ class MongoLocalSettingsHandler(LocalSettingsHandler): }) self.local_settings_cache.update_from_document(document) - data = self.local_settings_cache.data - if ( - "projects" in data - and self.default_project_key in data["projects"] - ): - data["projects"][None] = data["projects"].pop( - self.default_project_key - ) return self.local_settings_cache.data_copy() diff --git a/pype/settings/lib.py b/pype/settings/lib.py index fb0bb733c9..1fbcbeabb8 100644 --- a/pype/settings/lib.py +++ b/pype/settings/lib.py @@ -12,7 +12,8 @@ from .constants import ( SYSTEM_SETTINGS_KEY, PROJECT_SETTINGS_KEY, - PROJECT_ANATOMY_KEY + PROJECT_ANATOMY_KEY, + DEFAULT_PROJECT_KEY ) log = logging.getLogger(__name__) @@ -402,7 +403,7 @@ def apply_local_settings_on_anatomy_settings( return current_platform = platform.system().lower() - local_defaults = local_project_settings.get(None) + local_defaults = local_project_settings.get(DEFAULT_PROJECT_KEY) root_data = anatomy_settings["roots"] if local_defaults and "roots" in local_defaults: for root_name, path in local_defaults["roots"].items(): @@ -411,7 +412,7 @@ def apply_local_settings_on_anatomy_settings( anatomy_settings["roots"][root_name][current_platform] = ( path ) - if project_name is None: + if project_name == DEFAULT_PROJECT_KEY: return local_projects = local_project_settings.get(project_name) From 60e36787dba0e4dcaa3fcb03f99f15a506ccd385 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 24 Feb 2021 18:03:14 +0100 Subject: [PATCH 66/91] fill comboboxes with sites and added ability to define project settings --- .../local_settings/projects_widget.py | 125 ++++++++++++++---- 1 file changed, 100 insertions(+), 25 deletions(-) diff --git a/pype/tools/settings/local_settings/projects_widget.py b/pype/tools/settings/local_settings/projects_widget.py index 831dbfcbd7..303bb39e3d 100644 --- a/pype/tools/settings/local_settings/projects_widget.py +++ b/pype/tools/settings/local_settings/projects_widget.py @@ -1,7 +1,10 @@ import platform from Qt import QtWidgets, QtCore from pype.tools.settings.settings import ProjectListWidget -from pype.settings.constants import PROJECT_ANATOMY_KEY +from pype.settings.constants import ( + PROJECT_ANATOMY_KEY, + DEFAULT_PROJECT_KEY +) from .widgets import SpacerWidget LOCAL_ROOTS_KEY = "roots" @@ -20,6 +23,11 @@ class _ProjectListWidget(ProjectListWidget): self.current_project = new_project_name self.project_changed.emit() + def project_name(self): + if self.current_project == self.default: + return DEFAULT_PROJECT_KEY + return self.current_project + class RootsWidget(QtWidgets.QWidget): value_changed = QtCore.Signal() @@ -51,22 +59,27 @@ class RootsWidget(QtWidgets.QWidget): if self._project_name is None or self._site_name is None: return - default_root_values = self.local_default_project_values() or {} + default_root_values = self._get_site_value_for_project( + DEFAULT_PROJECT_KEY + ) + if self._project_name == DEFAULT_PROJECT_KEY: + project_root_values = default_root_values + else: + project_root_values = self._get_site_value_for_project( + self._project_name + ) roots_entity = ( self.project_settings[PROJECT_ANATOMY_KEY][LOCAL_ROOTS_KEY] ) - is_in_default = self.project_settings.project_name is None + is_in_default = self._project_name == DEFAULT_PROJECT_KEY for root_name, path_entity in roots_entity.items(): platform_entity = path_entity[platform.system().lower()] root_widget = QtWidgets.QWidget(self) key_label = QtWidgets.QLabel(root_name, root_widget) - - root_input_widget = QtWidgets.QWidget(root_widget) - root_input_layout = QtWidgets.QVBoxLayout(root_input_widget) - - value_input = QtWidgets.QLineEdit(root_input_widget) + value_input = QtWidgets.QLineEdit(root_widget) + # Placeholder placeholder = None if not is_in_default: placeholder = default_root_values.get(root_name) @@ -75,29 +88,36 @@ class RootsWidget(QtWidgets.QWidget): if not placeholder: placeholder = platform_entity.value + value_input.setPlaceholderText(placeholder) + # Root value + project_value = project_root_values.get(root_name) + if project_value: + value_input.setText(project_value) + + # Register change callback def _on_root_change(): self._on_root_value_change(root_name) value_input.textChanged.connect(_on_root_change) - root_input_layout.addWidget(value_input) - root_layout = QtWidgets.QHBoxLayout(root_widget) root_layout.addWidget(key_label) - root_layout.addWidget(root_input_widget) + root_layout.addWidget(value_input) self.content_layout.addWidget(root_widget) self.widgts_by_root_name[root_name] = value_input self.content_layout.addWidget(SpacerWidget(self), 1) - def local_default_project_values(self): - default_project = self.local_project_settings.get(None) + def _get_site_value_for_project(self, project_name): + default_project = self.local_project_settings.get(project_name) if default_project: - return default_project.get(LOCAL_ROOTS_KEY) - return None + root_value = default_project.get(LOCAL_ROOTS_KEY) + if root_value: + return root_value.get(self._site_name) or {} + return {} def set_value(self, local_project_settings): self.local_project_settings = local_project_settings @@ -119,7 +139,6 @@ class RootSiteWidget(QtWidgets.QWidget): super(RootSiteWidget, self).__init__(parent) self.project_settings = project_settings - self.widgts_by_root_name = {} self._project_name = None sites_widget = QtWidgets.QWidget(self) @@ -142,17 +161,74 @@ class RootSiteWidget(QtWidgets.QWidget): self.remote_site_combo = remote_site_combo self.roots_widget = roots_widget - def _fill_active_site_combo(self): - if self._project_name is None: - return + def _active_site_values(self): + global_entity = self.project_settings["project_settings"]["global"] + sites_entity = global_entity["sync_server"]["sites"] + return tuple(sites_entity.keys()) - def _fill_remote_site_combo(self): - if self._project_name is None: - return + def _remote_site_values(self): + global_entity = self.project_settings["project_settings"]["global"] + sites_entity = global_entity["sync_server"]["sites"] + return tuple(sites_entity.keys()) def _change_combobox_values(self): + self.active_site_combo.clear() + self.remote_site_combo.clear() + if self._project_name is None: + return + + active_site_values = self._active_site_values() + remote_site_values = self._remote_site_values() + # Set sites from local settings in comboboxes - pass + active_site = None + remote_site = None + + project_values = self.local_project_settings.get(self._project_name) + if project_values: + active_site = project_values.get("active_site") + remote_site = project_values.get("remote_site") + + if ( + (not active_site or not remote_site) + and self._project_name is not DEFAULT_PROJECT_KEY + ): + default_values = self.local_project_settings.get( + DEFAULT_PROJECT_KEY + ) + if default_values: + if not active_site: + active_site = default_values.get("active_site") + if not remote_site: + remote_site = default_values.get("remote_site") + + self.active_site_combo.addItems(active_site_values) + self.remote_site_combo.addItems(remote_site_values) + + # Find and set remote site in combobox + if remote_site: + idx = self.remote_site_combo.findText(active_site) + if idx >= 0: + index = self.remote_site_combo.model().index(idx, 0) + self.remote_site_combo.setCurrentIndex(index) + + # Find and set active site in combobox + if active_site: + idx = self.active_site_combo.findText(active_site) + if idx < 0: + active_site = None + else: + index = self.active_site_combo.model().index(idx, 0) + self.active_site_combo.setCurrentIndex(index) + + # Prepare value to change active site in roots widget + if not active_site: + if not active_site_values: + active_site = None + else: + active_site = self.active_site_combo.currentText() + + self._change_active_site(active_site) def set_value(self, local_project_settings): self.local_project_settings = local_project_settings @@ -165,8 +241,7 @@ class RootSiteWidget(QtWidgets.QWidget): # Set roots project to None so all changes below are ignored self.roots_widget.change_project(None) - self._fill_active_site_combo() - self._fill_remote_site_combo() + # Aply changes in site comboboxes self._change_combobox_values() # Change project name in roots widget From 13053fad6ef8ed5a33b2b8e5db3197ecbff9fc8d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 24 Feb 2021 18:34:43 +0100 Subject: [PATCH 67/91] roots are handled in specific widget --- .../local_settings/projects_widget.py | 159 ++++++++++++------ 1 file changed, 108 insertions(+), 51 deletions(-) diff --git a/pype/tools/settings/local_settings/projects_widget.py b/pype/tools/settings/local_settings/projects_widget.py index 303bb39e3d..e553bab965 100644 --- a/pype/tools/settings/local_settings/projects_widget.py +++ b/pype/tools/settings/local_settings/projects_widget.py @@ -1,4 +1,5 @@ import platform +import copy from Qt import QtWidgets, QtCore from pype.tools.settings.settings import ProjectListWidget from pype.settings.constants import ( @@ -29,6 +30,97 @@ class _ProjectListWidget(ProjectListWidget): return self.current_project +class RootInputWidget(QtWidgets.QWidget): + def __init__( + self, + local_project_settings, + local_project_settings_orig, + platform_root_entity, + root_name, + project_name, + site_name, + parent + ): + super(RootInputWidget, self).__init__(parent) + + self.local_project_settings = local_project_settings + self.local_project_settings_orig = local_project_settings_orig + self.platform_root_entity = platform_root_entity + self.root_name = root_name + self.site_name = site_name + self.project_name = project_name + + self.origin_value = self._get_site_value_for_project( + self.project_name, self.local_project_settings_orig + ) + + key_label = QtWidgets.QLabel(root_name, self) + value_input = QtWidgets.QLineEdit(self) + + is_default_project = bool(project_name == DEFAULT_PROJECT_KEY) + + default_input_value = self._get_site_value_for_project( + DEFAULT_PROJECT_KEY + ) + if is_default_project: + project_value = None + input_value = default_input_value + else: + input_value = self._get_site_value_for_project(self.project_name) + project_value = input_value + + # Placeholder + placeholder = None + if not is_default_project: + placeholder = default_input_value + + if not placeholder: + placeholder = platform_root_entity.value + + value_input.setPlaceholderText("< {} >".format(placeholder)) + + # Root value + if input_value: + value_input.setText(input_value) + + value_input.textChanged.connect(self._on_value_change) + + root_layout = QtWidgets.QHBoxLayout(self) + root_layout.addWidget(key_label) + root_layout.addWidget(value_input) + + self.value_input = value_input + + self.is_default_project = is_default_project + self.studio_value = platform_root_entity.value + self.default_value = default_input_value + self.project_value = project_value + + @property + def is_modified(self): + return self.origin_value != self.value_input.text() + + def _get_site_value_for_project(self, project_name, data=None): + if data is None: + data = self.local_project_settings + project_values = data.get(project_name) + site_value = {} + if project_values: + root_value = project_values.get(LOCAL_ROOTS_KEY) + if root_value: + site_value = root_value.get(self.site_name) or {} + return site_value.get(self.root_name) + + def _on_value_change(self): + value = self.value_input.text() + data = self.local_project_settings + for key in (self.project_name, LOCAL_ROOTS_KEY, self.site_name): + if key not in data: + data[key] = {} + data = data[key] + data[self.root_name] = value + + class RootsWidget(QtWidgets.QWidget): value_changed = QtCore.Signal() @@ -36,8 +128,9 @@ class RootsWidget(QtWidgets.QWidget): super(RootsWidget, self).__init__(parent) self.project_settings = project_settings - self.local_project_settings = {} self.widgts_by_root_name = {} + self.local_project_settings = None + self.local_project_settings_orig = None self._project_name = None self._site_name = None @@ -59,68 +152,31 @@ class RootsWidget(QtWidgets.QWidget): if self._project_name is None or self._site_name is None: return - default_root_values = self._get_site_value_for_project( - DEFAULT_PROJECT_KEY - ) - if self._project_name == DEFAULT_PROJECT_KEY: - project_root_values = default_root_values - else: - project_root_values = self._get_site_value_for_project( - self._project_name - ) - roots_entity = ( self.project_settings[PROJECT_ANATOMY_KEY][LOCAL_ROOTS_KEY] ) - is_in_default = self._project_name == DEFAULT_PROJECT_KEY for root_name, path_entity in roots_entity.items(): platform_entity = path_entity[platform.system().lower()] - root_widget = QtWidgets.QWidget(self) - - key_label = QtWidgets.QLabel(root_name, root_widget) - value_input = QtWidgets.QLineEdit(root_widget) - # Placeholder - placeholder = None - if not is_in_default: - placeholder = default_root_values.get(root_name) - if placeholder: - placeholder = "< {} >".format(placeholder) - - if not placeholder: - placeholder = platform_entity.value - - value_input.setPlaceholderText(placeholder) - - # Root value - project_value = project_root_values.get(root_name) - if project_value: - value_input.setText(project_value) - - # Register change callback - def _on_root_change(): - self._on_root_value_change(root_name) - - value_input.textChanged.connect(_on_root_change) - - root_layout = QtWidgets.QHBoxLayout(root_widget) - root_layout.addWidget(key_label) - root_layout.addWidget(value_input) + root_widget = RootInputWidget( + self.local_project_settings, + self.local_project_settings_orig, + platform_entity, + root_name, + self._project_name, + self._site_name, + self + ) self.content_layout.addWidget(root_widget) - self.widgts_by_root_name[root_name] = value_input + self.widgts_by_root_name[root_name] = root_widget self.content_layout.addWidget(SpacerWidget(self), 1) - def _get_site_value_for_project(self, project_name): - default_project = self.local_project_settings.get(project_name) - if default_project: - root_value = default_project.get(LOCAL_ROOTS_KEY) - if root_value: - return root_value.get(self._site_name) or {} - return {} - def set_value(self, local_project_settings): self.local_project_settings = local_project_settings + self.local_project_settings_orig = copy.deepcopy( + dict(local_project_settings) + ) def change_site(self, site_name): self._site_name = site_name @@ -232,6 +288,7 @@ class RootSiteWidget(QtWidgets.QWidget): def set_value(self, local_project_settings): self.local_project_settings = local_project_settings + self.roots_widget.set_value(local_project_settings) def _change_active_site(self, site_name): self.roots_widget.change_site(site_name) From 374b342bb42a3c8841dbb3f8ba966e98d19cdf1e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 25 Feb 2021 10:50:40 +0100 Subject: [PATCH 68/91] implemented comboboxes for sites --- .../local_settings/projects_widget.py | 237 +++++++++++------- 1 file changed, 153 insertions(+), 84 deletions(-) diff --git a/pype/tools/settings/local_settings/projects_widget.py b/pype/tools/settings/local_settings/projects_widget.py index e553bab965..6cc4e89682 100644 --- a/pype/tools/settings/local_settings/projects_widget.py +++ b/pype/tools/settings/local_settings/projects_widget.py @@ -24,11 +24,6 @@ class _ProjectListWidget(ProjectListWidget): self.current_project = new_project_name self.project_changed.emit() - def project_name(self): - if self.current_project == self.default: - return DEFAULT_PROJECT_KEY - return self.current_project - class RootInputWidget(QtWidgets.QWidget): def __init__( @@ -54,17 +49,14 @@ class RootInputWidget(QtWidgets.QWidget): self.project_name, self.local_project_settings_orig ) - key_label = QtWidgets.QLabel(root_name, self) - value_input = QtWidgets.QLineEdit(self) - is_default_project = bool(project_name == DEFAULT_PROJECT_KEY) default_input_value = self._get_site_value_for_project( DEFAULT_PROJECT_KEY ) if is_default_project: - project_value = None input_value = default_input_value + project_value = None else: input_value = self._get_site_value_for_project(self.project_name) project_value = input_value @@ -77,6 +69,8 @@ class RootInputWidget(QtWidgets.QWidget): if not placeholder: placeholder = platform_root_entity.value + key_label = QtWidgets.QLabel(root_name, self) + value_input = QtWidgets.QLineEdit(self) value_input.setPlaceholderText("< {} >".format(placeholder)) # Root value @@ -122,8 +116,6 @@ class RootInputWidget(QtWidgets.QWidget): class RootsWidget(QtWidgets.QWidget): - value_changed = QtCore.Signal() - def __init__(self, project_settings, parent): super(RootsWidget, self).__init__(parent) @@ -136,9 +128,6 @@ class RootsWidget(QtWidgets.QWidget): self.content_layout = QtWidgets.QVBoxLayout(self) - def _on_root_value_change(self, root_key): - print("root value or key {} changed".format(root_key)) - def _clear_widgets(self): while self.content_layout.count(): item = self.content_layout.itemAt(0) @@ -152,6 +141,10 @@ class RootsWidget(QtWidgets.QWidget): if self._project_name is None or self._site_name is None: return + # Site label + self.content_layout.addWidget(QtWidgets.QLabel(self._site_name, self)) + + # Root inputs roots_entity = ( self.project_settings[PROJECT_ANATOMY_KEY][LOCAL_ROOTS_KEY] ) @@ -170,6 +163,7 @@ class RootsWidget(QtWidgets.QWidget): self.content_layout.addWidget(root_widget) self.widgts_by_root_name[root_name] = root_widget + # Add spacer so other widgets are squeezed to top self.content_layout.addWidget(SpacerWidget(self), 1) def set_value(self, local_project_settings): @@ -187,9 +181,135 @@ class RootsWidget(QtWidgets.QWidget): self.refresh() -class RootSiteWidget(QtWidgets.QWidget): - value_changed = QtCore.Signal() +class _SiteCombobox(QtWidgets.QWidget): + site_changed = QtCore.Signal(str) + def __init__(self, project_settings, parent): + super(_SiteCombobox, self).__init__(parent) + self.project_settings = project_settings + + self.local_project_settings = None + self.local_project_settings_orig = None + self.project_name = None + self.is_default_project = None + + self.default_override_value = None + self.project_override_value = None + + label_widget = QtWidgets.QLabel(self) + combobox_input = QtWidgets.QComboBox(self) + + main_layout = QtWidgets.QHBoxLayout(self) + main_layout.addWidget(label_widget) + main_layout.addWidget(combobox_input) + + combobox_input.currentIndexChanged.connect(self._on_index_change) + self.label_widget = label_widget + self.combobox_input = combobox_input + + self._ui_tweaks() + + def set_value(self, local_project_settings): + self.local_project_settings = local_project_settings + self.local_project_settings_orig = copy.deepcopy( + dict(local_project_settings) + ) + + def change_project(self, project_name): + self.default_override_value = None + self.project_override_value = None + + self.project_name = None + self.combobox_input.clear() + if project_name is None: + return + + self.is_default_project = bool(project_name == DEFAULT_PROJECT_KEY) + site_items = self._get_project_sites() + self.combobox_input.addItems(site_items) + + default_item = self._get_local_settings_item(DEFAULT_PROJECT_KEY) + if self.is_default_project: + project_item = None + else: + project_item = self._get_local_settings_item(project_name) + + index = None + if project_item: + idx = self.combobox_input.findText(project_item) + if idx >= 0: + self.project_override_value = project_item + index = self.combobox_input.model().index(idx, 0) + + if default_item: + idx = self.combobox_input.findText(default_item) + if idx >= 0: + self.default_override_value = default_item + if not index: + index = self.combobox_input.model().index(idx, 0) + if index: + self.combobox_input.setCurrentIndex(index) + + self.project_name = project_name + self.site_changed.emit(self.combobox_input.currentText()) + + def _on_index_change(self): + if self.project_name is None: + return + self.site_changed.emit(self.combobox_input.currentText()) + print("here") + + def _ui_tweaks(self): + raise NotImplementedError("_ui_tweaks not implemented {}".format( + self.__class__.__name__ + )) + + def _get_project_sites(self): + raise NotImplementedError("_ui_tweaks not implemented {}".format( + self.__class__.__name__ + )) + + def _get_local_settings_item(self, project_name): + raise NotImplementedError("_ui_tweaks not implemented {}".format( + self.__class__.__name__ + )) + + +class AciveSiteCombo(_SiteCombobox): + def _ui_tweaks(self): + self.label_widget.setText("Active site") + + def _get_project_sites(self): + global_entity = self.project_settings["project_settings"]["global"] + sites_entity = global_entity["sync_server"]["sites"] + return tuple(sites_entity.keys()) + + def _get_local_settings_item(self, project_name): + project_values = self.local_project_settings.get(project_name) + value = None + if project_values: + value = project_values.get("active_site") + return value + + +class RemoteSiteCombo(_SiteCombobox): + def _ui_tweaks(self): + self.label_widget.setText("Remote site") + + def _get_project_sites(self): + global_entity = self.project_settings["project_settings"]["global"] + sites_entity = global_entity["sync_server"]["sites"] + return tuple(sites_entity.keys()) + + def _get_local_settings_item(self, project_name): + project_values = self.local_project_settings.get(project_name) + value = None + if project_values: + value = project_values.get("remote_site") + return value + + +class RootSiteWidget(QtWidgets.QWidget): def __init__(self, project_settings, parent): self._parent_widget = parent super(RootSiteWidget, self).__init__(parent) @@ -199,12 +319,11 @@ class RootSiteWidget(QtWidgets.QWidget): sites_widget = QtWidgets.QWidget(self) sites_layout = QtWidgets.QHBoxLayout(sites_widget) - active_site_combo = QtWidgets.QComboBox(sites_widget) - remote_site_combo = QtWidgets.QComboBox(sites_widget) - sites_layout.addWidget(QtWidgets.QLabel("Active Site", sites_widget)) - sites_layout.addWidget(active_site_combo) - sites_layout.addWidget(QtWidgets.QLabel("Remote Site", sites_widget)) - sites_layout.addWidget(remote_site_combo) + + active_site_widget = AciveSiteCombo(project_settings, sites_widget) + remote_site_widget = RemoteSiteCombo(project_settings, sites_widget) + sites_layout.addWidget(active_site_widget) + sites_layout.addWidget(remote_site_widget) roots_widget = RootsWidget(project_settings, self) @@ -213,8 +332,9 @@ class RootSiteWidget(QtWidgets.QWidget): main_layout.addWidget(roots_widget) main_layout.addWidget(SpacerWidget(self), 1) - self.active_site_combo = active_site_combo - self.remote_site_combo = remote_site_combo + active_site_widget.site_changed.connect(self._on_acite_site_change) + self.active_site_widget = active_site_widget + self.remote_site_widget = remote_site_widget self.roots_widget = roots_widget def _active_site_values(self): @@ -227,69 +347,15 @@ class RootSiteWidget(QtWidgets.QWidget): sites_entity = global_entity["sync_server"]["sites"] return tuple(sites_entity.keys()) - def _change_combobox_values(self): - self.active_site_combo.clear() - self.remote_site_combo.clear() - if self._project_name is None: - return - - active_site_values = self._active_site_values() - remote_site_values = self._remote_site_values() - - # Set sites from local settings in comboboxes - active_site = None - remote_site = None - - project_values = self.local_project_settings.get(self._project_name) - if project_values: - active_site = project_values.get("active_site") - remote_site = project_values.get("remote_site") - - if ( - (not active_site or not remote_site) - and self._project_name is not DEFAULT_PROJECT_KEY - ): - default_values = self.local_project_settings.get( - DEFAULT_PROJECT_KEY - ) - if default_values: - if not active_site: - active_site = default_values.get("active_site") - if not remote_site: - remote_site = default_values.get("remote_site") - - self.active_site_combo.addItems(active_site_values) - self.remote_site_combo.addItems(remote_site_values) - - # Find and set remote site in combobox - if remote_site: - idx = self.remote_site_combo.findText(active_site) - if idx >= 0: - index = self.remote_site_combo.model().index(idx, 0) - self.remote_site_combo.setCurrentIndex(index) - - # Find and set active site in combobox - if active_site: - idx = self.active_site_combo.findText(active_site) - if idx < 0: - active_site = None - else: - index = self.active_site_combo.model().index(idx, 0) - self.active_site_combo.setCurrentIndex(index) - - # Prepare value to change active site in roots widget - if not active_site: - if not active_site_values: - active_site = None - else: - active_site = self.active_site_combo.currentText() - - self._change_active_site(active_site) - def set_value(self, local_project_settings): self.local_project_settings = local_project_settings + self.active_site_widget.set_value(local_project_settings) + self.remote_site_widget.set_value(local_project_settings) self.roots_widget.set_value(local_project_settings) + def _on_acite_site_change(self, site_name): + self._change_active_site(site_name) + def _change_active_site(self, site_name): self.roots_widget.change_site(site_name) @@ -299,7 +365,8 @@ class RootSiteWidget(QtWidgets.QWidget): self.roots_widget.change_project(None) # Aply changes in site comboboxes - self._change_combobox_values() + self.active_site_widget.change_project(project_name) + self.remote_site_widget.change_project(project_name) # Change project name in roots widget self.roots_widget.change_project(project_name) @@ -336,6 +403,8 @@ class ProjectSettingsWidget(QtWidgets.QWidget): def _on_project_change(self): project_name = self.project_name() self.project_settings.change_project(project_name) + if project_name is None: + project_name = DEFAULT_PROJECT_KEY self.roos_site_widget.change_project(project_name) def set_value(self, value): From e17ae80de139014b80be856d55cf30be9672f365 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 25 Feb 2021 10:54:46 +0100 Subject: [PATCH 69/91] reduce margins and squeeze comboboxes --- pype/tools/settings/local_settings/projects_widget.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pype/tools/settings/local_settings/projects_widget.py b/pype/tools/settings/local_settings/projects_widget.py index 6cc4e89682..3122b47bc5 100644 --- a/pype/tools/settings/local_settings/projects_widget.py +++ b/pype/tools/settings/local_settings/projects_widget.py @@ -318,12 +318,15 @@ class RootSiteWidget(QtWidgets.QWidget): self._project_name = None sites_widget = QtWidgets.QWidget(self) - sites_layout = QtWidgets.QHBoxLayout(sites_widget) active_site_widget = AciveSiteCombo(project_settings, sites_widget) remote_site_widget = RemoteSiteCombo(project_settings, sites_widget) + + sites_layout = QtWidgets.QHBoxLayout(sites_widget) + sites_layout.setContentsMargins(0, 0, 0, 0) sites_layout.addWidget(active_site_widget) sites_layout.addWidget(remote_site_widget) + sites_layout.addWidget(SpacerWidget(self), 1) roots_widget = RootsWidget(project_settings, self) From bb49592adf95de2b99b6aea35fa6616e479216f7 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 25 Feb 2021 12:04:18 +0100 Subject: [PATCH 70/91] implemented proxy label for actions --- pype/tools/settings/local_settings/widgets.py | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/pype/tools/settings/local_settings/widgets.py b/pype/tools/settings/local_settings/widgets.py index ebb14a3102..e3c9590438 100644 --- a/pype/tools/settings/local_settings/widgets.py +++ b/pype/tools/settings/local_settings/widgets.py @@ -1,4 +1,4 @@ -from Qt import QtWidgets +from Qt import QtWidgets, QtCore from pype.tools.settings.settings.widgets.widgets import ( ExpandingWidget, SpacerWidget @@ -20,3 +20,29 @@ class Separator(QtWidgets.QFrame): layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(5, 5, 5, 5) layout.addWidget(splitter_item) + + +class ProxyLabelWidget(QtWidgets.QWidget): + def __init__(self, label, mouse_release_callback, parent=None): + super(ProxyLabelWidget, self).__init__(parent) + + self.mouse_release_callback = mouse_release_callback + + layout = QtWidgets.QVBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(0) + + label_widget = QtWidgets.QLabel(label, self) + layout.addWidget(label_widget) + + self.setAttribute(QtCore.Qt.WA_TranslucentBackground) + + self.label_widget = label_widget + + def setText(self, text): + self.label_widget.setText(text) + + def mouseReleaseEvent(self, event): + if self.mouse_release_callback: + return self.mouse_release_callback(event) + return super(ProxyLabelWidget, self).mouseReleaseEvent(event) From 6e377f8634832e478bd911f0b23dff6184cd0d46 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 25 Feb 2021 12:05:20 +0100 Subject: [PATCH 71/91] it is possible to set different sites and overrides should work --- .../local_settings/projects_widget.py | 206 +++++++++++++++--- 1 file changed, 181 insertions(+), 25 deletions(-) diff --git a/pype/tools/settings/local_settings/projects_widget.py b/pype/tools/settings/local_settings/projects_widget.py index 3122b47bc5..753732ab34 100644 --- a/pype/tools/settings/local_settings/projects_widget.py +++ b/pype/tools/settings/local_settings/projects_widget.py @@ -1,12 +1,15 @@ import platform import copy -from Qt import QtWidgets, QtCore +from Qt import QtWidgets, QtCore, QtGui from pype.tools.settings.settings import ProjectListWidget from pype.settings.constants import ( PROJECT_ANATOMY_KEY, DEFAULT_PROJECT_KEY ) -from .widgets import SpacerWidget +from .widgets import ( + SpacerWidget, + ProxyLabelWidget +) LOCAL_ROOTS_KEY = "roots" @@ -196,25 +199,138 @@ class _SiteCombobox(QtWidgets.QWidget): self.default_override_value = None self.project_override_value = None - label_widget = QtWidgets.QLabel(self) + label_widget = ProxyLabelWidget("", self._mouse_release_callback, self) combobox_input = QtWidgets.QComboBox(self) main_layout = QtWidgets.QHBoxLayout(self) main_layout.addWidget(label_widget) main_layout.addWidget(combobox_input) + # --- START --- DEBUG widget + self._debug_label = QtWidgets.QLabel("*", self) + main_layout.addWidget(self._debug_label) + # --- END --- + combobox_input.currentIndexChanged.connect(self._on_index_change) self.label_widget = label_widget self.combobox_input = combobox_input self._ui_tweaks() + def _set_current_text(self, text): + index = None + if text: + idx = self.combobox_input.findText(text) + if idx >= 0: + index = idx + + if index: + self.combobox_input.setCurrentIndex(index) + return True + return False + + def update_style(self): + if self.project_name is None: + self._debug_label.setText("None") + return + + current_value = self._get_local_settings_item(self.project_name) + orig_value = self._get_local_settings_item( + self.project_name, self.local_project_settings_orig + ) + if current_value and orig_value: + modified = current_value != orig_value + elif not current_value and not orig_value: + modified = False + else: + modified = True + + if modified: + self._debug_label.setText("Modified") + return + + if self.project_name == DEFAULT_PROJECT_KEY: + if not current_value: + self._debug_label.setText("No active site") + else: + self._debug_label.setText("Active default") + else: + if not current_value: + self._debug_label.setText("Default active site") + else: + self._debug_label.setText("Active project") + + def _mouse_release_callback(self, event): + if event.button() != QtCore.Qt.RightButton: + return + self._show_actions() + + def _remove_from_local(self): + if ( + self.project_name != DEFAULT_PROJECT_KEY + and self.default_override_value + ): + _project_name = self.project_name + self.project_name = None + self._set_current_text(self.default_override_value) + self.project_name = _project_name + + self._set_local_settings_value("") + self.update_style() + + def _add_to_local(self): + self._set_local_settings_value(self.current_text()) + self.update_style() + + def _add_actions(self, menu, actions_mapping): + # TODO better labels + if self.project_name == DEFAULT_PROJECT_KEY: + if self.default_override_value: + action = QtWidgets.QAction("Remove from default") + callback = self._remove_from_local + else: + action = QtWidgets.QAction("Add to default") + callback = self._add_to_local + else: + if self.project_override_value: + action = QtWidgets.QAction("Remove from project") + callback = self._remove_from_local + else: + action = QtWidgets.QAction("Add to project") + callback = self._add_to_local + + actions_mapping[action] = callback + menu.addAction(action) + + def _show_actions(self): + if self.project_name is None: + return + + menu = QtWidgets.QMenu(self) + actions_mapping = {} + + self._add_actions(menu, actions_mapping) + + 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 set_value(self, local_project_settings): self.local_project_settings = local_project_settings self.local_project_settings_orig = copy.deepcopy( dict(local_project_settings) ) + def current_text(self): + return self.combobox_input.currentText() + def change_project(self, project_name): self.default_override_value = None self.project_override_value = None @@ -222,6 +338,7 @@ class _SiteCombobox(QtWidgets.QWidget): self.project_name = None self.combobox_input.clear() if project_name is None: + self.update_style() return self.is_default_project = bool(project_name == DEFAULT_PROJECT_KEY) @@ -239,40 +356,54 @@ class _SiteCombobox(QtWidgets.QWidget): idx = self.combobox_input.findText(project_item) if idx >= 0: self.project_override_value = project_item - index = self.combobox_input.model().index(idx, 0) + index = idx if default_item: idx = self.combobox_input.findText(default_item) if idx >= 0: self.default_override_value = default_item - if not index: - index = self.combobox_input.model().index(idx, 0) + if index is None: + index = idx if index: self.combobox_input.setCurrentIndex(index) self.project_name = project_name - self.site_changed.emit(self.combobox_input.currentText()) + self.site_changed.emit(self.current_text()) + self.update_style() def _on_index_change(self): if self.project_name is None: return - self.site_changed.emit(self.combobox_input.currentText()) - print("here") + + self._set_local_settings_value(self.current_text()) + self.site_changed.emit(self.current_text()) + self.update_style() + + def _set_local_settings_value(self, value): + raise NotImplementedError( + "{} `_set_local_settings_value` not implemented".format( + self.__class__.__name__ + ) + ) def _ui_tweaks(self): - raise NotImplementedError("_ui_tweaks not implemented {}".format( + raise NotImplementedError("{} `_ui_tweaks` not implemented".format( self.__class__.__name__ )) def _get_project_sites(self): - raise NotImplementedError("_ui_tweaks not implemented {}".format( - self.__class__.__name__ - )) + raise NotImplementedError( + "{} `_get_project_sites` not implemented".format( + self.__class__.__name__ + ) + ) - def _get_local_settings_item(self, project_name): - raise NotImplementedError("_ui_tweaks not implemented {}".format( - self.__class__.__name__ - )) + def _get_local_settings_item(self, project_name, data=None): + raise NotImplementedError( + "{}`_get_local_settings_item` not implemented".format( + self.__class__.__name__ + ) + ) class AciveSiteCombo(_SiteCombobox): @@ -284,13 +415,20 @@ class AciveSiteCombo(_SiteCombobox): sites_entity = global_entity["sync_server"]["sites"] return tuple(sites_entity.keys()) - def _get_local_settings_item(self, project_name): - project_values = self.local_project_settings.get(project_name) + def _get_local_settings_item(self, project_name, data=None): + if data is None: + data = self.local_project_settings + project_values = data.get(project_name) value = None if project_values: value = project_values.get("active_site") return value + def _set_local_settings_value(self, value): + if self.project_name not in self.local_project_settings: + self.local_project_settings[self.project_name] = {} + self.local_project_settings[self.project_name]["active_site"] = value + class RemoteSiteCombo(_SiteCombobox): def _ui_tweaks(self): @@ -301,13 +439,20 @@ class RemoteSiteCombo(_SiteCombobox): sites_entity = global_entity["sync_server"]["sites"] return tuple(sites_entity.keys()) - def _get_local_settings_item(self, project_name): - project_values = self.local_project_settings.get(project_name) + def _get_local_settings_item(self, project_name, data=None): + if data is None: + data = self.local_project_settings + project_values = data.get(project_name) value = None if project_values: value = project_values.get("remote_site") return value + def _set_local_settings_value(self, value): + if self.project_name not in self.local_project_settings: + self.local_project_settings[self.project_name] = {} + self.local_project_settings[self.project_name]["remote_site"] = value + class RootSiteWidget(QtWidgets.QWidget): def __init__(self, project_settings, parent): @@ -419,11 +564,22 @@ class ProjectSettingsWidget(QtWidgets.QWidget): self.projects_widget.refresh() - def settings_value(self): + def _clear_value(self, value): + if not value: + return None + + if not isinstance(value, dict): + return value + output = {} - for project_name, value in self.local_project_settings.items(): - if value: - output[project_name] = value + for _key, _value in value.items(): + _modified_value = self._clear_value(_value) + if _modified_value: + output[_key] = _modified_value + return output + + def settings_value(self): + output = self._clear_value(self.local_project_settings) if not output: return None return output From 1cf3d924802829c6b059ab7a263e85c9203dc2bf Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 25 Feb 2021 12:21:46 +0100 Subject: [PATCH 72/91] changed site roots modification on anatomy data load --- pype/settings/lib.py | 43 ++++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/pype/settings/lib.py b/pype/settings/lib.py index 1fbcbeabb8..ad1292b326 100644 --- a/pype/settings/lib.py +++ b/pype/settings/lib.py @@ -402,28 +402,33 @@ def apply_local_settings_on_anatomy_settings( if not local_project_settings: return - current_platform = platform.system().lower() - local_defaults = local_project_settings.get(DEFAULT_PROJECT_KEY) - root_data = anatomy_settings["roots"] - if local_defaults and "roots" in local_defaults: - for root_name, path in local_defaults["roots"].items(): - if root_name not in root_data: - continue - anatomy_settings["roots"][root_name][current_platform] = ( - path - ) - if project_name == DEFAULT_PROJECT_KEY: + project_locals = local_project_settings.get(project_name) or {} + default_locals = local_project_settings.get(DEFAULT_PROJECT_KEY) or {} + active_site = project_locals.get("active_site") + if not active_site: + active_site = default_locals.get("active_site") + + if not active_site: return - local_projects = local_project_settings.get(project_name) - if local_projects and "roots" in local_projects: - for root_name, path in local_projects["roots"].items(): - if root_name not in root_data: - continue + roots_locals = default_locals.get("roots", {}).get(active_site, {}) + if project_name != DEFAULT_PROJECT_KEY: + roots_locals.update( + project_locals.get("roots", {}).get(active_site, {}) + ) - anatomy_settings["roots"][root_name][current_platform] = ( - path - ) + if not roots_locals: + return + + current_platform = platform.system().lower() + + root_data = anatomy_settings["roots"] + for root_name, path in roots_locals.items(): + if root_name not in root_data: + continue + anatomy_settings["roots"][root_name][current_platform] = ( + path + ) def get_system_settings(clear_metadata=True): From 724ba49bd1f7aea197ed6ef5794febceaac1abfe Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 25 Feb 2021 12:33:55 +0100 Subject: [PATCH 73/91] revert debug changes --- pype/tools/settings/__init__.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pype/tools/settings/__init__.py b/pype/tools/settings/__init__.py index 0a5d6933a1..3f47d1c2c3 100644 --- a/pype/tools/settings/__init__.py +++ b/pype/tools/settings/__init__.py @@ -22,8 +22,7 @@ def main(user_role=None): app = QtWidgets.QApplication(sys.argv) app.setWindowIcon(QtGui.QIcon(style.app_icon_path())) - # widget = MainWidget(user_role) - widget = LocalSettingsWindow() + widget = MainWidget(user_role) widget.show() widget.reset() From 054a79f6ee4a77b043cf859f68b0fb26e6cf6d83 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 25 Feb 2021 13:07:03 +0100 Subject: [PATCH 74/91] color changes work for site comboboxes --- .../local_settings/projects_widget.py | 40 ++++++++++--------- pype/tools/settings/local_settings/widgets.py | 4 ++ 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/pype/tools/settings/local_settings/projects_widget.py b/pype/tools/settings/local_settings/projects_widget.py index 753732ab34..d09c67af08 100644 --- a/pype/tools/settings/local_settings/projects_widget.py +++ b/pype/tools/settings/local_settings/projects_widget.py @@ -206,11 +206,6 @@ class _SiteCombobox(QtWidgets.QWidget): main_layout.addWidget(label_widget) main_layout.addWidget(combobox_input) - # --- START --- DEBUG widget - self._debug_label = QtWidgets.QLabel("*", self) - main_layout.addWidget(self._debug_label) - # --- END --- - combobox_input.currentIndexChanged.connect(self._on_index_change) self.label_widget = label_widget self.combobox_input = combobox_input @@ -229,10 +224,9 @@ class _SiteCombobox(QtWidgets.QWidget): return True return False - def update_style(self): + def _get_style_state(self): if self.project_name is None: - self._debug_label.setText("None") - return + return "" current_value = self._get_local_settings_item(self.project_name) orig_value = self._get_local_settings_item( @@ -246,19 +240,29 @@ class _SiteCombobox(QtWidgets.QWidget): modified = True if modified: - self._debug_label.setText("Modified") - return + return "modified" if self.project_name == DEFAULT_PROJECT_KEY: - if not current_value: - self._debug_label.setText("No active site") - else: - self._debug_label.setText("Active default") + if current_value: + return "studio" else: - if not current_value: - self._debug_label.setText("Default active site") - else: - self._debug_label.setText("Active project") + if current_value: + return "overriden" + + studio_value = current_value = self._get_local_settings_item( + DEFAULT_PROJECT_KEY + ) + if studio_value: + return "studio" + return "" + + def update_style(self): + state = self._get_style_state() + + self.combobox_input.setProperty("input-state", state) + self.combobox_input.style().polish(self.combobox_input) + + self.label_widget.set_label_property("state", state) def _mouse_release_callback(self, event): if event.button() != QtCore.Qt.RightButton: diff --git a/pype/tools/settings/local_settings/widgets.py b/pype/tools/settings/local_settings/widgets.py index e3c9590438..d69ad816e0 100644 --- a/pype/tools/settings/local_settings/widgets.py +++ b/pype/tools/settings/local_settings/widgets.py @@ -42,6 +42,10 @@ class ProxyLabelWidget(QtWidgets.QWidget): def setText(self, text): self.label_widget.setText(text) + def set_label_property(self, *args, **kwargs): + self.label_widget.setProperty(*args, **kwargs) + self.label_widget.style().polish(self.label_widget) + def mouseReleaseEvent(self, event): if self.mouse_release_callback: return self.mouse_release_callback(event) From 7969441ec69f2f5f6b56fdb08b51565198cc5e29 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 25 Feb 2021 13:11:20 +0100 Subject: [PATCH 75/91] removed `_ui_tweaks` --- .../local_settings/projects_widget.py | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/pype/tools/settings/local_settings/projects_widget.py b/pype/tools/settings/local_settings/projects_widget.py index d09c67af08..7f05ebe7a8 100644 --- a/pype/tools/settings/local_settings/projects_widget.py +++ b/pype/tools/settings/local_settings/projects_widget.py @@ -186,6 +186,7 @@ class RootsWidget(QtWidgets.QWidget): class _SiteCombobox(QtWidgets.QWidget): site_changed = QtCore.Signal(str) + input_label = None def __init__(self, project_settings, parent): super(_SiteCombobox, self).__init__(parent) @@ -199,7 +200,11 @@ class _SiteCombobox(QtWidgets.QWidget): self.default_override_value = None self.project_override_value = None - label_widget = ProxyLabelWidget("", self._mouse_release_callback, self) + label_widget = ProxyLabelWidget( + self.input_label, + self._mouse_release_callback, + self + ) combobox_input = QtWidgets.QComboBox(self) main_layout = QtWidgets.QHBoxLayout(self) @@ -210,8 +215,6 @@ class _SiteCombobox(QtWidgets.QWidget): self.label_widget = label_widget self.combobox_input = combobox_input - self._ui_tweaks() - def _set_current_text(self, text): index = None if text: @@ -390,11 +393,6 @@ class _SiteCombobox(QtWidgets.QWidget): ) ) - def _ui_tweaks(self): - raise NotImplementedError("{} `_ui_tweaks` not implemented".format( - self.__class__.__name__ - )) - def _get_project_sites(self): raise NotImplementedError( "{} `_get_project_sites` not implemented".format( @@ -411,8 +409,7 @@ class _SiteCombobox(QtWidgets.QWidget): class AciveSiteCombo(_SiteCombobox): - def _ui_tweaks(self): - self.label_widget.setText("Active site") + input_label = "Active site" def _get_project_sites(self): global_entity = self.project_settings["project_settings"]["global"] @@ -435,8 +432,7 @@ class AciveSiteCombo(_SiteCombobox): class RemoteSiteCombo(_SiteCombobox): - def _ui_tweaks(self): - self.label_widget.setText("Remote site") + input_label = "Remote site" def _get_project_sites(self): global_entity = self.project_settings["project_settings"]["global"] From 610728aaff59b627d39b7310acd42b1d07ec368e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 25 Feb 2021 13:13:28 +0100 Subject: [PATCH 76/91] renamed `site_name` key to `site_label` --- pype/tools/settings/local_settings/window.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pype/tools/settings/local_settings/window.py b/pype/tools/settings/local_settings/window.py index 80ad811b60..242cbf8817 100644 --- a/pype/tools/settings/local_settings/window.py +++ b/pype/tools/settings/local_settings/window.py @@ -38,15 +38,15 @@ class LocalGeneralWidgets(QtWidgets.QWidget): layout = QtWidgets.QFormLayout(self) layout.setContentsMargins(0, 0, 0, 0) - layout.addRow("Local site name", local_site_name_input) + layout.addRow("Local site label", local_site_name_input) self.local_site_name_input = local_site_name_input def set_value(self, value): - site_name = "" + site_label = "" if value: - site_name = value.get("site_name", site_name) - self.local_site_name_input.setText(site_name) + site_label = value.get("site_label", site_label) + self.local_site_name_input.setText(site_label) def settings_value(self): # Add changed @@ -54,7 +54,7 @@ class LocalGeneralWidgets(QtWidgets.QWidget): output = {} local_site_name = self.local_site_name_input.text() if local_site_name: - output["site_name"] = local_site_name + output["site_label"] = local_site_name # Do not return output yet since we don't have mechanism to save or # load these data through api calls return output From 40fb974d4de749d3ccf76021c3d5240598f85025 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 25 Feb 2021 13:15:03 +0100 Subject: [PATCH 77/91] renamed `set_value` method to `update_local_settings` --- .../settings/local_settings/apps_widget.py | 10 +++++----- .../settings/local_settings/projects_widget.py | 16 ++++++++-------- pype/tools/settings/local_settings/window.py | 18 ++++++++++++------ 3 files changed, 25 insertions(+), 19 deletions(-) diff --git a/pype/tools/settings/local_settings/apps_widget.py b/pype/tools/settings/local_settings/apps_widget.py index 52fbc45fd6..e948847992 100644 --- a/pype/tools/settings/local_settings/apps_widget.py +++ b/pype/tools/settings/local_settings/apps_widget.py @@ -60,7 +60,7 @@ class AppVariantWidget(QtWidgets.QWidget): path_widget.setEnabled(False) content_layout.addWidget(path_widget) - def set_value(self, value): + def update_local_settings(self, value): if not self.executable_input_widget: return @@ -127,12 +127,12 @@ class AppGroupWidget(QtWidgets.QWidget): self.widgets_by_variant_name = widgets_by_variant_name - def set_value(self, value): + def update_local_settings(self, value): if not value: value = {} for variant_name, widget in self.widgets_by_variant_name.items(): - widget.set_value(value.get(variant_name)) + widget.update_local_settings(value.get(variant_name)) def settings_value(self): output = {} @@ -177,12 +177,12 @@ class LocalApplicationsWidgets(QtWidgets.QWidget): self.widgets_by_group_name = widgets_by_group_name - def set_value(self, value): + def update_local_settings(self, value): if not value: value = {} for group_name, widget in self.widgets_by_group_name.items(): - widget.set_value(value.get(group_name)) + widget.update_local_settings(value.get(group_name)) def settings_value(self): output = {} diff --git a/pype/tools/settings/local_settings/projects_widget.py b/pype/tools/settings/local_settings/projects_widget.py index 7f05ebe7a8..7be2b73075 100644 --- a/pype/tools/settings/local_settings/projects_widget.py +++ b/pype/tools/settings/local_settings/projects_widget.py @@ -169,7 +169,7 @@ class RootsWidget(QtWidgets.QWidget): # Add spacer so other widgets are squeezed to top self.content_layout.addWidget(SpacerWidget(self), 1) - def set_value(self, local_project_settings): + def update_local_settings(self, local_project_settings): self.local_project_settings = local_project_settings self.local_project_settings_orig = copy.deepcopy( dict(local_project_settings) @@ -329,7 +329,7 @@ class _SiteCombobox(QtWidgets.QWidget): if to_run: to_run() - def set_value(self, local_project_settings): + def update_local_settings(self, local_project_settings): self.local_project_settings = local_project_settings self.local_project_settings_orig = copy.deepcopy( dict(local_project_settings) @@ -495,11 +495,11 @@ class RootSiteWidget(QtWidgets.QWidget): sites_entity = global_entity["sync_server"]["sites"] return tuple(sites_entity.keys()) - def set_value(self, local_project_settings): + def update_local_settings(self, local_project_settings): self.local_project_settings = local_project_settings - self.active_site_widget.set_value(local_project_settings) - self.remote_site_widget.set_value(local_project_settings) - self.roots_widget.set_value(local_project_settings) + self.active_site_widget.update_local_settings(local_project_settings) + self.remote_site_widget.update_local_settings(local_project_settings) + self.roots_widget.update_local_settings(local_project_settings) def _on_acite_site_change(self, site_name): self._change_active_site(site_name) @@ -555,12 +555,12 @@ class ProjectSettingsWidget(QtWidgets.QWidget): project_name = DEFAULT_PROJECT_KEY self.roos_site_widget.change_project(project_name) - def set_value(self, value): + def update_local_settings(self, value): if not value: value = {} self.local_project_settings = ProjectValue(value) - self.roos_site_widget.set_value(self.local_project_settings) + self.roos_site_widget.update_local_settings(self.local_project_settings) self.projects_widget.refresh() diff --git a/pype/tools/settings/local_settings/window.py b/pype/tools/settings/local_settings/window.py index 242cbf8817..c53d16ab8d 100644 --- a/pype/tools/settings/local_settings/window.py +++ b/pype/tools/settings/local_settings/window.py @@ -42,7 +42,7 @@ class LocalGeneralWidgets(QtWidgets.QWidget): self.local_site_name_input = local_site_name_input - def set_value(self, value): + def update_local_settings(self, value): site_label = "" if value: site_label = value.get("site_label", site_label) @@ -144,13 +144,19 @@ class LocalSettingsWidget(QtWidgets.QWidget): self.projects_widget = projects_widget - def set_value(self, value): + def update_local_settings(self, value): if not value: value = {} - self.general_widget.set_value(value.get(LOCAL_GENERAL_KEY)) - self.app_widget.set_value(value.get(LOCAL_APPS_KEY)) - self.projects_widget.set_value(value.get(LOCAL_PROJECTS_KEY)) + self.general_widget.update_local_settings( + value.get(LOCAL_GENERAL_KEY) + ) + self.app_widget.update_local_settings( + value.get(LOCAL_APPS_KEY) + ) + self.projects_widget.update_local_settings( + value.get(LOCAL_PROJECTS_KEY) + ) def settings_value(self): output = {} @@ -207,7 +213,7 @@ class LocalSettingsWindow(QtWidgets.QWidget): def reset(self): value = get_local_settings() - self.settings_widget.set_value(value) + self.settings_widget.update_local_settings(value) def _on_save_clicked(self): try: From 33632e2e42ca6572ef69c6954320e39f24101673 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 25 Feb 2021 13:27:34 +0100 Subject: [PATCH 78/91] moved general settings widget to specific file --- .../settings/local_settings/general_widget.py | 32 +++++++++++++++++++ pype/tools/settings/local_settings/window.py | 32 +------------------ 2 files changed, 33 insertions(+), 31 deletions(-) create mode 100644 pype/tools/settings/local_settings/general_widget.py diff --git a/pype/tools/settings/local_settings/general_widget.py b/pype/tools/settings/local_settings/general_widget.py new file mode 100644 index 0000000000..7732157122 --- /dev/null +++ b/pype/tools/settings/local_settings/general_widget.py @@ -0,0 +1,32 @@ +from Qt import QtWidgets + + +class LocalGeneralWidgets(QtWidgets.QWidget): + def __init__(self, parent): + super(LocalGeneralWidgets, self).__init__(parent) + + local_site_name_input = QtWidgets.QLineEdit(self) + + layout = QtWidgets.QFormLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + + layout.addRow("Local site label", local_site_name_input) + + self.local_site_name_input = local_site_name_input + + def update_local_settings(self, value): + site_label = "" + if value: + site_label = value.get("site_label", site_label) + self.local_site_name_input.setText(site_label) + + def settings_value(self): + # Add changed + # If these have changed then + output = {} + local_site_name = self.local_site_name_input.text() + if local_site_name: + output["site_label"] = local_site_name + # Do not return output yet since we don't have mechanism to save or + # load these data through api calls + return output diff --git a/pype/tools/settings/local_settings/window.py b/pype/tools/settings/local_settings/window.py index c53d16ab8d..32a480a82f 100644 --- a/pype/tools/settings/local_settings/window.py +++ b/pype/tools/settings/local_settings/window.py @@ -19,6 +19,7 @@ from .widgets import ( CHILD_OFFSET ) from .mongo_widget import PypeMongoWidget +from .general_widget import LocalGeneralWidgets from .apps_widget import LocalApplicationsWidgets from .projects_widget import ProjectSettingsWidget @@ -29,37 +30,6 @@ LOCAL_PROJECTS_KEY = "projects" LOCAL_APPS_KEY = "applications" -class LocalGeneralWidgets(QtWidgets.QWidget): - def __init__(self, parent): - super(LocalGeneralWidgets, self).__init__(parent) - - local_site_name_input = QtWidgets.QLineEdit(self) - - layout = QtWidgets.QFormLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - - layout.addRow("Local site label", local_site_name_input) - - self.local_site_name_input = local_site_name_input - - def update_local_settings(self, value): - site_label = "" - if value: - site_label = value.get("site_label", site_label) - self.local_site_name_input.setText(site_label) - - def settings_value(self): - # Add changed - # If these have changed then - output = {} - local_site_name = self.local_site_name_input.text() - if local_site_name: - output["site_label"] = local_site_name - # Do not return output yet since we don't have mechanism to save or - # load these data through api calls - return output - - class LocalSettingsWidget(QtWidgets.QWidget): def __init__(self, parent=None): super(LocalSettingsWidget, self).__init__(parent) From 6fc81bd41a4dedaf09f7d5f7e7a317176c32761c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 25 Feb 2021 13:28:18 +0100 Subject: [PATCH 79/91] update_local_settings triggers reset of widgets --- .../settings/local_settings/apps_widget.py | 22 ++++++++++++++----- .../local_settings/projects_widget.py | 9 +++++++- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/pype/tools/settings/local_settings/apps_widget.py b/pype/tools/settings/local_settings/apps_widget.py index e948847992..f2a47a8f27 100644 --- a/pype/tools/settings/local_settings/apps_widget.py +++ b/pype/tools/settings/local_settings/apps_widget.py @@ -150,12 +150,22 @@ class LocalApplicationsWidgets(QtWidgets.QWidget): def __init__(self, system_settings_entity, parent): super(LocalApplicationsWidgets, self).__init__(parent) - widgets_by_group_name = {} + self.widgets_by_group_name = {} + self.system_settings_entity = system_settings_entity layout = QtWidgets.QVBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) - for key, entity in system_settings_entity["applications"].items(): + self.content_layout = layout + + def _reset_app_widgets(self): + while self.content_layout.count() > 0: + item = self.content_layout.itemAt(0) + item.widget().hide() + self.content_layout.removeItem(item) + self.widgets_by_group_name.clear() + + for key, entity in self.system_settings_entity["applications"].items(): # Filter not enabled app groups if not entity["enabled"].value: continue @@ -172,15 +182,15 @@ class LocalApplicationsWidgets(QtWidgets.QWidget): # Create App group specific widget and store it by the key group_widget = AppGroupWidget(entity, self) - widgets_by_group_name[key] = group_widget - layout.addWidget(group_widget) - - self.widgets_by_group_name = widgets_by_group_name + self.widgets_by_group_name[key] = group_widget + self.content_layout.addWidget(group_widget) def update_local_settings(self, value): if not value: value = {} + self._reset_app_widgets() + for group_name, widget in self.widgets_by_group_name.items(): widget.update_local_settings(value.get(group_name)) diff --git a/pype/tools/settings/local_settings/projects_widget.py b/pype/tools/settings/local_settings/projects_widget.py index 7be2b73075..7859996490 100644 --- a/pype/tools/settings/local_settings/projects_widget.py +++ b/pype/tools/settings/local_settings/projects_widget.py @@ -500,6 +500,11 @@ class RootSiteWidget(QtWidgets.QWidget): self.active_site_widget.update_local_settings(local_project_settings) self.remote_site_widget.update_local_settings(local_project_settings) self.roots_widget.update_local_settings(local_project_settings) + project_name = self._project_name + if project_name is None: + project_name = DEFAULT_PROJECT_KEY + + self.change_project(project_name) def _on_acite_site_change(self, site_name): self._change_active_site(site_name) @@ -560,7 +565,9 @@ class ProjectSettingsWidget(QtWidgets.QWidget): value = {} self.local_project_settings = ProjectValue(value) - self.roos_site_widget.update_local_settings(self.local_project_settings) + self.roos_site_widget.update_local_settings( + self.local_project_settings + ) self.projects_widget.refresh() From 81180738f076e7e15ec8711790c2da9a7d88fa9e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 25 Feb 2021 13:28:41 +0100 Subject: [PATCH 80/91] save local values triggers reset --- pype/tools/settings/local_settings/window.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/pype/tools/settings/local_settings/window.py b/pype/tools/settings/local_settings/window.py index 32a480a82f..ee7437d184 100644 --- a/pype/tools/settings/local_settings/window.py +++ b/pype/tools/settings/local_settings/window.py @@ -118,6 +118,9 @@ class LocalSettingsWidget(QtWidgets.QWidget): if not value: value = {} + self.system_settings.reset() + self.project_settings.reset() + self.general_widget.update_local_settings( value.get(LOCAL_GENERAL_KEY) ) @@ -186,10 +189,6 @@ class LocalSettingsWindow(QtWidgets.QWidget): self.settings_widget.update_local_settings(value) def _on_save_clicked(self): - try: - value = self.settings_widget.settings_value() - except Exception: - log.warning("Failed to save", exc_info=True) - return - + value = self.settings_widget.settings_value() save_local_settings(value) + self.reset() From d72549bdef5b61e1cd944fabdc0b6f1ddf2d099b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 25 Feb 2021 13:31:39 +0100 Subject: [PATCH 81/91] added reset button --- pype/tools/settings/local_settings/window.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pype/tools/settings/local_settings/window.py b/pype/tools/settings/local_settings/window.py index ee7437d184..b8113fbfbc 100644 --- a/pype/tools/settings/local_settings/window.py +++ b/pype/tools/settings/local_settings/window.py @@ -167,8 +167,12 @@ class LocalSettingsWindow(QtWidgets.QWidget): scroll_widget.setWidgetResizable(True) footer = QtWidgets.QWidget(self) + save_btn = QtWidgets.QPushButton("Save", footer) + reset_btn = QtWidgets.QPushButton("Reset", footer) + footer_layout = QtWidgets.QHBoxLayout(footer) + footer_layout.addWidget(reset_btn, 0) footer_layout.addWidget(SpacerWidget(footer), 1) footer_layout.addWidget(save_btn, 0) @@ -178,8 +182,10 @@ class LocalSettingsWindow(QtWidgets.QWidget): main_layout.addWidget(footer, 0) save_btn.clicked.connect(self._on_save_clicked) + reset_btn.clicked.connect(self._on_reset_clicked) self.settings_widget = settings_widget + self.reset_btn = reset_btn self.save_btn = save_btn self.reset() @@ -188,6 +194,9 @@ class LocalSettingsWindow(QtWidgets.QWidget): value = get_local_settings() self.settings_widget.update_local_settings(value) + def _on_reset_clicked(self): + self.reset() + def _on_save_clicked(self): value = self.settings_widget.settings_value() save_local_settings(value) From baa5b07610b45bacf710d14fceb2045909b54ef9 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 25 Feb 2021 14:27:23 +0100 Subject: [PATCH 82/91] roots for all sites are always visible --- .../local_settings/projects_widget.py | 75 +++++++++---------- 1 file changed, 36 insertions(+), 39 deletions(-) diff --git a/pype/tools/settings/local_settings/projects_widget.py b/pype/tools/settings/local_settings/projects_widget.py index 7859996490..959bbafdd0 100644 --- a/pype/tools/settings/local_settings/projects_widget.py +++ b/pype/tools/settings/local_settings/projects_widget.py @@ -14,6 +14,12 @@ from .widgets import ( LOCAL_ROOTS_KEY = "roots" +def get_active_sites(project_settings): + global_entity = project_settings["project_settings"]["global"] + sites_entity = global_entity["sync_server"]["sites"] + return tuple(sites_entity.keys()) + + class _ProjectListWidget(ProjectListWidget): def on_item_clicked(self, new_index): new_project_name = new_index.data(QtCore.Qt.DisplayRole) @@ -123,11 +129,10 @@ class RootsWidget(QtWidgets.QWidget): super(RootsWidget, self).__init__(parent) self.project_settings = project_settings - self.widgts_by_root_name = {} + self.site_widgets = [] self.local_project_settings = None self.local_project_settings_orig = None self._project_name = None - self._site_name = None self.content_layout = QtWidgets.QVBoxLayout(self) @@ -136,35 +141,43 @@ class RootsWidget(QtWidgets.QWidget): item = self.content_layout.itemAt(0) item.widget().hide() self.content_layout.removeItem(item) - self.widgts_by_root_name.clear() + self.site_widgets = [] def refresh(self): self._clear_widgets() - if self._project_name is None or self._site_name is None: + if self._project_name is None: return - # Site label - self.content_layout.addWidget(QtWidgets.QLabel(self._site_name, self)) - - # Root inputs roots_entity = ( self.project_settings[PROJECT_ANATOMY_KEY][LOCAL_ROOTS_KEY] ) - for root_name, path_entity in roots_entity.items(): - platform_entity = path_entity[platform.system().lower()] - root_widget = RootInputWidget( - self.local_project_settings, - self.local_project_settings_orig, - platform_entity, - root_name, - self._project_name, - self._site_name, - self - ) + # Site label + for site_name in get_active_sites(self.project_settings): + site_widget = QtWidgets.QWidget(self) + site_layout = QtWidgets.QVBoxLayout(site_widget) - self.content_layout.addWidget(root_widget) - self.widgts_by_root_name[root_name] = root_widget + site_label = QtWidgets.QLabel(site_name, site_widget) + + site_layout.addWidget(site_label) + + # Root inputs + for root_name, path_entity in roots_entity.items(): + platform_entity = path_entity[platform.system().lower()] + root_widget = RootInputWidget( + self.local_project_settings, + self.local_project_settings_orig, + platform_entity, + root_name, + self._project_name, + site_name, + site_widget + ) + + site_layout.addWidget(root_widget) + + self.site_widgets.append(site_widget) + self.content_layout.addWidget(site_widget) # Add spacer so other widgets are squeezed to top self.content_layout.addWidget(SpacerWidget(self), 1) @@ -175,17 +188,12 @@ class RootsWidget(QtWidgets.QWidget): dict(local_project_settings) ) - def change_site(self, site_name): - self._site_name = site_name - self.refresh() - def change_project(self, project_name): self._project_name = project_name self.refresh() class _SiteCombobox(QtWidgets.QWidget): - site_changed = QtCore.Signal(str) input_label = None def __init__(self, project_settings, parent): @@ -375,7 +383,6 @@ class _SiteCombobox(QtWidgets.QWidget): self.combobox_input.setCurrentIndex(index) self.project_name = project_name - self.site_changed.emit(self.current_text()) self.update_style() def _on_index_change(self): @@ -383,7 +390,6 @@ class _SiteCombobox(QtWidgets.QWidget): return self._set_local_settings_value(self.current_text()) - self.site_changed.emit(self.current_text()) self.update_style() def _set_local_settings_value(self, value): @@ -412,9 +418,7 @@ class AciveSiteCombo(_SiteCombobox): input_label = "Active site" def _get_project_sites(self): - global_entity = self.project_settings["project_settings"]["global"] - sites_entity = global_entity["sync_server"]["sites"] - return tuple(sites_entity.keys()) + return get_active_sites(self.project_settings) def _get_local_settings_item(self, project_name, data=None): if data is None: @@ -480,7 +484,6 @@ class RootSiteWidget(QtWidgets.QWidget): main_layout.addWidget(roots_widget) main_layout.addWidget(SpacerWidget(self), 1) - active_site_widget.site_changed.connect(self._on_acite_site_change) self.active_site_widget = active_site_widget self.remote_site_widget = remote_site_widget self.roots_widget = roots_widget @@ -504,13 +507,7 @@ class RootSiteWidget(QtWidgets.QWidget): if project_name is None: project_name = DEFAULT_PROJECT_KEY - self.change_project(project_name) - - def _on_acite_site_change(self, site_name): - self._change_active_site(site_name) - - def _change_active_site(self, site_name): - self.roots_widget.change_site(site_name) + self.change_project(project_name) def change_project(self, project_name): self._project_name = project_name From 812abd3a905950f9e7e40ffc491fa5fc2f9b8dfe Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 25 Feb 2021 14:58:04 +0100 Subject: [PATCH 83/91] active and remote site comboboxes refers to project settings --- .../local_settings/projects_widget.py | 85 +++++++++++++------ 1 file changed, 60 insertions(+), 25 deletions(-) diff --git a/pype/tools/settings/local_settings/projects_widget.py b/pype/tools/settings/local_settings/projects_widget.py index 959bbafdd0..a38d22c5cd 100644 --- a/pype/tools/settings/local_settings/projects_widget.py +++ b/pype/tools/settings/local_settings/projects_widget.py @@ -12,6 +12,7 @@ from .widgets import ( ) LOCAL_ROOTS_KEY = "roots" +NOT_SET = type("NOT_SET", (), {})() def get_active_sites(project_settings): @@ -94,7 +95,6 @@ class RootInputWidget(QtWidgets.QWidget): self.value_input = value_input - self.is_default_project = is_default_project self.studio_value = platform_root_entity.value self.default_value = default_input_value self.project_value = project_value @@ -203,7 +203,6 @@ class _SiteCombobox(QtWidgets.QWidget): self.local_project_settings = None self.local_project_settings_orig = None self.project_name = None - self.is_default_project = None self.default_override_value = None self.project_override_value = None @@ -281,13 +280,22 @@ class _SiteCombobox(QtWidgets.QWidget): self._show_actions() def _remove_from_local(self): - if ( - self.project_name != DEFAULT_PROJECT_KEY - and self.default_override_value - ): + settings_value = self._get_value_from_project_settings() + combobox_value = None + if self.project_name == DEFAULT_PROJECT_KEY: + combobox_value = self._get_local_settings_item(DEFAULT_PROJECT_KEY) + if combobox_value: + idx = self.combobox_input.findText(combobox_value) + if idx < 0: + combobox_value = None + + if not combobox_value: + combobox_value = settings_value + + if combobox_value: _project_name = self.project_name self.project_name = None - self._set_current_text(self.default_override_value) + self._set_current_text(combobox_value) self.project_name = _project_name self._set_local_settings_value("") @@ -300,19 +308,19 @@ class _SiteCombobox(QtWidgets.QWidget): def _add_actions(self, menu, actions_mapping): # TODO better labels if self.project_name == DEFAULT_PROJECT_KEY: - if self.default_override_value: - action = QtWidgets.QAction("Remove from default") - callback = self._remove_from_local - else: - action = QtWidgets.QAction("Add to default") - callback = self._add_to_local + remove_label = "Remove from default" + add_label = "Add to default" else: - if self.project_override_value: - action = QtWidgets.QAction("Remove from project") - callback = self._remove_from_local - else: - action = QtWidgets.QAction("Add to project") - callback = self._add_to_local + remove_label = "Remove from project" + add_label = "Add to project" + + has_value = self._get_local_settings_item(self.project_name) + if has_value: + action = QtWidgets.QAction(remove_label) + callback = self._remove_from_local + else: + action = QtWidgets.QAction(add_label) + callback = self._add_to_local actions_mapping[action] = callback menu.addAction(action) @@ -356,12 +364,12 @@ class _SiteCombobox(QtWidgets.QWidget): self.update_style() return - self.is_default_project = bool(project_name == DEFAULT_PROJECT_KEY) + is_default_project = bool(project_name == DEFAULT_PROJECT_KEY) site_items = self._get_project_sites() self.combobox_input.addItems(site_items) default_item = self._get_local_settings_item(DEFAULT_PROJECT_KEY) - if self.is_default_project: + if is_default_project: project_item = None else: project_item = self._get_local_settings_item(project_name) @@ -379,7 +387,14 @@ class _SiteCombobox(QtWidgets.QWidget): self.default_override_value = default_item if index is None: index = idx - if index: + + if index is None: + settings_value = self._get_value_from_project_settings() + idx = self.combobox_input.findText(settings_value) + if idx >= 0: + index = idx + + if index is not None: self.combobox_input.setCurrentIndex(index) self.project_name = project_name @@ -406,13 +421,20 @@ class _SiteCombobox(QtWidgets.QWidget): ) ) - def _get_local_settings_item(self, project_name, data=None): + def _get_local_settings_item(self, project_name=None, data=None): raise NotImplementedError( "{}`_get_local_settings_item` not implemented".format( self.__class__.__name__ ) ) + def _get_value_from_project_settings(self): + raise NotImplementedError( + "{}`_get_value_from_project_settings` not implemented".format( + self.__class__.__name__ + ) + ) + class AciveSiteCombo(_SiteCombobox): input_label = "Active site" @@ -420,7 +442,10 @@ class AciveSiteCombo(_SiteCombobox): def _get_project_sites(self): return get_active_sites(self.project_settings) - def _get_local_settings_item(self, project_name, data=None): + def _get_local_settings_item(self, project_name=None, data=None): + if project_name is None: + project_name = self.project_name + if data is None: data = self.local_project_settings project_values = data.get(project_name) @@ -429,6 +454,10 @@ class AciveSiteCombo(_SiteCombobox): value = project_values.get("active_site") return value + def _get_value_from_project_settings(self): + global_entity = self.project_settings["project_settings"]["global"] + return global_entity["sync_server"]["config"]["active_site"].value + def _set_local_settings_value(self, value): if self.project_name not in self.local_project_settings: self.local_project_settings[self.project_name] = {} @@ -443,7 +472,9 @@ class RemoteSiteCombo(_SiteCombobox): sites_entity = global_entity["sync_server"]["sites"] return tuple(sites_entity.keys()) - def _get_local_settings_item(self, project_name, data=None): + def _get_local_settings_item(self, project_name=None, data=None): + if project_name is None: + project_name = self.project_name if data is None: data = self.local_project_settings project_values = data.get(project_name) @@ -452,6 +483,10 @@ class RemoteSiteCombo(_SiteCombobox): value = project_values.get("remote_site") return value + def _get_value_from_project_settings(self): + global_entity = self.project_settings["project_settings"]["global"] + return global_entity["sync_server"]["config"]["remote_site"].value + def _set_local_settings_value(self, value): if self.project_name not in self.local_project_settings: self.local_project_settings[self.project_name] = {} From 1c727612f64d2eeaec67a36622a7732c4cc8a90a Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 25 Feb 2021 15:01:09 +0100 Subject: [PATCH 84/91] active site is also used from sync server when roots are filled --- pype/settings/lib.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pype/settings/lib.py b/pype/settings/lib.py index ad1292b326..feeeaf3813 100644 --- a/pype/settings/lib.py +++ b/pype/settings/lib.py @@ -408,6 +408,17 @@ def apply_local_settings_on_anatomy_settings( if not active_site: active_site = default_locals.get("active_site") + if not active_site: + project_settings = get_project_settings(project_name) + active_site = ( + project_settings + ["global"] + ["sync_server"] + ["config"] + ["active_site"] + ) + + # QUESTION should raise an exception? if not active_site: return From 5a261477fabb1c72f0a6cb6bffd9f3f233049418 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 25 Feb 2021 16:15:59 +0100 Subject: [PATCH 85/91] added discard changes and actions to root inputs --- .../settings/local_settings/constants.py | 30 +++ .../local_settings/projects_widget.py | 185 ++++++++++++++---- pype/tools/settings/local_settings/window.py | 10 +- 3 files changed, 181 insertions(+), 44 deletions(-) create mode 100644 pype/tools/settings/local_settings/constants.py diff --git a/pype/tools/settings/local_settings/constants.py b/pype/tools/settings/local_settings/constants.py new file mode 100644 index 0000000000..7a8774467f --- /dev/null +++ b/pype/tools/settings/local_settings/constants.py @@ -0,0 +1,30 @@ +# Action labels +LABEL_REMOVE_DEFAULT = "Remove from default" +LABEL_ADD_DEFAULT = "Add to default" +LABEL_REMOVE_PROJECT = "Remove from project" +LABEL_ADD_PROJECT = "Add to project" +LABEL_DISCARD_CHANGES = "Discard changes" + +# Local setting contants +# TODO move to settings constants +LOCAL_GENERAL_KEY = "general" +LOCAL_PROJECTS_KEY = "projects" +LOCAL_APPS_KEY = "applications" + +# Roots key constant +LOCAL_ROOTS_KEY = "roots" + + +__all__ = ( + "LABEL_REMOVE_DEFAULT", + "LABEL_ADD_DEFAULT", + "LABEL_REMOVE_PROJECT", + "LABEL_ADD_PROJECT", + "LABEL_DISCARD_CHANGES", + + "LOCAL_GENERAL_KEY", + "LOCAL_PROJECTS_KEY", + "LOCAL_APPS_KEY", + + "LOCAL_ROOTS_KEY" +) diff --git a/pype/tools/settings/local_settings/projects_widget.py b/pype/tools/settings/local_settings/projects_widget.py index a38d22c5cd..37e31d6de0 100644 --- a/pype/tools/settings/local_settings/projects_widget.py +++ b/pype/tools/settings/local_settings/projects_widget.py @@ -10,8 +10,15 @@ from .widgets import ( SpacerWidget, ProxyLabelWidget ) +from .constants import ( + LABEL_REMOVE_DEFAULT, + LABEL_ADD_DEFAULT, + LABEL_REMOVE_PROJECT, + LABEL_ADD_PROJECT, + LABEL_DISCARD_CHANGES, + LOCAL_ROOTS_KEY +) -LOCAL_ROOTS_KEY = "roots" NOT_SET = type("NOT_SET", (), {})() @@ -57,7 +64,7 @@ class RootInputWidget(QtWidgets.QWidget): self.origin_value = self._get_site_value_for_project( self.project_name, self.local_project_settings_orig - ) + ) or "" is_default_project = bool(project_name == DEFAULT_PROJECT_KEY) @@ -79,7 +86,11 @@ class RootInputWidget(QtWidgets.QWidget): if not placeholder: placeholder = platform_root_entity.value - key_label = QtWidgets.QLabel(root_name, self) + key_label = ProxyLabelWidget( + root_name, + self._mouse_release_callback, + self + ) value_input = QtWidgets.QLineEdit(self) value_input.setPlaceholderText("< {} >".format(placeholder)) @@ -94,15 +105,99 @@ class RootInputWidget(QtWidgets.QWidget): root_layout.addWidget(value_input) self.value_input = value_input + self.label_widget = key_label self.studio_value = platform_root_entity.value self.default_value = default_input_value self.project_value = project_value + self.placeholder_value = placeholder - @property def is_modified(self): return self.origin_value != self.value_input.text() + def _mouse_release_callback(self, event): + if event.button() != QtCore.Qt.RightButton: + return + self._show_actions() + event.accept() + + def _get_style_state(self): + if self.project_name is None: + return "" + + if self.is_modified(): + return "modified" + + current_value = self.value_input.text() + if self.project_name == DEFAULT_PROJECT_KEY: + if current_value: + return "studio" + else: + if current_value: + return "overriden" + + studio_value = self._get_site_value_for_project( + DEFAULT_PROJECT_KEY + ) + if studio_value: + return "studio" + return "" + + def _update_style(self): + state = self._get_style_state() + + self.value_input.setProperty("input-state", state) + self.value_input.style().polish(self.value_input) + + self.label_widget.set_label_property("state", state) + + def _remove_from_local(self): + self.value_input.setText("") + self._update_style() + + def _add_to_local(self): + self.value_input.setText(self.placeholder_value) + self._update_style() + + def discard_changes(self): + self.value_input.setText(self.origin_value) + self._update_style() + + def _show_actions(self): + if self.project_name is None: + return + + menu = QtWidgets.QMenu(self) + actions_mapping = {} + + if self.project_name == DEFAULT_PROJECT_KEY: + remove_label = LABEL_REMOVE_DEFAULT + add_label = LABEL_ADD_DEFAULT + else: + remove_label = LABEL_REMOVE_PROJECT + add_label = LABEL_ADD_PROJECT + + if self.value_input.text(): + action = QtWidgets.QAction(remove_label) + callback = self._remove_from_local + else: + action = QtWidgets.QAction(add_label) + callback = self._add_to_local + + actions_mapping[action] = callback + menu.addAction(action) + + if self.is_modified(): + discard_changes_action = QtWidgets.QAction(LABEL_DISCARD_CHANGES) + actions_mapping[discard_changes_action] = self.discard_changes + menu.addAction(discard_changes_action) + + result = menu.exec_(QtGui.QCursor.pos()) + if result: + to_run = actions_mapping[result] + if to_run: + to_run() + def _get_site_value_for_project(self, project_name, data=None): if data is None: data = self.local_project_settings @@ -122,6 +217,7 @@ class RootInputWidget(QtWidgets.QWidget): data[key] = {} data = data[key] data[self.root_name] = value + self._update_style() class RootsWidget(QtWidgets.QWidget): @@ -229,11 +325,26 @@ class _SiteCombobox(QtWidgets.QWidget): if idx >= 0: index = idx - if index: + if index is not None: self.combobox_input.setCurrentIndex(index) return True return False + def is_modified(self, current_value=NOT_SET, orig_value=NOT_SET): + if current_value is NOT_SET: + current_value = self._get_local_settings_item(self.project_name) + if orig_value is NOT_SET: + orig_value = self._get_local_settings_item( + self.project_name, self.local_project_settings_orig + ) + if current_value and orig_value: + modified = current_value != orig_value + elif not current_value and not orig_value: + modified = False + else: + modified = True + return modified + def _get_style_state(self): if self.project_name is None: return "" @@ -242,14 +353,8 @@ class _SiteCombobox(QtWidgets.QWidget): orig_value = self._get_local_settings_item( self.project_name, self.local_project_settings_orig ) - if current_value and orig_value: - modified = current_value != orig_value - elif not current_value and not orig_value: - modified = False - else: - modified = True - if modified: + if self.is_modified(current_value, orig_value): return "modified" if self.project_name == DEFAULT_PROJECT_KEY: @@ -259,14 +364,12 @@ class _SiteCombobox(QtWidgets.QWidget): if current_value: return "overriden" - studio_value = current_value = self._get_local_settings_item( - DEFAULT_PROJECT_KEY - ) + studio_value = self._get_local_settings_item(DEFAULT_PROJECT_KEY) if studio_value: return "studio" return "" - def update_style(self): + def _update_style(self): state = self._get_style_state() self.combobox_input.setProperty("input-state", state) @@ -299,20 +402,31 @@ class _SiteCombobox(QtWidgets.QWidget): self.project_name = _project_name self._set_local_settings_value("") - self.update_style() + self._update_style() def _add_to_local(self): self._set_local_settings_value(self.current_text()) - self.update_style() + self._update_style() + + def discard_changes(self): + orig_value = self._get_local_settings_item( + self.project_name, self.local_project_settings_orig + ) + self._set_current_text(orig_value) + + def _show_actions(self): + if self.project_name is None: + return + + menu = QtWidgets.QMenu(self) + actions_mapping = {} - def _add_actions(self, menu, actions_mapping): - # TODO better labels if self.project_name == DEFAULT_PROJECT_KEY: - remove_label = "Remove from default" - add_label = "Add to default" + remove_label = LABEL_REMOVE_DEFAULT + add_label = LABEL_ADD_DEFAULT else: - remove_label = "Remove from project" - add_label = "Add to project" + remove_label = LABEL_REMOVE_PROJECT + add_label = LABEL_ADD_PROJECT has_value = self._get_local_settings_item(self.project_name) if has_value: @@ -325,19 +439,10 @@ class _SiteCombobox(QtWidgets.QWidget): actions_mapping[action] = callback menu.addAction(action) - def _show_actions(self): - if self.project_name is None: - return - - menu = QtWidgets.QMenu(self) - actions_mapping = {} - - self._add_actions(menu, actions_mapping) - - if not actions_mapping: - action = QtWidgets.QAction("< No action >") - actions_mapping[action] = None - menu.addAction(action) + if self.is_modified(): + discard_changes_action = QtWidgets.QAction(LABEL_DISCARD_CHANGES) + actions_mapping[discard_changes_action] = self.discard_changes + menu.addAction(discard_changes_action) result = menu.exec_(QtGui.QCursor.pos()) if result: @@ -361,7 +466,7 @@ class _SiteCombobox(QtWidgets.QWidget): self.project_name = None self.combobox_input.clear() if project_name is None: - self.update_style() + self._update_style() return is_default_project = bool(project_name == DEFAULT_PROJECT_KEY) @@ -398,14 +503,14 @@ class _SiteCombobox(QtWidgets.QWidget): self.combobox_input.setCurrentIndex(index) self.project_name = project_name - self.update_style() + self._update_style() def _on_index_change(self): if self.project_name is None: return self._set_local_settings_value(self.current_text()) - self.update_style() + self._update_style() def _set_local_settings_value(self, value): raise NotImplementedError( diff --git a/pype/tools/settings/local_settings/window.py b/pype/tools/settings/local_settings/window.py index b8113fbfbc..d8d8adfea9 100644 --- a/pype/tools/settings/local_settings/window.py +++ b/pype/tools/settings/local_settings/window.py @@ -23,11 +23,13 @@ from .general_widget import LocalGeneralWidgets from .apps_widget import LocalApplicationsWidgets from .projects_widget import ProjectSettingsWidget -log = logging.getLogger(__name__) +from .constants import ( + LOCAL_GENERAL_KEY, + LOCAL_PROJECTS_KEY, + LOCAL_APPS_KEY +) -LOCAL_GENERAL_KEY = "general" -LOCAL_PROJECTS_KEY = "projects" -LOCAL_APPS_KEY = "applications" +log = logging.getLogger(__name__) class LocalSettingsWidget(QtWidgets.QWidget): From cec90430e80cf011997b96c3f81b528345b1aa13 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 25 Feb 2021 16:30:32 +0100 Subject: [PATCH 86/91] update style on root input initialization --- pype/tools/settings/local_settings/projects_widget.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pype/tools/settings/local_settings/projects_widget.py b/pype/tools/settings/local_settings/projects_widget.py index 37e31d6de0..28765155c2 100644 --- a/pype/tools/settings/local_settings/projects_widget.py +++ b/pype/tools/settings/local_settings/projects_widget.py @@ -112,6 +112,8 @@ class RootInputWidget(QtWidgets.QWidget): self.project_value = project_value self.placeholder_value = placeholder + self._update_style() + def is_modified(self): return self.origin_value != self.value_input.text() From 78bca249ffdf7c6f79c8241451df214a71553016 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 25 Feb 2021 17:07:21 +0100 Subject: [PATCH 87/91] a little bit faster project gui creation --- pype/tools/settings/settings/widgets/widgets.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pype/tools/settings/settings/widgets/widgets.py b/pype/tools/settings/settings/widgets/widgets.py index fd8d9d753c..656aaaa652 100644 --- a/pype/tools/settings/settings/widgets/widgets.py +++ b/pype/tools/settings/settings/widgets/widgets.py @@ -664,9 +664,8 @@ class ProjectListWidget(QtWidgets.QWidget): self.current_project = None if self.dbcon: - for project_doc in tuple(self.dbcon.projects()): - items.append(project_doc["name"]) - + for project_name in self.dbcon.database.collection_names(): + items.append(project_name) for item in items: model.appendRow(QtGui.QStandardItem(item)) From 3101c91596a02c5f1dd3e7f0279f1daa86f19941 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 25 Feb 2021 17:12:51 +0100 Subject: [PATCH 88/91] hound fixes --- pype/tools/settings/local_settings/apps_widget.py | 4 ++-- pype/tools/settings/local_settings/constants.py | 2 ++ pype/tools/settings/local_settings/widgets.py | 9 ++++++++- pype/tools/settings/local_settings/window.py | 7 +++---- 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/pype/tools/settings/local_settings/apps_widget.py b/pype/tools/settings/local_settings/apps_widget.py index f2a47a8f27..d63cd6a834 100644 --- a/pype/tools/settings/local_settings/apps_widget.py +++ b/pype/tools/settings/local_settings/apps_widget.py @@ -2,9 +2,9 @@ import platform from Qt import QtWidgets from .widgets import ( Separator, - ExpandingWidget, - CHILD_OFFSET + ExpandingWidget ) +from .constants import CHILD_OFFSET class AppVariantWidget(QtWidgets.QWidget): diff --git a/pype/tools/settings/local_settings/constants.py b/pype/tools/settings/local_settings/constants.py index 7a8774467f..83c45afba8 100644 --- a/pype/tools/settings/local_settings/constants.py +++ b/pype/tools/settings/local_settings/constants.py @@ -14,6 +14,8 @@ LOCAL_APPS_KEY = "applications" # Roots key constant LOCAL_ROOTS_KEY = "roots" +# Child offset in expandable widget +CHILD_OFFSET = 15 __all__ = ( "LABEL_REMOVE_DEFAULT", diff --git a/pype/tools/settings/local_settings/widgets.py b/pype/tools/settings/local_settings/widgets.py index d69ad816e0..1b077f93be 100644 --- a/pype/tools/settings/local_settings/widgets.py +++ b/pype/tools/settings/local_settings/widgets.py @@ -3,7 +3,6 @@ from pype.tools.settings.settings.widgets.widgets import ( ExpandingWidget, SpacerWidget ) -from pype.tools.settings.settings.widgets.lib import CHILD_OFFSET class Separator(QtWidgets.QFrame): @@ -50,3 +49,11 @@ class ProxyLabelWidget(QtWidgets.QWidget): if self.mouse_release_callback: return self.mouse_release_callback(event) return super(ProxyLabelWidget, self).mouseReleaseEvent(event) + + +__all__ = ( + "ExpandingWidget", + "SpacerWidget", + "Separator", + "SpacerWidget" +) diff --git a/pype/tools/settings/local_settings/window.py b/pype/tools/settings/local_settings/window.py index d8d8adfea9..87a276c78c 100644 --- a/pype/tools/settings/local_settings/window.py +++ b/pype/tools/settings/local_settings/window.py @@ -1,5 +1,5 @@ import logging -from Qt import QtWidgets, QtCore, QtGui +from Qt import QtWidgets, QtGui from ..settings import style @@ -13,10 +13,8 @@ from pype.api import ( ) from .widgets import ( - Separator, SpacerWidget, - ExpandingWidget, - CHILD_OFFSET + ExpandingWidget ) from .mongo_widget import PypeMongoWidget from .general_widget import LocalGeneralWidgets @@ -24,6 +22,7 @@ from .apps_widget import LocalApplicationsWidgets from .projects_widget import ProjectSettingsWidget from .constants import ( + CHILD_OFFSET, LOCAL_GENERAL_KEY, LOCAL_PROJECTS_KEY, LOCAL_APPS_KEY From 45dbc6d5263067b90be5914ecb2d4da5d75c0531 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 25 Feb 2021 17:13:33 +0100 Subject: [PATCH 89/91] added `__all__` to local settings init file --- pype/tools/settings/local_settings/__init__.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pype/tools/settings/local_settings/__init__.py b/pype/tools/settings/local_settings/__init__.py index ddb9741b79..135a719a09 100644 --- a/pype/tools/settings/local_settings/__init__.py +++ b/pype/tools/settings/local_settings/__init__.py @@ -1 +1,6 @@ from .window import LocalSettingsWindow + + +__all__ = ( + "LocalSettingsWindow", +) From 03b9908570266b03dfc4f56a65abd8eaebf3ebdb Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 25 Feb 2021 17:14:50 +0100 Subject: [PATCH 90/91] added modifications to local settings md --- pype/settings/local_settings.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/settings/local_settings.md b/pype/settings/local_settings.md index 66c5b753d5..fbb5cf3df1 100644 --- a/pype/settings/local_settings.md +++ b/pype/settings/local_settings.md @@ -10,7 +10,7 @@ ```json { "general": { - "site_name": "MySite" + "site_label": "MySite" } } ``` @@ -55,7 +55,7 @@ "_id": "", "site_id": "", "general": { - "site_name": "MySite" + "site_label": "MySite" }, "applications": { "": { From 6e0f0f512df59192fde3a8b6f98bf5a201e2c3e0 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 26 Feb 2021 10:36:32 +0100 Subject: [PATCH 91/91] query of subset documents is now explicit by asset id and subset name --- .../plugins/publish/collect_anatomy_instance_data.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pype/plugins/publish/collect_anatomy_instance_data.py b/pype/plugins/publish/collect_anatomy_instance_data.py index 446f671b86..99fbe8a52e 100644 --- a/pype/plugins/publish/collect_anatomy_instance_data.py +++ b/pype/plugins/publish/collect_anatomy_instance_data.py @@ -123,8 +123,7 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): self.log.debug("Qeurying latest versions for instances.") hierarchy = {} - subset_names = set() - asset_ids = set() + subset_filters = [] for instance in context: # Make sure `"latestVersion"` key is set latest_version = instance.data.get("latestVersion") @@ -138,8 +137,6 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): # 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: @@ -147,11 +144,14 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): if subset_name not in hierarchy[asset_id]: hierarchy[asset_id][subset_name] = [] hierarchy[asset_id][subset_name].append(instance) + subset_filters.append({ + "parent": asset_id, + "name": subset_name + }) subset_docs = list(io.find({ "type": "subset", - "parent": {"$in": list(asset_ids)}, - "name": {"$in": list(subset_names)} + "$or": subset_filters })) subset_ids = [