mirror of
https://github.com/ynput/ayon-core.git
synced 2026-01-01 16:34:53 +01:00
Merge branch 'develop' into enhancement/OP-2075_version-handling
This commit is contained in:
commit
247c931c0f
310 changed files with 6874 additions and 1047 deletions
|
|
@ -1,9 +1,15 @@
|
|||
import sys
|
||||
import json
|
||||
import traceback
|
||||
|
||||
from Qt import QtWidgets, QtGui, QtCore
|
||||
|
||||
from openpype.settings.entities import ProjectSettings
|
||||
from openpype.tools.settings import CHILD_OFFSET
|
||||
|
||||
from .widgets import ExpandingWidget
|
||||
from .lib import create_deffered_value_change_timer
|
||||
from .constants import DEFAULT_PROJECT_LABEL
|
||||
|
||||
|
||||
class BaseWidget(QtWidgets.QWidget):
|
||||
|
|
@ -119,9 +125,10 @@ class BaseWidget(QtWidgets.QWidget):
|
|||
return
|
||||
|
||||
def discard_changes():
|
||||
self.ignore_input_changes.set_ignore(True)
|
||||
self.entity.discard_changes()
|
||||
self.ignore_input_changes.set_ignore(False)
|
||||
with self.category_widget.working_state_context():
|
||||
self.ignore_input_changes.set_ignore(True)
|
||||
self.entity.discard_changes()
|
||||
self.ignore_input_changes.set_ignore(False)
|
||||
|
||||
action = QtWidgets.QAction("Discard changes")
|
||||
actions_mapping[action] = discard_changes
|
||||
|
|
@ -133,8 +140,11 @@ class BaseWidget(QtWidgets.QWidget):
|
|||
if not self.entity.can_trigger_add_to_studio_default:
|
||||
return
|
||||
|
||||
def add_to_studio_default():
|
||||
with self.category_widget.working_state_context():
|
||||
self.entity.add_to_studio_default()
|
||||
action = QtWidgets.QAction("Add to studio default")
|
||||
actions_mapping[action] = self.entity.add_to_studio_default
|
||||
actions_mapping[action] = add_to_studio_default
|
||||
menu.addAction(action)
|
||||
|
||||
def _remove_from_studio_default_action(self, menu, actions_mapping):
|
||||
|
|
@ -142,9 +152,10 @@ class BaseWidget(QtWidgets.QWidget):
|
|||
return
|
||||
|
||||
def remove_from_studio_default():
|
||||
self.ignore_input_changes.set_ignore(True)
|
||||
self.entity.remove_from_studio_default()
|
||||
self.ignore_input_changes.set_ignore(False)
|
||||
with self.category_widget.working_state_context():
|
||||
self.ignore_input_changes.set_ignore(True)
|
||||
self.entity.remove_from_studio_default()
|
||||
self.ignore_input_changes.set_ignore(False)
|
||||
action = QtWidgets.QAction("Remove from studio default")
|
||||
actions_mapping[action] = remove_from_studio_default
|
||||
menu.addAction(action)
|
||||
|
|
@ -153,8 +164,12 @@ class BaseWidget(QtWidgets.QWidget):
|
|||
if not self.entity.can_trigger_add_to_project_override:
|
||||
return
|
||||
|
||||
def add_to_project_override():
|
||||
with self.category_widget.working_state_context():
|
||||
self.entity.add_to_project_override
|
||||
|
||||
action = QtWidgets.QAction("Add to project project override")
|
||||
actions_mapping[action] = self.entity.add_to_project_override
|
||||
actions_mapping[action] = add_to_project_override
|
||||
menu.addAction(action)
|
||||
|
||||
def _remove_from_project_override_action(self, menu, actions_mapping):
|
||||
|
|
@ -162,9 +177,11 @@ class BaseWidget(QtWidgets.QWidget):
|
|||
return
|
||||
|
||||
def remove_from_project_override():
|
||||
self.ignore_input_changes.set_ignore(True)
|
||||
self.entity.remove_from_project_override()
|
||||
self.ignore_input_changes.set_ignore(False)
|
||||
with self.category_widget.working_state_context():
|
||||
self.ignore_input_changes.set_ignore(True)
|
||||
self.entity.remove_from_project_override()
|
||||
self.ignore_input_changes.set_ignore(False)
|
||||
|
||||
action = QtWidgets.QAction("Remove from project override")
|
||||
actions_mapping[action] = remove_from_project_override
|
||||
menu.addAction(action)
|
||||
|
|
@ -266,14 +283,16 @@ class BaseWidget(QtWidgets.QWidget):
|
|||
|
||||
# Simple paste value method
|
||||
def paste_value():
|
||||
_set_entity_value(self.entity, value)
|
||||
with self.category_widget.working_state_context():
|
||||
_set_entity_value(self.entity, value)
|
||||
|
||||
action = QtWidgets.QAction("Paste", menu)
|
||||
output.append((action, paste_value))
|
||||
|
||||
# Paste value to matchin entity
|
||||
def paste_value_to_path():
|
||||
_set_entity_value(matching_entity, value)
|
||||
with self.category_widget.working_state_context():
|
||||
_set_entity_value(matching_entity, value)
|
||||
|
||||
if matching_entity is not None:
|
||||
action = QtWidgets.QAction("Paste to same place", menu)
|
||||
|
|
@ -281,6 +300,68 @@ class BaseWidget(QtWidgets.QWidget):
|
|||
|
||||
return output
|
||||
|
||||
def _apply_values_from_project_action(self, menu, actions_mapping):
|
||||
for attr_name in ("project_name", "get_project_names"):
|
||||
if not hasattr(self.category_widget, attr_name):
|
||||
return
|
||||
|
||||
if self.entity.is_dynamic_item or self.entity.is_in_dynamic_item:
|
||||
return
|
||||
|
||||
current_project_name = self.category_widget.project_name
|
||||
project_names = []
|
||||
for project_name in self.category_widget.get_project_names():
|
||||
if project_name != current_project_name:
|
||||
project_names.append(project_name)
|
||||
|
||||
if not project_names:
|
||||
return
|
||||
|
||||
submenu = QtWidgets.QMenu("Apply values from", menu)
|
||||
|
||||
for project_name in project_names:
|
||||
if project_name is None:
|
||||
project_name = DEFAULT_PROJECT_LABEL
|
||||
|
||||
action = QtWidgets.QAction(project_name)
|
||||
submenu.addAction(action)
|
||||
actions_mapping[action] = lambda: self._apply_values_from_project(
|
||||
project_name
|
||||
)
|
||||
menu.addMenu(submenu)
|
||||
|
||||
def _apply_values_from_project(self, project_name):
|
||||
with self.category_widget.working_state_context():
|
||||
try:
|
||||
path_keys = [
|
||||
item
|
||||
for item in self.entity.path.split("/")
|
||||
if item
|
||||
]
|
||||
entity = ProjectSettings(project_name)
|
||||
for key in path_keys:
|
||||
entity = entity[key]
|
||||
self.entity.set(entity.value)
|
||||
|
||||
except Exception:
|
||||
if project_name is None:
|
||||
project_name = DEFAULT_PROJECT_LABEL
|
||||
|
||||
# TODO better message
|
||||
title = "Applying values failed"
|
||||
msg = "Applying values from project \"{}\" failed.".format(
|
||||
project_name
|
||||
)
|
||||
detail_msg = "".join(
|
||||
traceback.format_exception(*sys.exc_info())
|
||||
)
|
||||
dialog = QtWidgets.QMessageBox(self)
|
||||
dialog.setWindowTitle(title)
|
||||
dialog.setIcon(QtWidgets.QMessageBox.Warning)
|
||||
dialog.setText(msg)
|
||||
dialog.setDetailedText(detail_msg)
|
||||
dialog.exec_()
|
||||
|
||||
def show_actions_menu(self, event=None):
|
||||
if event and event.button() != QtCore.Qt.RightButton:
|
||||
return
|
||||
|
|
@ -299,6 +380,7 @@ class BaseWidget(QtWidgets.QWidget):
|
|||
self._remove_from_studio_default_action(menu, actions_mapping)
|
||||
self._add_to_project_override_action(menu, actions_mapping)
|
||||
self._remove_from_project_override_action(menu, actions_mapping)
|
||||
self._apply_values_from_project_action(menu, actions_mapping)
|
||||
|
||||
ui_actions = []
|
||||
ui_actions.extend(self._copy_value_actions(menu))
|
||||
|
|
@ -481,7 +563,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)
|
||||
|
|
@ -497,6 +581,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:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import os
|
||||
import sys
|
||||
import traceback
|
||||
import contextlib
|
||||
from enum import Enum
|
||||
from Qt import QtWidgets, QtCore, QtGui
|
||||
|
||||
|
|
@ -85,6 +86,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)
|
||||
|
|
@ -274,6 +276,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)
|
||||
|
||||
|
|
@ -316,6 +349,12 @@ class SettingsCategoryWidget(QtWidgets.QWidget):
|
|||
)
|
||||
self.content_layout.addWidget(widget, 0)
|
||||
|
||||
@contextlib.contextmanager
|
||||
def working_state_context(self):
|
||||
self.set_state(CategoryState.Working)
|
||||
yield
|
||||
self.set_state(CategoryState.Idle)
|
||||
|
||||
def save(self):
|
||||
if not self.items_are_valid():
|
||||
return
|
||||
|
|
@ -555,6 +594,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 +638,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
|
||||
|
||||
|
|
@ -606,6 +668,14 @@ class ProjectWidget(SettingsCategoryWidget):
|
|||
|
||||
self.project_list_widget = project_list_widget
|
||||
|
||||
def get_project_names(self):
|
||||
if (
|
||||
self.modify_defaults_checkbox
|
||||
and self.modify_defaults_checkbox.isChecked()
|
||||
):
|
||||
return []
|
||||
return self.project_list_widget.get_project_names()
|
||||
|
||||
def on_saved(self, saved_tab_widget):
|
||||
"""Callback on any tab widget save.
|
||||
|
||||
|
|
|
|||
|
|
@ -970,6 +970,13 @@ class ProjectListWidget(QtWidgets.QWidget):
|
|||
index, QtCore.QItemSelectionModel.SelectionFlag.SelectCurrent
|
||||
)
|
||||
|
||||
def get_project_names(self):
|
||||
output = []
|
||||
for row in range(self.project_proxy.rowCount()):
|
||||
index = self.project_proxy.index(row, 0)
|
||||
output.append(index.data(PROJECT_NAME_ROLE))
|
||||
return output
|
||||
|
||||
def refresh(self):
|
||||
selected_project = None
|
||||
for index in self.project_list.selectedIndexes():
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue