mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
[Automated] Merged develop into main
This commit is contained in:
commit
1355563462
29 changed files with 245 additions and 32 deletions
|
|
@ -1,3 +1,5 @@
|
|||
import re
|
||||
|
||||
import pyblish.api
|
||||
import openpype.api
|
||||
from avalon import photoshop
|
||||
|
|
@ -19,20 +21,33 @@ class ValidateNamingRepair(pyblish.api.Action):
|
|||
and result["instance"] not in failed):
|
||||
failed.append(result["instance"])
|
||||
|
||||
invalid_chars, replace_char = plugin.get_replace_chars()
|
||||
self.log.info("{} --- {}".format(invalid_chars, replace_char))
|
||||
|
||||
# Apply pyblish.logic to get the instances for the plug-in
|
||||
instances = pyblish.api.instances_by_plugin(failed, plugin)
|
||||
stub = photoshop.stub()
|
||||
for instance in instances:
|
||||
self.log.info("validate_naming instance {}".format(instance))
|
||||
name = instance.data["name"].replace(" ", "_")
|
||||
name = name.replace(instance.data["family"], '')
|
||||
instance[0].Name = name
|
||||
data = stub.read(instance[0])
|
||||
data["subset"] = "image" + name
|
||||
stub.imprint(instance[0], data)
|
||||
metadata = stub.read(instance[0])
|
||||
self.log.info("metadata instance {}".format(metadata))
|
||||
layer_name = None
|
||||
if metadata.get("uuid"):
|
||||
layer_data = stub.get_layer(metadata["uuid"])
|
||||
self.log.info("layer_data {}".format(layer_data))
|
||||
if layer_data:
|
||||
layer_name = re.sub(invalid_chars,
|
||||
replace_char,
|
||||
layer_data.name)
|
||||
|
||||
name = stub.PUBLISH_ICON + name
|
||||
stub.rename_layer(instance.data["uuid"], name)
|
||||
stub.rename_layer(instance.data["uuid"], layer_name)
|
||||
|
||||
subset_name = re.sub(invalid_chars, replace_char,
|
||||
instance.data["name"])
|
||||
|
||||
instance[0].Name = layer_name or subset_name
|
||||
metadata["subset"] = subset_name
|
||||
stub.imprint(instance[0], metadata)
|
||||
|
||||
return True
|
||||
|
||||
|
|
@ -49,12 +64,21 @@ class ValidateNaming(pyblish.api.InstancePlugin):
|
|||
families = ["image"]
|
||||
actions = [ValidateNamingRepair]
|
||||
|
||||
# configured by Settings
|
||||
invalid_chars = ''
|
||||
replace_char = ''
|
||||
|
||||
def process(self, instance):
|
||||
help_msg = ' Use Repair action (A) in Pyblish to fix it.'
|
||||
msg = "Name \"{}\" is not allowed.{}".format(instance.data["name"],
|
||||
help_msg)
|
||||
assert " " not in instance.data["name"], msg
|
||||
assert not re.search(self.invalid_chars, instance.data["name"]), msg
|
||||
|
||||
msg = "Subset \"{}\" is not allowed.{}".format(instance.data["subset"],
|
||||
help_msg)
|
||||
assert " " not in instance.data["subset"], msg
|
||||
assert not re.search(self.invalid_chars, instance.data["subset"]), msg
|
||||
|
||||
@classmethod
|
||||
def get_replace_chars(cls):
|
||||
"""Pass values configured in Settings for Repair."""
|
||||
return cls.invalid_chars, cls.replace_char
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ DEFAULT_OPENPYPE_MODULES = (
|
|||
"project_manager_action",
|
||||
"settings_action",
|
||||
"standalonepublish_action",
|
||||
"job_queue",
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -7,11 +7,6 @@
|
|||
}
|
||||
},
|
||||
"publish": {
|
||||
"ValidateContainers": {
|
||||
"enabled": true,
|
||||
"optional": true,
|
||||
"active": true
|
||||
},
|
||||
"CollectRemoteInstances": {
|
||||
"color_code_mapping": [
|
||||
{
|
||||
|
|
@ -22,6 +17,15 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"ValidateContainers": {
|
||||
"enabled": true,
|
||||
"optional": true,
|
||||
"active": true
|
||||
},
|
||||
"ValidateNaming": {
|
||||
"invalid_chars": "[ \\\\/+\\*\\?\\(\\)\\[\\]\\{\\}:,]",
|
||||
"replace_char": "_"
|
||||
},
|
||||
"ExtractImage": {
|
||||
"formats": [
|
||||
"png",
|
||||
|
|
|
|||
|
|
@ -235,6 +235,11 @@ class BaseItemEntity(BaseEntity):
|
|||
"""Return system settings entity."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def has_child_with_key(self, key):
|
||||
"""Entity contains key as children."""
|
||||
pass
|
||||
|
||||
def schema_validations(self):
|
||||
"""Validate schema of entity and it's hierachy.
|
||||
|
||||
|
|
|
|||
|
|
@ -107,6 +107,9 @@ class DictConditionalEntity(ItemEntity):
|
|||
for _key, _value in new_value.items():
|
||||
self.non_gui_children[self.current_enum][_key].set(_value)
|
||||
|
||||
def has_child_with_key(self, key):
|
||||
return key in self.keys()
|
||||
|
||||
def _item_initialization(self):
|
||||
self._default_metadata = NOT_SET
|
||||
self._studio_override_metadata = NOT_SET
|
||||
|
|
|
|||
|
|
@ -205,6 +205,9 @@ class DictImmutableKeysEntity(ItemEntity):
|
|||
)
|
||||
self.show_borders = self.schema_data.get("show_borders", True)
|
||||
|
||||
def has_child_with_key(self, key):
|
||||
return key in self.non_gui_children
|
||||
|
||||
def collect_static_entities_by_path(self):
|
||||
output = {}
|
||||
if self.is_dynamic_item or self.is_in_dynamic_item:
|
||||
|
|
|
|||
|
|
@ -60,6 +60,12 @@ class DictMutableKeysEntity(EndpointEntity):
|
|||
def pop(self, key, *args, **kwargs):
|
||||
if key in self.required_keys:
|
||||
raise RequiredKeyModified(self.path, key)
|
||||
|
||||
if self._override_state is OverrideState.STUDIO:
|
||||
self._has_studio_override = True
|
||||
elif self._override_state is OverrideState.PROJECT:
|
||||
self._has_project_override = True
|
||||
|
||||
result = self.children_by_key.pop(key, *args, **kwargs)
|
||||
self.on_change()
|
||||
return result
|
||||
|
|
@ -191,6 +197,9 @@ class DictMutableKeysEntity(EndpointEntity):
|
|||
child_entity = self.children_by_key[key]
|
||||
self.set_child_label(child_entity, label)
|
||||
|
||||
def has_child_with_key(self, key):
|
||||
return key in self.children_by_key
|
||||
|
||||
def _item_initialization(self):
|
||||
self._default_metadata = {}
|
||||
self._studio_override_metadata = {}
|
||||
|
|
|
|||
|
|
@ -118,6 +118,9 @@ class InputEntity(EndpointEntity):
|
|||
return self.value == other.value
|
||||
return self.value == other
|
||||
|
||||
def has_child_with_key(self, key):
|
||||
return False
|
||||
|
||||
def get_child_path(self, child_obj):
|
||||
raise TypeError("{} can't have children".format(
|
||||
self.__class__.__name__
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
import re
|
||||
|
||||
import six
|
||||
|
||||
from .lib import (
|
||||
NOT_SET,
|
||||
STRING_TYPE,
|
||||
|
|
@ -48,6 +52,9 @@ class PathEntity(ItemEntity):
|
|||
raise AttributeError(self.attribute_error_msg.format("items"))
|
||||
return self.child_obj.items()
|
||||
|
||||
def has_child_with_key(self, key):
|
||||
return self.child_obj.has_child_with_key(key)
|
||||
|
||||
def _item_initialization(self):
|
||||
if self.group_item is None and not self.is_group:
|
||||
self.is_group = True
|
||||
|
|
@ -197,6 +204,7 @@ class PathEntity(ItemEntity):
|
|||
|
||||
class ListStrictEntity(ItemEntity):
|
||||
schema_types = ["list-strict"]
|
||||
_key_regex = re.compile(r"[0-9]+")
|
||||
|
||||
def __getitem__(self, idx):
|
||||
if not isinstance(idx, int):
|
||||
|
|
@ -216,6 +224,19 @@ class ListStrictEntity(ItemEntity):
|
|||
return self.children[idx]
|
||||
return default
|
||||
|
||||
def has_child_with_key(self, key):
|
||||
if (
|
||||
key
|
||||
and isinstance(key, six.string_types)
|
||||
and self._key_regex.match(key)
|
||||
):
|
||||
key = int(key)
|
||||
|
||||
if not isinstance(key, int):
|
||||
return False
|
||||
|
||||
return 0 <= key < len(self.children)
|
||||
|
||||
def _item_initialization(self):
|
||||
self.valid_value_types = (list, )
|
||||
self.require_key = True
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import copy
|
||||
import six
|
||||
import re
|
||||
from . import (
|
||||
BaseEntity,
|
||||
EndpointEntity
|
||||
|
|
@ -21,6 +23,7 @@ class ListEntity(EndpointEntity):
|
|||
"collapsible": True,
|
||||
"collapsed": False
|
||||
}
|
||||
_key_regex = re.compile(r"[0-9]+")
|
||||
|
||||
def __iter__(self):
|
||||
for item in self.children:
|
||||
|
|
@ -144,6 +147,19 @@ class ListEntity(EndpointEntity):
|
|||
)
|
||||
self.on_change()
|
||||
|
||||
def has_child_with_key(self, key):
|
||||
if (
|
||||
key
|
||||
and isinstance(key, six.string_types)
|
||||
and self._key_regex.match(key)
|
||||
):
|
||||
key = int(key)
|
||||
|
||||
if not isinstance(key, int):
|
||||
return False
|
||||
|
||||
return 0 <= key < len(self.children)
|
||||
|
||||
def _convert_to_valid_type(self, value):
|
||||
if isinstance(value, (set, tuple)):
|
||||
return list(value)
|
||||
|
|
|
|||
|
|
@ -127,6 +127,9 @@ class RootEntity(BaseItemEntity):
|
|||
for _key, _value in new_value.items():
|
||||
self.non_gui_children[_key].set(_value)
|
||||
|
||||
def has_child_with_key(self, key):
|
||||
return key in self.non_gui_children
|
||||
|
||||
def keys(self):
|
||||
return self.non_gui_children.keys()
|
||||
|
||||
|
|
|
|||
|
|
@ -33,16 +33,6 @@
|
|||
"key": "publish",
|
||||
"label": "Publish plugins",
|
||||
"children": [
|
||||
{
|
||||
"type": "schema_template",
|
||||
"name": "template_publish_plugin",
|
||||
"template_data": [
|
||||
{
|
||||
"key": "ValidateContainers",
|
||||
"label": "ValidateContainers"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
|
|
@ -108,6 +98,38 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "schema_template",
|
||||
"name": "template_publish_plugin",
|
||||
"template_data": [
|
||||
{
|
||||
"key": "ValidateContainers",
|
||||
"label": "ValidateContainers"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "ValidateNaming",
|
||||
"label": "Validate naming of subsets and layers",
|
||||
"children": [
|
||||
{
|
||||
"type": "label",
|
||||
"label": "Subset cannot contain invalid characters or extract to file would fail"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "invalid_chars",
|
||||
"label": "Regex pattern of invalid characters"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "replace_char",
|
||||
"label": "Replacement character"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
|
|
|
|||
|
|
@ -36,6 +36,11 @@
|
|||
"app_variant_label": "2021",
|
||||
"app_variant": "2021",
|
||||
"variant_skip_paths": ["use_python_2"]
|
||||
},
|
||||
{
|
||||
"app_variant_label": "2022",
|
||||
"app_variant": "2022",
|
||||
"variant_skip_paths": ["use_python_2"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -554,7 +554,9 @@ class GUIWidget(BaseWidget):
|
|||
def _create_label_ui(self):
|
||||
label = self.entity["label"]
|
||||
label_widget = QtWidgets.QLabel(label, self)
|
||||
label_widget.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction)
|
||||
label_widget.setObjectName("SettingsLabel")
|
||||
label_widget.linkActivated.connect(self._on_link_activate)
|
||||
|
||||
layout = QtWidgets.QHBoxLayout(self)
|
||||
layout.setContentsMargins(0, 5, 0, 5)
|
||||
|
|
@ -570,6 +572,14 @@ class GUIWidget(BaseWidget):
|
|||
layout.setContentsMargins(5, 5, 5, 5)
|
||||
layout.addWidget(splitter_item)
|
||||
|
||||
def _on_link_activate(self, url):
|
||||
if not url.startswith("settings://"):
|
||||
QtGui.QDesktopServices.openUrl(url)
|
||||
return
|
||||
|
||||
path = url.replace("settings://", "")
|
||||
self.category_widget.go_to_fullpath(path)
|
||||
|
||||
def set_entity_value(self):
|
||||
pass
|
||||
|
||||
|
|
|
|||
|
|
@ -71,17 +71,35 @@ class SettingsBreadcrumbs(BreadcrumbsModel):
|
|||
return True
|
||||
return False
|
||||
|
||||
def get_valid_path(self, path):
|
||||
if not path:
|
||||
return ""
|
||||
|
||||
path_items = path.split("/")
|
||||
new_path_items = []
|
||||
entity = self.entity
|
||||
for item in path_items:
|
||||
if not entity.has_child_with_key(item):
|
||||
break
|
||||
|
||||
new_path_items.append(item)
|
||||
entity = entity[item]
|
||||
|
||||
return "/".join(new_path_items)
|
||||
|
||||
def is_valid_path(self, path):
|
||||
if not path:
|
||||
return True
|
||||
|
||||
path_items = path.split("/")
|
||||
try:
|
||||
entity = self.entity
|
||||
for item in path_items:
|
||||
entity = entity[item]
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
entity = self.entity
|
||||
for item in path_items:
|
||||
if not entity.has_child_with_key(item):
|
||||
return False
|
||||
|
||||
entity = entity[item]
|
||||
|
||||
return True
|
||||
|
||||
|
||||
|
|
@ -436,6 +454,7 @@ class BreadcrumbsAddressBar(QtWidgets.QFrame):
|
|||
self.change_path(path)
|
||||
|
||||
def change_path(self, path):
|
||||
path = self._model.get_valid_path(path)
|
||||
if self._model and not self._model.is_valid_path(path):
|
||||
self._show_address_field()
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -82,6 +82,7 @@ class SettingsCategoryWidget(QtWidgets.QWidget):
|
|||
state_changed = QtCore.Signal()
|
||||
saved = QtCore.Signal(QtWidgets.QWidget)
|
||||
restart_required_trigger = QtCore.Signal()
|
||||
full_path_requested = QtCore.Signal(str, str)
|
||||
|
||||
def __init__(self, user_role, parent=None):
|
||||
super(SettingsCategoryWidget, self).__init__(parent)
|
||||
|
|
@ -268,6 +269,37 @@ class SettingsCategoryWidget(QtWidgets.QWidget):
|
|||
# Scroll to widget
|
||||
self.scroll_widget.ensureWidgetVisible(widget)
|
||||
|
||||
def go_to_fullpath(self, full_path):
|
||||
"""Full path of settings entity which can lead to different category.
|
||||
|
||||
Args:
|
||||
full_path (str): Full path to settings entity. It is expected that
|
||||
path starts with category name ("system_setting" etc.).
|
||||
"""
|
||||
if not full_path:
|
||||
return
|
||||
items = full_path.split("/")
|
||||
category = items[0]
|
||||
path = ""
|
||||
if len(items) > 1:
|
||||
path = "/".join(items[1:])
|
||||
self.full_path_requested.emit(category, path)
|
||||
|
||||
def contain_category_key(self, category):
|
||||
"""Parent widget ask if category of full path lead to this widget.
|
||||
|
||||
Args:
|
||||
category (str): The category name.
|
||||
|
||||
Returns:
|
||||
bool: Passed category lead to this widget.
|
||||
"""
|
||||
return False
|
||||
|
||||
def set_category_path(self, category, path):
|
||||
"""Change path of widget based on category full path."""
|
||||
pass
|
||||
|
||||
def set_path(self, path):
|
||||
self.breadcrumbs_widget.set_path(path)
|
||||
|
||||
|
|
@ -555,6 +587,14 @@ class SettingsCategoryWidget(QtWidgets.QWidget):
|
|||
|
||||
|
||||
class SystemWidget(SettingsCategoryWidget):
|
||||
def contain_category_key(self, category):
|
||||
if category == "system_settings":
|
||||
return True
|
||||
return False
|
||||
|
||||
def set_category_path(self, category, path):
|
||||
self.breadcrumbs_widget.change_path(path)
|
||||
|
||||
def _create_root_entity(self):
|
||||
self.entity = SystemSettings(set_studio_state=False)
|
||||
self.entity.on_change_callbacks.append(self._on_entity_change)
|
||||
|
|
@ -591,6 +631,21 @@ class SystemWidget(SettingsCategoryWidget):
|
|||
|
||||
|
||||
class ProjectWidget(SettingsCategoryWidget):
|
||||
def contain_category_key(self, category):
|
||||
if category in ("project_settings", "project_anatomy"):
|
||||
return True
|
||||
return False
|
||||
|
||||
def set_category_path(self, category, path):
|
||||
if path:
|
||||
path_items = path.split("/")
|
||||
if path_items[0] not in ("project_settings", "project_anatomy"):
|
||||
path = "/".join([category, path])
|
||||
else:
|
||||
path = category
|
||||
|
||||
self.breadcrumbs_widget.change_path(path)
|
||||
|
||||
def initialize_attributes(self):
|
||||
self.project_name = None
|
||||
|
||||
|
|
|
|||
|
|
@ -63,7 +63,9 @@ class MainWidget(QtWidgets.QWidget):
|
|||
tab_widget.restart_required_trigger.connect(
|
||||
self._on_restart_required
|
||||
)
|
||||
tab_widget.full_path_requested.connect(self._on_full_path_request)
|
||||
|
||||
self._header_tab_widget = header_tab_widget
|
||||
self.tab_widgets = tab_widgets
|
||||
|
||||
def _on_tab_save(self, source_widget):
|
||||
|
|
@ -90,6 +92,14 @@ class MainWidget(QtWidgets.QWidget):
|
|||
if app:
|
||||
app.processEvents()
|
||||
|
||||
def _on_full_path_request(self, category, path):
|
||||
for tab_widget in self.tab_widgets:
|
||||
if tab_widget.contain_category_key(category):
|
||||
idx = self._header_tab_widget.indexOf(tab_widget)
|
||||
self._header_tab_widget.setCurrentIndex(idx)
|
||||
tab_widget.set_category_path(category, path)
|
||||
break
|
||||
|
||||
def showEvent(self, event):
|
||||
super(MainWidget, self).showEvent(event)
|
||||
if self._reset_on_show:
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 85c656fcf9beb06ab92d3d6ce47f6472cf88df54
|
||||
Subproject commit 4f10fb1255beb156f23afa1bb8362dfc53d0c6f8
|
||||
Loading…
Add table
Add a link
Reference in a new issue