mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-25 05:14:40 +01:00
Merge pull request #2570 from pypeclub/feature/OP-2361_Store-settings-by-OpenPype-version
General: Store settings by OpenPype version
This commit is contained in:
commit
f1c2acdfd3
20 changed files with 1722 additions and 293 deletions
|
|
@ -14,6 +14,15 @@ def get_resource(*args):
|
|||
return os.path.normpath(os.path.join(RESOURCES_DIR, *args))
|
||||
|
||||
|
||||
def get_image_path(*args):
|
||||
"""Helper function to get images.
|
||||
|
||||
Args:
|
||||
*<str>: Filepath part items.
|
||||
"""
|
||||
return get_resource("images", *args)
|
||||
|
||||
|
||||
def get_liberation_font_path(bold=False, italic=False):
|
||||
font_name = "LiberationSans"
|
||||
suffix = ""
|
||||
|
|
|
|||
BIN
openpype/resources/images/warning.png
Normal file
BIN
openpype/resources/images/warning.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.2 KiB |
|
|
@ -5,6 +5,8 @@ from .constants import (
|
|||
PROJECT_ANATOMY_KEY,
|
||||
LOCAL_SETTING_KEY,
|
||||
|
||||
LEGACY_SETTINGS_VERSION,
|
||||
|
||||
SCHEMA_KEY_SYSTEM_SETTINGS,
|
||||
SCHEMA_KEY_PROJECT_SETTINGS,
|
||||
|
||||
|
|
@ -37,6 +39,8 @@ __all__ = (
|
|||
"PROJECT_ANATOMY_KEY",
|
||||
"LOCAL_SETTING_KEY",
|
||||
|
||||
"LEGACY_SETTINGS_VERSION",
|
||||
|
||||
"SCHEMA_KEY_SYSTEM_SETTINGS",
|
||||
"SCHEMA_KEY_PROJECT_SETTINGS",
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ PROJECT_SETTINGS_KEY = "project_settings"
|
|||
PROJECT_ANATOMY_KEY = "project_anatomy"
|
||||
LOCAL_SETTING_KEY = "local_settings"
|
||||
|
||||
LEGACY_SETTINGS_VERSION = "legacy"
|
||||
|
||||
# Schema hub names
|
||||
SCHEMA_KEY_SYSTEM_SETTINGS = "system_schema"
|
||||
SCHEMA_KEY_PROJECT_SETTINGS = "projects_schema"
|
||||
|
|
|
|||
|
|
@ -34,15 +34,24 @@ from openpype.settings.lib import (
|
|||
reset_default_settings,
|
||||
|
||||
get_studio_system_settings_overrides,
|
||||
get_studio_system_settings_overrides_for_version,
|
||||
save_studio_settings,
|
||||
get_available_studio_system_settings_overrides_versions,
|
||||
|
||||
get_studio_project_settings_overrides,
|
||||
get_studio_project_settings_overrides_for_version,
|
||||
get_studio_project_anatomy_overrides,
|
||||
get_studio_project_anatomy_overrides_for_version,
|
||||
get_project_settings_overrides,
|
||||
get_project_settings_overrides_for_version,
|
||||
get_project_anatomy_overrides,
|
||||
save_project_settings,
|
||||
save_project_anatomy,
|
||||
|
||||
get_available_project_settings_overrides_versions,
|
||||
get_available_studio_project_settings_overrides_versions,
|
||||
get_available_studio_project_anatomy_overrides_versions,
|
||||
|
||||
find_environments,
|
||||
apply_overrides
|
||||
)
|
||||
|
|
@ -495,17 +504,27 @@ class SystemSettings(RootEntity):
|
|||
root_key = SYSTEM_SETTINGS_KEY
|
||||
|
||||
def __init__(
|
||||
self, set_studio_state=True, reset=True, schema_hub=None
|
||||
self,
|
||||
set_studio_state=True,
|
||||
reset=True,
|
||||
schema_hub=None,
|
||||
source_version=None
|
||||
):
|
||||
if schema_hub is None:
|
||||
# Load system schemas
|
||||
schema_hub = SchemasHub(SCHEMA_KEY_SYSTEM_SETTINGS)
|
||||
|
||||
self._source_version = source_version
|
||||
|
||||
super(SystemSettings, self).__init__(schema_hub, reset)
|
||||
|
||||
if set_studio_state:
|
||||
self.set_studio_state()
|
||||
|
||||
@property
|
||||
def source_version(self):
|
||||
return self._source_version
|
||||
|
||||
def get_entity_from_path(self, path):
|
||||
"""Return system settings entity."""
|
||||
path_parts = path.split("/")
|
||||
|
|
@ -524,12 +543,24 @@ class SystemSettings(RootEntity):
|
|||
value = default_value.get(key, NOT_SET)
|
||||
child_obj.update_default_value(value)
|
||||
|
||||
studio_overrides = get_studio_system_settings_overrides()
|
||||
if self._source_version is None:
|
||||
studio_overrides, version = get_studio_system_settings_overrides(
|
||||
return_version=True
|
||||
)
|
||||
self._source_version = version
|
||||
|
||||
else:
|
||||
studio_overrides = (
|
||||
get_studio_system_settings_overrides_for_version(
|
||||
self._source_version
|
||||
)
|
||||
)
|
||||
|
||||
for key, child_obj in self.non_gui_children.items():
|
||||
value = studio_overrides.get(key, NOT_SET)
|
||||
child_obj.update_studio_value(value)
|
||||
|
||||
def reset(self, new_state=None):
|
||||
def reset(self, new_state=None, source_version=None):
|
||||
"""Discard changes and reset entit's values.
|
||||
|
||||
Reload default values and studio override values and update entities.
|
||||
|
|
@ -547,9 +578,22 @@ class SystemSettings(RootEntity):
|
|||
if new_state is OverrideState.PROJECT:
|
||||
raise ValueError("System settings can't store poject overrides.")
|
||||
|
||||
if source_version is not None:
|
||||
self._source_version = source_version
|
||||
|
||||
self._reset_values()
|
||||
self.set_override_state(new_state)
|
||||
|
||||
def get_available_source_versions(self, sorted=None):
|
||||
if self.is_in_studio_state():
|
||||
return self.get_available_studio_versions(sorted=sorted)
|
||||
return []
|
||||
|
||||
def get_available_studio_versions(self, sorted=None):
|
||||
return get_available_studio_system_settings_overrides_versions(
|
||||
sorted=sorted
|
||||
)
|
||||
|
||||
def defaults_dir(self):
|
||||
"""Path to defaults directory.
|
||||
|
||||
|
|
@ -566,6 +610,8 @@ class SystemSettings(RootEntity):
|
|||
json.dumps(settings_value, indent=4)
|
||||
))
|
||||
save_studio_settings(settings_value)
|
||||
# Reset source version after restart
|
||||
self._source_version = None
|
||||
|
||||
def _validate_defaults_to_save(self, value):
|
||||
"""Valiations of default values before save."""
|
||||
|
|
@ -622,11 +668,15 @@ class ProjectSettings(RootEntity):
|
|||
project_name=None,
|
||||
change_state=True,
|
||||
reset=True,
|
||||
schema_hub=None
|
||||
schema_hub=None,
|
||||
source_version=None,
|
||||
anatomy_source_version=None
|
||||
):
|
||||
self._project_name = project_name
|
||||
|
||||
self._system_settings_entity = None
|
||||
self._source_version = source_version
|
||||
self._anatomy_source_version = anatomy_source_version
|
||||
|
||||
if schema_hub is None:
|
||||
# Load system schemas
|
||||
|
|
@ -640,6 +690,14 @@ class ProjectSettings(RootEntity):
|
|||
else:
|
||||
self.set_project_state()
|
||||
|
||||
@property
|
||||
def source_version(self):
|
||||
return self._source_version
|
||||
|
||||
@property
|
||||
def anatomy_source_version(self):
|
||||
return self._anatomy_source_version
|
||||
|
||||
@property
|
||||
def project_name(self):
|
||||
return self._project_name
|
||||
|
|
@ -682,23 +740,20 @@ class ProjectSettings(RootEntity):
|
|||
output = output[path_part]
|
||||
return output
|
||||
|
||||
def change_project(self, project_name):
|
||||
def change_project(self, project_name, source_version=None):
|
||||
if project_name == self._project_name:
|
||||
return
|
||||
if (
|
||||
source_version is None
|
||||
or source_version == self._source_version
|
||||
):
|
||||
if not self.is_in_project_state():
|
||||
self.set_project_state()
|
||||
return
|
||||
|
||||
self._project_name = project_name
|
||||
if project_name is None:
|
||||
self.set_studio_state()
|
||||
return
|
||||
|
||||
project_override_value = {
|
||||
PROJECT_SETTINGS_KEY: get_project_settings_overrides(project_name),
|
||||
PROJECT_ANATOMY_KEY: get_project_anatomy_overrides(project_name)
|
||||
}
|
||||
for key, child_obj in self.non_gui_children.items():
|
||||
value = project_override_value.get(key, NOT_SET)
|
||||
child_obj.update_project_value(value)
|
||||
self._source_version = source_version
|
||||
self._anatomy_source_version = None
|
||||
|
||||
self._set_values_for_project(project_name)
|
||||
self.set_project_state()
|
||||
|
||||
def _reset_values(self):
|
||||
|
|
@ -710,27 +765,97 @@ class ProjectSettings(RootEntity):
|
|||
value = default_values.get(key, NOT_SET)
|
||||
child_obj.update_default_value(value)
|
||||
|
||||
self._set_values_for_project(self.project_name)
|
||||
|
||||
def _set_values_for_project(self, project_name):
|
||||
self._project_name = project_name
|
||||
if project_name:
|
||||
project_settings_overrides = (
|
||||
get_studio_project_settings_overrides()
|
||||
)
|
||||
project_anatomy_overrides = (
|
||||
get_studio_project_anatomy_overrides()
|
||||
)
|
||||
else:
|
||||
if self._source_version is None:
|
||||
project_settings_overrides, version = (
|
||||
get_studio_project_settings_overrides(return_version=True)
|
||||
)
|
||||
self._source_version = version
|
||||
else:
|
||||
project_settings_overrides = (
|
||||
get_studio_project_settings_overrides_for_version(
|
||||
self._source_version
|
||||
)
|
||||
)
|
||||
|
||||
if self._anatomy_source_version is None:
|
||||
project_anatomy_overrides, anatomy_version = (
|
||||
get_studio_project_anatomy_overrides(return_version=True)
|
||||
)
|
||||
self._anatomy_source_version = anatomy_version
|
||||
else:
|
||||
project_anatomy_overrides = (
|
||||
get_studio_project_anatomy_overrides_for_version(
|
||||
self._anatomy_source_version
|
||||
)
|
||||
)
|
||||
|
||||
studio_overrides = {
|
||||
PROJECT_SETTINGS_KEY: get_studio_project_settings_overrides(),
|
||||
PROJECT_ANATOMY_KEY: get_studio_project_anatomy_overrides()
|
||||
PROJECT_SETTINGS_KEY: project_settings_overrides,
|
||||
PROJECT_ANATOMY_KEY: project_anatomy_overrides
|
||||
}
|
||||
|
||||
for key, child_obj in self.non_gui_children.items():
|
||||
value = studio_overrides.get(key, NOT_SET)
|
||||
child_obj.update_studio_value(value)
|
||||
|
||||
if not self.project_name:
|
||||
if not project_name:
|
||||
return
|
||||
|
||||
project_name = self.project_name
|
||||
if self._source_version is None:
|
||||
project_settings_overrides, version = (
|
||||
get_project_settings_overrides(
|
||||
project_name, return_version=True
|
||||
)
|
||||
)
|
||||
self._source_version = version
|
||||
else:
|
||||
project_settings_overrides = (
|
||||
get_project_settings_overrides_for_version(
|
||||
project_name, self._source_version
|
||||
)
|
||||
)
|
||||
|
||||
project_override_value = {
|
||||
PROJECT_SETTINGS_KEY: get_project_settings_overrides(project_name),
|
||||
PROJECT_SETTINGS_KEY: project_settings_overrides,
|
||||
PROJECT_ANATOMY_KEY: get_project_anatomy_overrides(project_name)
|
||||
}
|
||||
for key, child_obj in self.non_gui_children.items():
|
||||
value = project_override_value.get(key, NOT_SET)
|
||||
child_obj.update_project_value(value)
|
||||
|
||||
def get_available_source_versions(self, sorted=None):
|
||||
if self.is_in_studio_state():
|
||||
return self.get_available_studio_versions(sorted=sorted)
|
||||
elif self.is_in_project_state():
|
||||
return get_available_project_settings_overrides_versions(
|
||||
self.project_name, sorted=sorted
|
||||
)
|
||||
return []
|
||||
|
||||
def get_available_studio_versions(self, sorted=None):
|
||||
return get_available_studio_project_settings_overrides_versions(
|
||||
sorted=sorted
|
||||
)
|
||||
|
||||
def get_available_anatomy_source_versions(self, sorted=None):
|
||||
if self.is_in_studio_state():
|
||||
return get_available_studio_project_anatomy_overrides_versions(
|
||||
sorted=sorted
|
||||
)
|
||||
return []
|
||||
|
||||
def reset(self, new_state=None):
|
||||
"""Discard changes and reset entit's values.
|
||||
|
||||
|
|
@ -763,6 +888,9 @@ class ProjectSettings(RootEntity):
|
|||
|
||||
self._validate_values_to_save(settings_value)
|
||||
|
||||
self._source_version = None
|
||||
self._anatomy_source_version = None
|
||||
|
||||
self.log.debug("Saving project settings: {}".format(
|
||||
json.dumps(settings_value, indent=4)
|
||||
))
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -266,23 +266,31 @@ def save_project_anatomy(project_name, anatomy_data):
|
|||
|
||||
|
||||
@require_handler
|
||||
def get_studio_system_settings_overrides():
|
||||
return _SETTINGS_HANDLER.get_studio_system_settings_overrides()
|
||||
def get_studio_system_settings_overrides(return_version=False):
|
||||
return _SETTINGS_HANDLER.get_studio_system_settings_overrides(
|
||||
return_version
|
||||
)
|
||||
|
||||
|
||||
@require_handler
|
||||
def get_studio_project_settings_overrides():
|
||||
return _SETTINGS_HANDLER.get_studio_project_settings_overrides()
|
||||
def get_studio_project_settings_overrides(return_version=False):
|
||||
return _SETTINGS_HANDLER.get_studio_project_settings_overrides(
|
||||
return_version
|
||||
)
|
||||
|
||||
|
||||
@require_handler
|
||||
def get_studio_project_anatomy_overrides():
|
||||
return _SETTINGS_HANDLER.get_studio_project_anatomy_overrides()
|
||||
def get_studio_project_anatomy_overrides(return_version=False):
|
||||
return _SETTINGS_HANDLER.get_studio_project_anatomy_overrides(
|
||||
return_version
|
||||
)
|
||||
|
||||
|
||||
@require_handler
|
||||
def get_project_settings_overrides(project_name):
|
||||
return _SETTINGS_HANDLER.get_project_settings_overrides(project_name)
|
||||
def get_project_settings_overrides(project_name, return_version=False):
|
||||
return _SETTINGS_HANDLER.get_project_settings_overrides(
|
||||
project_name, return_version
|
||||
)
|
||||
|
||||
|
||||
@require_handler
|
||||
|
|
@ -290,6 +298,123 @@ def get_project_anatomy_overrides(project_name):
|
|||
return _SETTINGS_HANDLER.get_project_anatomy_overrides(project_name)
|
||||
|
||||
|
||||
@require_handler
|
||||
def get_studio_system_settings_overrides_for_version(version):
|
||||
return (
|
||||
_SETTINGS_HANDLER
|
||||
.get_studio_system_settings_overrides_for_version(version)
|
||||
)
|
||||
|
||||
|
||||
@require_handler
|
||||
def get_studio_project_anatomy_overrides_for_version(version):
|
||||
return (
|
||||
_SETTINGS_HANDLER
|
||||
.get_studio_project_anatomy_overrides_for_version(version)
|
||||
)
|
||||
|
||||
|
||||
@require_handler
|
||||
def get_studio_project_settings_overrides_for_version(version):
|
||||
return (
|
||||
_SETTINGS_HANDLER
|
||||
.get_studio_project_settings_overrides_for_version(version)
|
||||
)
|
||||
|
||||
|
||||
@require_handler
|
||||
def get_project_settings_overrides_for_version(
|
||||
project_name, version
|
||||
):
|
||||
return (
|
||||
_SETTINGS_HANDLER
|
||||
.get_project_settings_overrides_for_version(project_name, version)
|
||||
)
|
||||
|
||||
|
||||
@require_handler
|
||||
def get_available_studio_system_settings_overrides_versions(sorted=None):
|
||||
return (
|
||||
_SETTINGS_HANDLER
|
||||
.get_available_studio_system_settings_overrides_versions(
|
||||
sorted=sorted
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@require_handler
|
||||
def get_available_studio_project_anatomy_overrides_versions(sorted=None):
|
||||
return (
|
||||
_SETTINGS_HANDLER
|
||||
.get_available_studio_project_anatomy_overrides_versions(
|
||||
sorted=sorted
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@require_handler
|
||||
def get_available_studio_project_settings_overrides_versions(sorted=None):
|
||||
return (
|
||||
_SETTINGS_HANDLER
|
||||
.get_available_studio_project_settings_overrides_versions(
|
||||
sorted=sorted
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@require_handler
|
||||
def get_available_project_settings_overrides_versions(
|
||||
project_name, sorted=None
|
||||
):
|
||||
return (
|
||||
_SETTINGS_HANDLER
|
||||
.get_available_project_settings_overrides_versions(
|
||||
project_name, sorted=sorted
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@require_handler
|
||||
def find_closest_version_for_projects(project_names):
|
||||
return (
|
||||
_SETTINGS_HANDLER
|
||||
.find_closest_version_for_projects(project_names)
|
||||
)
|
||||
|
||||
|
||||
@require_handler
|
||||
def clear_studio_system_settings_overrides_for_version(version):
|
||||
return (
|
||||
_SETTINGS_HANDLER
|
||||
.clear_studio_system_settings_overrides_for_version(version)
|
||||
)
|
||||
|
||||
|
||||
@require_handler
|
||||
def clear_studio_project_settings_overrides_for_version(version):
|
||||
return (
|
||||
_SETTINGS_HANDLER
|
||||
.clear_studio_project_settings_overrides_for_version(version)
|
||||
)
|
||||
|
||||
|
||||
@require_handler
|
||||
def clear_studio_project_anatomy_overrides_for_version(version):
|
||||
return (
|
||||
_SETTINGS_HANDLER
|
||||
.clear_studio_project_anatomy_overrides_for_version(version)
|
||||
)
|
||||
|
||||
|
||||
@require_handler
|
||||
def clear_project_settings_overrides_for_version(
|
||||
version, project_name
|
||||
):
|
||||
return _SETTINGS_HANDLER.clear_project_settings_overrides_for_version(
|
||||
version, project_name
|
||||
)
|
||||
|
||||
|
||||
@require_local_handler
|
||||
def save_local_settings(data):
|
||||
return _LOCAL_SETTINGS_HANDLER.save_local_settings(data)
|
||||
|
|
|
|||
|
|
@ -118,7 +118,10 @@
|
|||
"image-btn-hover": "#189aea",
|
||||
"image-btn-disabled": "#bfccd6",
|
||||
"version-exists": "#458056",
|
||||
"version-not-found": "#ffc671"
|
||||
"version-not-found": "#ffc671",
|
||||
|
||||
"source-version": "#D3D8DE",
|
||||
"source-version-outdated": "#ffc671"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1117,6 +1117,20 @@ QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {
|
|||
#ExpandLabel[state="invalid"]:hover, #SettingsLabel[state="invalid"]:hover {
|
||||
color: {color:settings:invalid-dark};
|
||||
}
|
||||
#SettingsOutdatedSourceVersion {
|
||||
color: {color:settings:source-version-outdated};
|
||||
}
|
||||
#SourceVersionLabel {
|
||||
padding-left: 3px;
|
||||
padding-right: 3px;
|
||||
}
|
||||
|
||||
#SourceVersionLabel[state="same"] {
|
||||
color: {color:settings:source-version};
|
||||
}
|
||||
#SourceVersionLabel[state="different"] {
|
||||
color: {color:settings:source-version-outdated};
|
||||
}
|
||||
|
||||
/* TODO Replace these with explicit widget types if possible */
|
||||
#SettingsMainWidget QWidget[input-state="modified"] {
|
||||
|
|
@ -1132,8 +1146,8 @@ QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {
|
|||
border-color: {color:settings:invalid-dark};
|
||||
}
|
||||
|
||||
#GroupWidget {
|
||||
border-bottom: 1px solid #21252B;
|
||||
#SettingsFooter {
|
||||
border-top: 1px solid #21252B;
|
||||
}
|
||||
|
||||
#ProjectListWidget QLabel {
|
||||
|
|
@ -1141,6 +1155,10 @@ QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {
|
|||
font-weight: bold;
|
||||
}
|
||||
|
||||
#ProjectListContentWidget {
|
||||
background: {color:bg-view};
|
||||
}
|
||||
|
||||
#MultiSelectionComboBox {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import os
|
||||
from Qt import QtCore, QtGui
|
||||
|
||||
from openpype.style import get_objected_colors
|
||||
from avalon.vendor import qtawesome
|
||||
from openpype.tools.utils import paint_image_with_color
|
||||
|
||||
|
||||
class ResourceCache:
|
||||
|
|
@ -91,17 +91,6 @@ class ResourceCache:
|
|||
icon.addPixmap(disabled_pix, QtGui.QIcon.Disabled, QtGui.QIcon.Off)
|
||||
return icon
|
||||
|
||||
@classmethod
|
||||
def get_warning_pixmap(cls):
|
||||
src_image = get_warning_image()
|
||||
colors = get_objected_colors()
|
||||
color_value = colors["delete-btn-bg"]
|
||||
|
||||
return paint_image_with_color(
|
||||
src_image,
|
||||
color_value.get_qcolor()
|
||||
)
|
||||
|
||||
|
||||
def get_remove_image():
|
||||
image_path = os.path.join(
|
||||
|
|
@ -110,36 +99,3 @@ def get_remove_image():
|
|||
"bin.png"
|
||||
)
|
||||
return QtGui.QImage(image_path)
|
||||
|
||||
|
||||
def get_warning_image():
|
||||
image_path = os.path.join(
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"images",
|
||||
"warning.png"
|
||||
)
|
||||
return QtGui.QImage(image_path)
|
||||
|
||||
|
||||
def paint_image_with_color(image, color):
|
||||
"""TODO: This function should be imported from utils.
|
||||
|
||||
At the moment of creation is not available yet.
|
||||
"""
|
||||
width = image.width()
|
||||
height = image.height()
|
||||
|
||||
alpha_mask = image.createAlphaMask()
|
||||
alpha_region = QtGui.QRegion(QtGui.QBitmap.fromImage(alpha_mask))
|
||||
|
||||
pixmap = QtGui.QPixmap(width, height)
|
||||
pixmap.fill(QtCore.Qt.transparent)
|
||||
|
||||
painter = QtGui.QPainter(pixmap)
|
||||
painter.setClipRegion(alpha_region)
|
||||
painter.setPen(QtCore.Qt.NoPen)
|
||||
painter.setBrush(color)
|
||||
painter.drawRect(QtCore.QRect(0, 0, width, height))
|
||||
painter.end()
|
||||
|
||||
return pixmap
|
||||
|
|
|
|||
|
|
@ -4,14 +4,16 @@ from .constants import (
|
|||
NAME_ALLOWED_SYMBOLS,
|
||||
NAME_REGEX
|
||||
)
|
||||
from .style import ResourceCache
|
||||
from openpype.lib import (
|
||||
create_project,
|
||||
PROJECT_NAME_ALLOWED_SYMBOLS,
|
||||
PROJECT_NAME_REGEX
|
||||
)
|
||||
from openpype.style import load_stylesheet
|
||||
from openpype.tools.utils import PlaceholderLineEdit
|
||||
from openpype.tools.utils import (
|
||||
PlaceholderLineEdit,
|
||||
get_warning_pixmap
|
||||
)
|
||||
from avalon.api import AvalonMongoDB
|
||||
|
||||
from Qt import QtWidgets, QtCore, QtGui
|
||||
|
|
@ -338,7 +340,7 @@ class ConfirmProjectDeletion(QtWidgets.QDialog):
|
|||
|
||||
top_widget = QtWidgets.QWidget(self)
|
||||
|
||||
warning_pixmap = ResourceCache.get_warning_pixmap()
|
||||
warning_pixmap = get_warning_pixmap()
|
||||
warning_icon_label = PixmapLabel(warning_pixmap, top_widget)
|
||||
|
||||
message_label = QtWidgets.QLabel(top_widget)
|
||||
|
|
|
|||
|
|
@ -3,8 +3,10 @@ import sys
|
|||
import traceback
|
||||
import contextlib
|
||||
from enum import Enum
|
||||
from Qt import QtWidgets, QtCore, QtGui
|
||||
from Qt import QtWidgets, QtCore
|
||||
|
||||
from openpype.lib import get_openpype_version
|
||||
from openpype.tools.utils import set_style_property
|
||||
from openpype.settings.entities import (
|
||||
SystemSettings,
|
||||
ProjectSettings,
|
||||
|
|
@ -34,7 +36,10 @@ from openpype.settings.entities.op_version_entity import (
|
|||
)
|
||||
|
||||
from openpype.settings import SaveWarningExc
|
||||
from .widgets import ProjectListWidget
|
||||
from .widgets import (
|
||||
ProjectListWidget,
|
||||
VersionAction
|
||||
)
|
||||
from .breadcrumbs_widget import (
|
||||
BreadcrumbsAddressBar,
|
||||
SystemSettingsBreadcrumbs,
|
||||
|
|
@ -88,6 +93,20 @@ class SettingsCategoryWidget(QtWidgets.QWidget):
|
|||
restart_required_trigger = QtCore.Signal()
|
||||
full_path_requested = QtCore.Signal(str, str)
|
||||
|
||||
require_restart_label_text = (
|
||||
"Your changes require restart of"
|
||||
" all running OpenPype processes to take affect."
|
||||
)
|
||||
outdated_version_label_text = (
|
||||
"Your settings are loaded from an older version."
|
||||
)
|
||||
source_version_tooltip = "Using settings of current OpenPype version"
|
||||
source_version_tooltip_outdated = (
|
||||
"Please check that all settings are still correct (blue colour\n"
|
||||
"indicates potential changes in the new version) and save your\n"
|
||||
"settings to update them to you current running OpenPype version."
|
||||
)
|
||||
|
||||
def __init__(self, user_role, parent=None):
|
||||
super(SettingsCategoryWidget, self).__init__(parent)
|
||||
|
||||
|
|
@ -98,6 +117,10 @@ class SettingsCategoryWidget(QtWidgets.QWidget):
|
|||
self._state = CategoryState.Idle
|
||||
|
||||
self._hide_studio_overrides = False
|
||||
self._updating_root = False
|
||||
self._use_version = None
|
||||
self._current_version = get_openpype_version()
|
||||
|
||||
self.ignore_input_changes = IgnoreInputChangesObj(self)
|
||||
|
||||
self.keys = []
|
||||
|
|
@ -183,77 +206,126 @@ class SettingsCategoryWidget(QtWidgets.QWidget):
|
|||
def initialize_attributes(self):
|
||||
return
|
||||
|
||||
@property
|
||||
def is_modifying_defaults(self):
|
||||
if self.modify_defaults_checkbox is None:
|
||||
return False
|
||||
return self.modify_defaults_checkbox.isChecked()
|
||||
|
||||
def create_ui(self):
|
||||
self.modify_defaults_checkbox = None
|
||||
|
||||
scroll_widget = QtWidgets.QScrollArea(self)
|
||||
scroll_widget.setObjectName("GroupWidget")
|
||||
content_widget = QtWidgets.QWidget(scroll_widget)
|
||||
conf_wrapper_widget = QtWidgets.QWidget(self)
|
||||
configurations_widget = QtWidgets.QWidget(conf_wrapper_widget)
|
||||
|
||||
breadcrumbs_label = QtWidgets.QLabel("Path:", content_widget)
|
||||
breadcrumbs_widget = BreadcrumbsAddressBar(content_widget)
|
||||
# Breadcrumbs/Path widget
|
||||
breadcrumbs_widget = QtWidgets.QWidget(self)
|
||||
breadcrumbs_label = QtWidgets.QLabel("Path:", breadcrumbs_widget)
|
||||
breadcrumbs_bar = BreadcrumbsAddressBar(breadcrumbs_widget)
|
||||
|
||||
breadcrumbs_layout = QtWidgets.QHBoxLayout()
|
||||
refresh_icon = qtawesome.icon("fa.refresh", color="white")
|
||||
refresh_btn = QtWidgets.QPushButton(breadcrumbs_widget)
|
||||
refresh_btn.setIcon(refresh_icon)
|
||||
|
||||
breadcrumbs_layout = QtWidgets.QHBoxLayout(breadcrumbs_widget)
|
||||
breadcrumbs_layout.setContentsMargins(5, 5, 5, 5)
|
||||
breadcrumbs_layout.setSpacing(5)
|
||||
breadcrumbs_layout.addWidget(breadcrumbs_label)
|
||||
breadcrumbs_layout.addWidget(breadcrumbs_widget)
|
||||
breadcrumbs_layout.addWidget(breadcrumbs_label, 0)
|
||||
breadcrumbs_layout.addWidget(breadcrumbs_bar, 1)
|
||||
breadcrumbs_layout.addWidget(refresh_btn, 0)
|
||||
|
||||
# Widgets representing settings entities
|
||||
scroll_widget = QtWidgets.QScrollArea(configurations_widget)
|
||||
content_widget = QtWidgets.QWidget(scroll_widget)
|
||||
scroll_widget.setWidgetResizable(True)
|
||||
scroll_widget.setWidget(content_widget)
|
||||
|
||||
content_layout = QtWidgets.QVBoxLayout(content_widget)
|
||||
content_layout.setContentsMargins(3, 3, 3, 3)
|
||||
content_layout.setSpacing(5)
|
||||
content_layout.setAlignment(QtCore.Qt.AlignTop)
|
||||
|
||||
scroll_widget.setWidgetResizable(True)
|
||||
scroll_widget.setWidget(content_widget)
|
||||
# Footer widget
|
||||
footer_widget = QtWidgets.QWidget(self)
|
||||
footer_widget.setObjectName("SettingsFooter")
|
||||
|
||||
refresh_icon = qtawesome.icon("fa.refresh", color="white")
|
||||
refresh_btn = QtWidgets.QPushButton(self)
|
||||
refresh_btn.setIcon(refresh_icon)
|
||||
# Info labels
|
||||
# TODO dynamic labels
|
||||
labels_alignment = QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter
|
||||
empty_label = QtWidgets.QLabel(footer_widget)
|
||||
|
||||
footer_layout = QtWidgets.QHBoxLayout()
|
||||
outdated_version_label = QtWidgets.QLabel(
|
||||
self.outdated_version_label_text, footer_widget
|
||||
)
|
||||
outdated_version_label.setToolTip(self.source_version_tooltip_outdated)
|
||||
outdated_version_label.setAlignment(labels_alignment)
|
||||
outdated_version_label.setVisible(False)
|
||||
outdated_version_label.setObjectName("SettingsOutdatedSourceVersion")
|
||||
|
||||
require_restart_label = QtWidgets.QLabel(
|
||||
self.require_restart_label_text, footer_widget
|
||||
)
|
||||
require_restart_label.setAlignment(labels_alignment)
|
||||
require_restart_label.setVisible(False)
|
||||
|
||||
# Label showing source version of loaded settings
|
||||
source_version_label = QtWidgets.QLabel("", footer_widget)
|
||||
source_version_label.setObjectName("SourceVersionLabel")
|
||||
set_style_property(source_version_label, "state", "")
|
||||
source_version_label.setToolTip(self.source_version_tooltip)
|
||||
|
||||
save_btn = QtWidgets.QPushButton("Save", footer_widget)
|
||||
|
||||
footer_layout = QtWidgets.QHBoxLayout(footer_widget)
|
||||
footer_layout.setContentsMargins(5, 5, 5, 5)
|
||||
if self.user_role == "developer":
|
||||
self._add_developer_ui(footer_layout)
|
||||
self._add_developer_ui(footer_layout, footer_widget)
|
||||
|
||||
save_btn = QtWidgets.QPushButton("Save", self)
|
||||
require_restart_label = QtWidgets.QLabel(self)
|
||||
require_restart_label.setAlignment(QtCore.Qt.AlignCenter)
|
||||
|
||||
footer_layout.addWidget(refresh_btn, 0)
|
||||
footer_layout.addWidget(empty_label, 1)
|
||||
footer_layout.addWidget(outdated_version_label, 1)
|
||||
footer_layout.addWidget(require_restart_label, 1)
|
||||
footer_layout.addWidget(source_version_label, 0)
|
||||
footer_layout.addWidget(save_btn, 0)
|
||||
|
||||
configurations_layout = QtWidgets.QVBoxLayout()
|
||||
configurations_layout = QtWidgets.QVBoxLayout(configurations_widget)
|
||||
configurations_layout.setContentsMargins(0, 0, 0, 0)
|
||||
configurations_layout.setSpacing(0)
|
||||
|
||||
configurations_layout.addWidget(scroll_widget, 1)
|
||||
configurations_layout.addLayout(footer_layout, 0)
|
||||
|
||||
conf_wrapper_layout = QtWidgets.QHBoxLayout()
|
||||
conf_wrapper_layout = QtWidgets.QHBoxLayout(conf_wrapper_widget)
|
||||
conf_wrapper_layout.setContentsMargins(0, 0, 0, 0)
|
||||
conf_wrapper_layout.setSpacing(0)
|
||||
conf_wrapper_layout.addLayout(configurations_layout, 1)
|
||||
conf_wrapper_layout.addWidget(configurations_widget, 1)
|
||||
|
||||
main_layout = QtWidgets.QVBoxLayout(self)
|
||||
main_layout.setContentsMargins(0, 0, 0, 0)
|
||||
main_layout.setSpacing(0)
|
||||
main_layout.addLayout(breadcrumbs_layout, 0)
|
||||
main_layout.addLayout(conf_wrapper_layout, 1)
|
||||
main_layout.addWidget(breadcrumbs_widget, 0)
|
||||
main_layout.addWidget(conf_wrapper_widget, 1)
|
||||
main_layout.addWidget(footer_widget, 0)
|
||||
|
||||
save_btn.clicked.connect(self._save)
|
||||
refresh_btn.clicked.connect(self._on_refresh)
|
||||
breadcrumbs_widget.path_edited.connect(self._on_path_edit)
|
||||
breadcrumbs_bar.path_edited.connect(self._on_path_edit)
|
||||
|
||||
self._require_restart_label = require_restart_label
|
||||
self._outdated_version_label = outdated_version_label
|
||||
self._empty_label = empty_label
|
||||
|
||||
self._is_loaded_version_outdated = False
|
||||
|
||||
self.save_btn = save_btn
|
||||
self.refresh_btn = refresh_btn
|
||||
self.require_restart_label = require_restart_label
|
||||
self._source_version_label = source_version_label
|
||||
|
||||
self.scroll_widget = scroll_widget
|
||||
self.content_layout = content_layout
|
||||
self.content_widget = content_widget
|
||||
self.breadcrumbs_widget = breadcrumbs_widget
|
||||
self.breadcrumbs_bar = breadcrumbs_bar
|
||||
|
||||
self.breadcrumbs_model = None
|
||||
self.refresh_btn = refresh_btn
|
||||
|
||||
self.conf_wrapper_layout = conf_wrapper_layout
|
||||
self.main_layout = main_layout
|
||||
|
||||
|
|
@ -308,21 +380,17 @@ class SettingsCategoryWidget(QtWidgets.QWidget):
|
|||
pass
|
||||
|
||||
def set_path(self, path):
|
||||
self.breadcrumbs_widget.set_path(path)
|
||||
self.breadcrumbs_bar.set_path(path)
|
||||
|
||||
def _add_developer_ui(self, footer_layout):
|
||||
modify_defaults_widget = QtWidgets.QWidget()
|
||||
modify_defaults_checkbox = QtWidgets.QCheckBox(modify_defaults_widget)
|
||||
def _add_developer_ui(self, footer_layout, footer_widget):
|
||||
modify_defaults_checkbox = QtWidgets.QCheckBox(footer_widget)
|
||||
modify_defaults_checkbox.setChecked(self._hide_studio_overrides)
|
||||
label_widget = QtWidgets.QLabel(
|
||||
"Modify defaults", modify_defaults_widget
|
||||
"Modify defaults", footer_widget
|
||||
)
|
||||
|
||||
modify_defaults_layout = QtWidgets.QHBoxLayout(modify_defaults_widget)
|
||||
modify_defaults_layout.addWidget(label_widget)
|
||||
modify_defaults_layout.addWidget(modify_defaults_checkbox)
|
||||
|
||||
footer_layout.addWidget(modify_defaults_widget, 0)
|
||||
footer_layout.addWidget(label_widget, 0)
|
||||
footer_layout.addWidget(modify_defaults_checkbox, 0)
|
||||
|
||||
modify_defaults_checkbox.stateChanged.connect(
|
||||
self._on_modify_defaults
|
||||
|
|
@ -361,6 +429,7 @@ class SettingsCategoryWidget(QtWidgets.QWidget):
|
|||
|
||||
try:
|
||||
self.entity.save()
|
||||
self._use_version = None
|
||||
|
||||
# NOTE There are relations to previous entities and C++ callbacks
|
||||
# so it is easier to just use new entity and recreate UI but
|
||||
|
|
@ -420,13 +489,7 @@ class SettingsCategoryWidget(QtWidgets.QWidget):
|
|||
return
|
||||
|
||||
def _on_require_restart_change(self):
|
||||
value = ""
|
||||
if self.entity.require_restart:
|
||||
value = (
|
||||
"Your changes require restart of"
|
||||
" all running OpenPype processes to take affect."
|
||||
)
|
||||
self.require_restart_label.setText(value)
|
||||
self._update_labels_visibility()
|
||||
|
||||
def reset(self):
|
||||
self.set_state(CategoryState.Working)
|
||||
|
|
@ -444,6 +507,8 @@ class SettingsCategoryWidget(QtWidgets.QWidget):
|
|||
widget.deleteLater()
|
||||
|
||||
dialog = None
|
||||
self._updating_root = True
|
||||
source_version = ""
|
||||
try:
|
||||
self._create_root_entity()
|
||||
|
||||
|
|
@ -459,6 +524,7 @@ class SettingsCategoryWidget(QtWidgets.QWidget):
|
|||
input_field.set_entity_value()
|
||||
|
||||
self.ignore_input_changes.set_ignore(False)
|
||||
source_version = self.entity.source_version
|
||||
|
||||
except DefaultsNotDefined:
|
||||
dialog = QtWidgets.QMessageBox(self)
|
||||
|
|
@ -502,6 +568,27 @@ class SettingsCategoryWidget(QtWidgets.QWidget):
|
|||
spacer, layout.rowCount(), 0, 1, layout.columnCount()
|
||||
)
|
||||
|
||||
self._updating_root = False
|
||||
|
||||
# Update source version label
|
||||
state_value = ""
|
||||
tooltip = ""
|
||||
outdated = False
|
||||
if source_version:
|
||||
if source_version != self._current_version:
|
||||
state_value = "different"
|
||||
tooltip = self.source_version_tooltip_outdated
|
||||
outdated = True
|
||||
else:
|
||||
state_value = "same"
|
||||
tooltip = self.source_version_tooltip
|
||||
|
||||
self._is_loaded_version_outdated = outdated
|
||||
self._source_version_label.setText(source_version)
|
||||
self._source_version_label.setToolTip(tooltip)
|
||||
set_style_property(self._source_version_label, "state", state_value)
|
||||
self._update_labels_visibility()
|
||||
|
||||
self.set_state(CategoryState.Idle)
|
||||
|
||||
if dialog:
|
||||
|
|
@ -510,6 +597,36 @@ class SettingsCategoryWidget(QtWidgets.QWidget):
|
|||
else:
|
||||
self._on_reset_success()
|
||||
|
||||
def _on_source_version_change(self, version):
|
||||
if self._updating_root:
|
||||
return
|
||||
|
||||
if version == self._current_version:
|
||||
version = None
|
||||
|
||||
self._use_version = version
|
||||
QtCore.QTimer.singleShot(20, self.reset)
|
||||
|
||||
def add_context_actions(self, menu):
|
||||
if not self.entity or self.is_modifying_defaults:
|
||||
return
|
||||
|
||||
versions = self.entity.get_available_studio_versions(sorted=True)
|
||||
if not versions:
|
||||
return
|
||||
|
||||
submenu = QtWidgets.QMenu("Use settings from version", menu)
|
||||
for version in reversed(versions):
|
||||
action = VersionAction(version, submenu)
|
||||
action.version_triggered.connect(
|
||||
self._on_context_version_trigger
|
||||
)
|
||||
submenu.addAction(action)
|
||||
menu.addMenu(submenu)
|
||||
|
||||
def _on_context_version_trigger(self, version):
|
||||
self._on_source_version_change(version)
|
||||
|
||||
def _on_reset_crash(self):
|
||||
self.save_btn.setEnabled(False)
|
||||
|
||||
|
|
@ -521,10 +638,10 @@ class SettingsCategoryWidget(QtWidgets.QWidget):
|
|||
self.save_btn.setEnabled(True)
|
||||
|
||||
if self.breadcrumbs_model is not None:
|
||||
path = self.breadcrumbs_widget.path()
|
||||
self.breadcrumbs_widget.set_path("")
|
||||
path = self.breadcrumbs_bar.path()
|
||||
self.breadcrumbs_bar.set_path("")
|
||||
self.breadcrumbs_model.set_entity(self.entity)
|
||||
self.breadcrumbs_widget.change_path(path)
|
||||
self.breadcrumbs_bar.change_path(path)
|
||||
|
||||
def add_children_gui(self):
|
||||
for child_obj in self.entity.children:
|
||||
|
|
@ -565,10 +682,7 @@ class SettingsCategoryWidget(QtWidgets.QWidget):
|
|||
|
||||
def _save(self):
|
||||
# Don't trigger restart if defaults are modified
|
||||
if (
|
||||
self.modify_defaults_checkbox
|
||||
and self.modify_defaults_checkbox.isChecked()
|
||||
):
|
||||
if self.is_modifying_defaults:
|
||||
require_restart = False
|
||||
else:
|
||||
require_restart = self.entity.require_restart
|
||||
|
|
@ -584,7 +698,29 @@ class SettingsCategoryWidget(QtWidgets.QWidget):
|
|||
|
||||
if require_restart:
|
||||
self.restart_required_trigger.emit()
|
||||
self.require_restart_label.setText("")
|
||||
|
||||
def _update_labels_visibility(self):
|
||||
visible_label = None
|
||||
labels = {
|
||||
self._empty_label,
|
||||
self._outdated_version_label,
|
||||
self._require_restart_label,
|
||||
}
|
||||
if self.entity.require_restart:
|
||||
visible_label = self._require_restart_label
|
||||
elif self._is_loaded_version_outdated:
|
||||
visible_label = self._outdated_version_label
|
||||
else:
|
||||
visible_label = self._empty_label
|
||||
|
||||
if visible_label.isVisible():
|
||||
return
|
||||
|
||||
for label in labels:
|
||||
if label is visible_label:
|
||||
visible_label.setVisible(True)
|
||||
else:
|
||||
label.setVisible(False)
|
||||
|
||||
def _on_refresh(self):
|
||||
self.reset()
|
||||
|
|
@ -594,25 +730,29 @@ class SettingsCategoryWidget(QtWidgets.QWidget):
|
|||
|
||||
|
||||
class SystemWidget(SettingsCategoryWidget):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self._actions = []
|
||||
super(SystemWidget, self).__init__(*args, **kwargs)
|
||||
|
||||
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)
|
||||
self.breadcrumbs_bar.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)
|
||||
entity = SystemSettings(
|
||||
set_studio_state=False, source_version=self._use_version
|
||||
)
|
||||
entity.on_change_callbacks.append(self._on_entity_change)
|
||||
self.entity = entity
|
||||
try:
|
||||
if (
|
||||
self.modify_defaults_checkbox
|
||||
and self.modify_defaults_checkbox.isChecked()
|
||||
):
|
||||
self.entity.set_defaults_state()
|
||||
if self.is_modifying_defaults:
|
||||
entity.set_defaults_state()
|
||||
else:
|
||||
self.entity.set_studio_state()
|
||||
entity.set_studio_state()
|
||||
|
||||
if self.modify_defaults_checkbox:
|
||||
self.modify_defaults_checkbox.setEnabled(True)
|
||||
|
|
@ -620,16 +760,16 @@ class SystemWidget(SettingsCategoryWidget):
|
|||
if not self.modify_defaults_checkbox:
|
||||
raise
|
||||
|
||||
self.entity.set_defaults_state()
|
||||
entity.set_defaults_state()
|
||||
self.modify_defaults_checkbox.setChecked(True)
|
||||
self.modify_defaults_checkbox.setEnabled(False)
|
||||
|
||||
def ui_tweaks(self):
|
||||
self.breadcrumbs_model = SystemSettingsBreadcrumbs()
|
||||
self.breadcrumbs_widget.set_model(self.breadcrumbs_model)
|
||||
self.breadcrumbs_bar.set_model(self.breadcrumbs_model)
|
||||
|
||||
def _on_modify_defaults(self):
|
||||
if self.modify_defaults_checkbox.isChecked():
|
||||
if self.is_modifying_defaults:
|
||||
if not self.entity.is_in_defaults_state():
|
||||
self.reset()
|
||||
else:
|
||||
|
|
@ -638,6 +778,9 @@ class SystemWidget(SettingsCategoryWidget):
|
|||
|
||||
|
||||
class ProjectWidget(SettingsCategoryWidget):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(ProjectWidget, self).__init__(*args, **kwargs)
|
||||
|
||||
def contain_category_key(self, category):
|
||||
if category in ("project_settings", "project_anatomy"):
|
||||
return True
|
||||
|
|
@ -651,28 +794,28 @@ class ProjectWidget(SettingsCategoryWidget):
|
|||
else:
|
||||
path = category
|
||||
|
||||
self.breadcrumbs_widget.change_path(path)
|
||||
self.breadcrumbs_bar.change_path(path)
|
||||
|
||||
def initialize_attributes(self):
|
||||
self.project_name = None
|
||||
|
||||
def ui_tweaks(self):
|
||||
self.breadcrumbs_model = ProjectSettingsBreadcrumbs()
|
||||
self.breadcrumbs_widget.set_model(self.breadcrumbs_model)
|
||||
self.breadcrumbs_bar.set_model(self.breadcrumbs_model)
|
||||
|
||||
project_list_widget = ProjectListWidget(self)
|
||||
|
||||
self.conf_wrapper_layout.insertWidget(0, project_list_widget, 0)
|
||||
|
||||
project_list_widget.project_changed.connect(self._on_project_change)
|
||||
project_list_widget.version_change_requested.connect(
|
||||
self._on_source_version_change
|
||||
)
|
||||
|
||||
self.project_list_widget = project_list_widget
|
||||
|
||||
def get_project_names(self):
|
||||
if (
|
||||
self.modify_defaults_checkbox
|
||||
and self.modify_defaults_checkbox.isChecked()
|
||||
):
|
||||
if self.is_modifying_defaults:
|
||||
return []
|
||||
return self.project_list_widget.get_project_names()
|
||||
|
||||
|
|
@ -684,6 +827,10 @@ class ProjectWidget(SettingsCategoryWidget):
|
|||
if self is saved_tab_widget:
|
||||
return
|
||||
|
||||
def _on_context_version_trigger(self, version):
|
||||
self.project_list_widget.select_project(None)
|
||||
super(ProjectWidget, self)._on_context_version_trigger(version)
|
||||
|
||||
def _on_reset_start(self):
|
||||
self.project_list_widget.refresh()
|
||||
|
||||
|
|
@ -696,32 +843,29 @@ class ProjectWidget(SettingsCategoryWidget):
|
|||
super(ProjectWidget, self)._on_reset_success()
|
||||
|
||||
def _set_enabled_project_list(self, enabled):
|
||||
if (
|
||||
enabled
|
||||
and self.modify_defaults_checkbox
|
||||
and self.modify_defaults_checkbox.isChecked()
|
||||
):
|
||||
if enabled and self.is_modifying_defaults:
|
||||
enabled = False
|
||||
if self.project_list_widget.isEnabled() != enabled:
|
||||
self.project_list_widget.setEnabled(enabled)
|
||||
|
||||
def _create_root_entity(self):
|
||||
self.entity = ProjectSettings(change_state=False)
|
||||
self.entity.on_change_callbacks.append(self._on_entity_change)
|
||||
entity = ProjectSettings(
|
||||
change_state=False, source_version=self._use_version
|
||||
)
|
||||
entity.on_change_callbacks.append(self._on_entity_change)
|
||||
self.project_list_widget.set_entity(entity)
|
||||
self.entity = entity
|
||||
try:
|
||||
if (
|
||||
self.modify_defaults_checkbox
|
||||
and self.modify_defaults_checkbox.isChecked()
|
||||
):
|
||||
if self.is_modifying_defaults:
|
||||
self.entity.set_defaults_state()
|
||||
|
||||
elif self.project_name is None:
|
||||
self.entity.set_studio_state()
|
||||
|
||||
elif self.project_name == self.entity.project_name:
|
||||
self.entity.set_project_state()
|
||||
else:
|
||||
self.entity.change_project(self.project_name)
|
||||
self.entity.change_project(
|
||||
self.project_name, self._use_version
|
||||
)
|
||||
|
||||
if self.modify_defaults_checkbox:
|
||||
self.modify_defaults_checkbox.setEnabled(True)
|
||||
|
|
@ -754,7 +898,7 @@ class ProjectWidget(SettingsCategoryWidget):
|
|||
self.set_state(CategoryState.Idle)
|
||||
|
||||
def _on_modify_defaults(self):
|
||||
if self.modify_defaults_checkbox.isChecked():
|
||||
if self.is_modifying_defaults:
|
||||
self._set_enabled_project_list(False)
|
||||
if not self.entity.is_in_defaults_state():
|
||||
self.reset()
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ DEFAULT_PROJECT_LABEL = "< Default >"
|
|||
PROJECT_NAME_ROLE = QtCore.Qt.UserRole + 1
|
||||
PROJECT_IS_ACTIVE_ROLE = QtCore.Qt.UserRole + 2
|
||||
PROJECT_IS_SELECTED_ROLE = QtCore.Qt.UserRole + 3
|
||||
PROJECT_VERSION_ROLE = QtCore.Qt.UserRole + 4
|
||||
|
||||
|
||||
__all__ = (
|
||||
|
|
@ -12,5 +13,6 @@ __all__ = (
|
|||
|
||||
"PROJECT_NAME_ROLE",
|
||||
"PROJECT_IS_ACTIVE_ROLE",
|
||||
"PROJECT_IS_SELECTED_ROLE"
|
||||
"PROJECT_IS_SELECTED_ROLE",
|
||||
"PROJECT_VERSION_ROLE",
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import os
|
||||
import copy
|
||||
import uuid
|
||||
from Qt import QtWidgets, QtCore, QtGui
|
||||
from avalon.vendor import qtawesome
|
||||
from avalon.mongodb import (
|
||||
|
|
@ -12,8 +13,12 @@ from openpype.tools.utils.widgets import ImageButton
|
|||
from openpype.tools.utils.lib import paint_image_with_color
|
||||
|
||||
from openpype.widgets.nice_checkbox import NiceCheckbox
|
||||
from openpype.tools.utils import PlaceholderLineEdit
|
||||
from openpype.settings.lib import get_system_settings
|
||||
from openpype.tools.utils import (
|
||||
PlaceholderLineEdit,
|
||||
DynamicQThread
|
||||
)
|
||||
from openpype.settings.lib import find_closest_version_for_projects
|
||||
from openpype.lib import get_openpype_version
|
||||
from .images import (
|
||||
get_pixmap,
|
||||
get_image
|
||||
|
|
@ -21,11 +26,40 @@ from .images import (
|
|||
from .constants import (
|
||||
DEFAULT_PROJECT_LABEL,
|
||||
PROJECT_NAME_ROLE,
|
||||
PROJECT_VERSION_ROLE,
|
||||
PROJECT_IS_ACTIVE_ROLE,
|
||||
PROJECT_IS_SELECTED_ROLE
|
||||
)
|
||||
|
||||
|
||||
class SettingsTabWidget(QtWidgets.QTabWidget):
|
||||
context_menu_requested = QtCore.Signal(int)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(SettingsTabWidget, self).__init__(*args, **kwargs)
|
||||
self._right_click_tab_idx = None
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
super(SettingsTabWidget, self).mousePressEvent(event)
|
||||
if event.button() == QtCore.Qt.RightButton:
|
||||
tab_bar = self.tabBar()
|
||||
pos = tab_bar.mapFromGlobal(event.globalPos())
|
||||
tab_idx = tab_bar.tabAt(pos)
|
||||
if tab_idx < 0:
|
||||
tab_idx = None
|
||||
self._right_click_tab_idx = tab_idx
|
||||
|
||||
def mouseReleaseEvent(self, event):
|
||||
super(SettingsTabWidget, self).mouseReleaseEvent(event)
|
||||
if event.button() == QtCore.Qt.RightButton:
|
||||
tab_bar = self.tabBar()
|
||||
pos = tab_bar.mapFromGlobal(event.globalPos())
|
||||
tab_idx = tab_bar.tabAt(pos)
|
||||
if tab_idx == self._right_click_tab_idx:
|
||||
self.context_menu_requested.emit(tab_idx)
|
||||
self._right_click_tab = None
|
||||
|
||||
|
||||
class CompleterFilter(QtCore.QSortFilterProxyModel):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(CompleterFilter, self).__init__(*args, **kwargs)
|
||||
|
|
@ -603,7 +637,7 @@ class UnsavedChangesDialog(QtWidgets.QDialog):
|
|||
message = "You have unsaved changes. What do you want to do with them?"
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
super(UnsavedChangesDialog, self).__init__(parent)
|
||||
message_label = QtWidgets.QLabel(self.message)
|
||||
|
||||
btns_widget = QtWidgets.QWidget(self)
|
||||
|
|
@ -735,19 +769,65 @@ class SettingsNiceCheckbox(NiceCheckbox):
|
|||
|
||||
|
||||
class ProjectModel(QtGui.QStandardItemModel):
|
||||
_update_versions = QtCore.Signal()
|
||||
|
||||
def __init__(self, only_active, *args, **kwargs):
|
||||
super(ProjectModel, self).__init__(*args, **kwargs)
|
||||
|
||||
self.setColumnCount(2)
|
||||
|
||||
self.dbcon = None
|
||||
|
||||
self._only_active = only_active
|
||||
self._default_item = None
|
||||
self._items_by_name = {}
|
||||
self._versions_by_project = {}
|
||||
|
||||
colors = get_objected_colors()
|
||||
font_color = colors["font"].get_qcolor()
|
||||
font_color.setAlpha(67)
|
||||
self._version_font_color = font_color
|
||||
self._current_version = get_openpype_version()
|
||||
|
||||
self._version_refresh_threads = []
|
||||
self._version_refresh_id = None
|
||||
|
||||
self._update_versions.connect(self._on_update_versions_signal)
|
||||
|
||||
def _on_update_versions_signal(self):
|
||||
for project_name, version in self._versions_by_project.items():
|
||||
if project_name is None:
|
||||
item = self._default_item
|
||||
else:
|
||||
item = self._items_by_name.get(project_name)
|
||||
|
||||
if item and version != self._current_version:
|
||||
item.setData(version, PROJECT_VERSION_ROLE)
|
||||
|
||||
def _fetch_settings_versions(self):
|
||||
"""Used versions per project are loaded in thread to not stuck UI."""
|
||||
version_refresh_id = self._version_refresh_id
|
||||
all_project_names = list(self._items_by_name.keys())
|
||||
all_project_names.append(None)
|
||||
closest_by_project_name = find_closest_version_for_projects(
|
||||
all_project_names
|
||||
)
|
||||
if self._version_refresh_id == version_refresh_id:
|
||||
self._versions_by_project = closest_by_project_name
|
||||
self._update_versions.emit()
|
||||
|
||||
def flags(self, index):
|
||||
if index.column() == 1:
|
||||
index = self.index(index.row(), 0, index.parent())
|
||||
return super(ProjectModel, self).flags(index)
|
||||
|
||||
def set_dbcon(self, dbcon):
|
||||
self.dbcon = dbcon
|
||||
|
||||
def refresh(self):
|
||||
# Change id of versions refresh
|
||||
self._version_refresh_id = uuid.uuid4()
|
||||
|
||||
new_items = []
|
||||
if self._default_item is None:
|
||||
item = QtGui.QStandardItem(DEFAULT_PROJECT_LABEL)
|
||||
|
|
@ -757,6 +837,7 @@ class ProjectModel(QtGui.QStandardItemModel):
|
|||
new_items.append(item)
|
||||
self._default_item = item
|
||||
|
||||
self._default_item.setData("", PROJECT_VERSION_ROLE)
|
||||
project_names = set()
|
||||
if self.dbcon is not None:
|
||||
for project_doc in self.dbcon.projects(
|
||||
|
|
@ -776,6 +857,7 @@ class ProjectModel(QtGui.QStandardItemModel):
|
|||
is_active = project_doc.get("data", {}).get("active", True)
|
||||
item.setData(project_name, PROJECT_NAME_ROLE)
|
||||
item.setData(is_active, PROJECT_IS_ACTIVE_ROLE)
|
||||
item.setData("", PROJECT_VERSION_ROLE)
|
||||
item.setData(False, PROJECT_IS_SELECTED_ROLE)
|
||||
|
||||
if not is_active:
|
||||
|
|
@ -792,15 +874,87 @@ class ProjectModel(QtGui.QStandardItemModel):
|
|||
if new_items:
|
||||
root_item.appendRows(new_items)
|
||||
|
||||
# Fetch versions per project in thread
|
||||
thread = DynamicQThread(self._fetch_settings_versions)
|
||||
self._version_refresh_threads.append(thread)
|
||||
thread.start()
|
||||
|
||||
class ProjectListView(QtWidgets.QListView):
|
||||
# Cleanup done threads
|
||||
for thread in tuple(self._version_refresh_threads):
|
||||
if thread.isFinished():
|
||||
self._version_refresh_threads.remove(thread)
|
||||
|
||||
def data(self, index, role=QtCore.Qt.DisplayRole):
|
||||
if index.column() == 1:
|
||||
if role == QtCore.Qt.TextAlignmentRole:
|
||||
return QtCore.Qt.AlignRight
|
||||
if role == QtCore.Qt.ForegroundRole:
|
||||
return self._version_font_color
|
||||
index = self.index(index.row(), 0, index.parent())
|
||||
if role in (QtCore.Qt.DisplayRole, QtCore.Qt.EditRole):
|
||||
role = PROJECT_VERSION_ROLE
|
||||
|
||||
return super(ProjectModel, self).data(index, role)
|
||||
|
||||
def setData(self, index, value, role=QtCore.Qt.EditRole):
|
||||
if index.column() == 1:
|
||||
index = self.index(index.row(), 0, index.parent())
|
||||
if role in (QtCore.Qt.DisplayRole, QtCore.Qt.EditRole):
|
||||
role = PROJECT_VERSION_ROLE
|
||||
return super(ProjectModel, self).setData(index, value, role)
|
||||
|
||||
def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
|
||||
if role == QtCore.Qt.DisplayRole:
|
||||
if section == 0:
|
||||
return "Project name"
|
||||
|
||||
elif section == 1:
|
||||
return "Used version"
|
||||
return ""
|
||||
return super(ProjectModel, self).headerData(
|
||||
section, orientation, role
|
||||
)
|
||||
|
||||
|
||||
class VersionAction(QtWidgets.QAction):
|
||||
version_triggered = QtCore.Signal(str)
|
||||
|
||||
def __init__(self, version, *args, **kwargs):
|
||||
super(VersionAction, self).__init__(version, *args, **kwargs)
|
||||
self._version = version
|
||||
self.triggered.connect(self._on_trigger)
|
||||
|
||||
def _on_trigger(self):
|
||||
self.version_triggered.emit(self._version)
|
||||
|
||||
|
||||
class ProjectView(QtWidgets.QTreeView):
|
||||
left_mouse_released_at = QtCore.Signal(QtCore.QModelIndex)
|
||||
right_mouse_released_at = QtCore.Signal(QtCore.QModelIndex)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(ProjectView, self).__init__(*args, **kwargs)
|
||||
self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
|
||||
self.setIndentation(0)
|
||||
|
||||
# Do not allow editing
|
||||
self.setEditTriggers(
|
||||
QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers
|
||||
)
|
||||
# Do not automatically handle selection
|
||||
self.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection)
|
||||
self.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
|
||||
|
||||
def mouseReleaseEvent(self, event):
|
||||
if event.button() == QtCore.Qt.LeftButton:
|
||||
index = self.indexAt(event.pos())
|
||||
self.left_mouse_released_at.emit(index)
|
||||
super(ProjectListView, self).mouseReleaseEvent(event)
|
||||
|
||||
elif event.button() == QtCore.Qt.RightButton:
|
||||
index = self.indexAt(event.pos())
|
||||
self.right_mouse_released_at.emit(index)
|
||||
|
||||
super(ProjectView, self).mouseReleaseEvent(event)
|
||||
|
||||
|
||||
class ProjectSortFilterProxy(QtCore.QSortFilterProxyModel):
|
||||
|
|
@ -846,18 +1000,21 @@ class ProjectSortFilterProxy(QtCore.QSortFilterProxyModel):
|
|||
|
||||
class ProjectListWidget(QtWidgets.QWidget):
|
||||
project_changed = QtCore.Signal()
|
||||
version_change_requested = QtCore.Signal(str)
|
||||
|
||||
def __init__(self, parent, only_active=False):
|
||||
self._parent = parent
|
||||
|
||||
self._entity = None
|
||||
self.current_project = None
|
||||
|
||||
super(ProjectListWidget, self).__init__(parent)
|
||||
self.setObjectName("ProjectListWidget")
|
||||
|
||||
label_widget = QtWidgets.QLabel("Projects")
|
||||
content_frame = QtWidgets.QFrame(self)
|
||||
content_frame.setObjectName("ProjectListContentWidget")
|
||||
|
||||
project_list = ProjectListView(self)
|
||||
project_list = ProjectView(content_frame)
|
||||
project_model = ProjectModel(only_active)
|
||||
project_proxy = ProjectSortFilterProxy()
|
||||
|
||||
|
|
@ -865,33 +1022,37 @@ class ProjectListWidget(QtWidgets.QWidget):
|
|||
project_proxy.setSourceModel(project_model)
|
||||
project_list.setModel(project_proxy)
|
||||
|
||||
# Do not allow editing
|
||||
project_list.setEditTriggers(
|
||||
QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers
|
||||
)
|
||||
# Do not automatically handle selection
|
||||
project_list.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection)
|
||||
content_layout = QtWidgets.QVBoxLayout(content_frame)
|
||||
content_layout.setContentsMargins(0, 0, 0, 0)
|
||||
content_layout.setSpacing(0)
|
||||
content_layout.addWidget(project_list, 1)
|
||||
|
||||
layout = QtWidgets.QVBoxLayout(self)
|
||||
layout.setSpacing(3)
|
||||
layout.addWidget(label_widget, 0)
|
||||
layout.addWidget(project_list, 1)
|
||||
inactive_chk = None
|
||||
if not only_active:
|
||||
checkbox_wrapper = QtWidgets.QWidget(content_frame)
|
||||
checkbox_wrapper.setAttribute(QtCore.Qt.WA_TranslucentBackground)
|
||||
|
||||
if only_active:
|
||||
inactive_chk = None
|
||||
else:
|
||||
inactive_chk = QtWidgets.QCheckBox(" Show Inactive Projects ")
|
||||
inactive_chk = QtWidgets.QCheckBox(
|
||||
"Show Inactive Projects", checkbox_wrapper
|
||||
)
|
||||
inactive_chk.setChecked(not project_proxy.is_filter_enabled())
|
||||
|
||||
layout.addSpacing(5)
|
||||
layout.addWidget(inactive_chk, 0)
|
||||
layout.addSpacing(5)
|
||||
wrapper_layout = QtWidgets.QHBoxLayout(checkbox_wrapper)
|
||||
wrapper_layout.addWidget(inactive_chk, 1)
|
||||
|
||||
content_layout.addWidget(checkbox_wrapper, 0)
|
||||
|
||||
inactive_chk.stateChanged.connect(self.on_inactive_vis_changed)
|
||||
|
||||
project_list.left_mouse_released_at.connect(self.on_item_clicked)
|
||||
layout = QtWidgets.QVBoxLayout(self)
|
||||
# Margins '3' are matching to configurables widget scroll area on right
|
||||
layout.setContentsMargins(5, 3, 3, 3)
|
||||
layout.addWidget(content_frame, 1)
|
||||
|
||||
self._default_project_item = None
|
||||
project_list.left_mouse_released_at.connect(self.on_item_clicked)
|
||||
project_list.right_mouse_released_at.connect(
|
||||
self._on_item_right_clicked
|
||||
)
|
||||
|
||||
self.project_list = project_list
|
||||
self.project_proxy = project_proxy
|
||||
|
|
@ -900,10 +1061,46 @@ class ProjectListWidget(QtWidgets.QWidget):
|
|||
|
||||
self.dbcon = None
|
||||
|
||||
def on_item_clicked(self, new_index):
|
||||
new_project_name = new_index.data(QtCore.Qt.DisplayRole)
|
||||
if new_project_name is None:
|
||||
def set_entity(self, entity):
|
||||
self._entity = entity
|
||||
|
||||
def _on_item_right_clicked(self, index):
|
||||
if not index.isValid():
|
||||
return
|
||||
project_name = index.data(PROJECT_NAME_ROLE)
|
||||
if project_name is None:
|
||||
project_name = DEFAULT_PROJECT_LABEL
|
||||
|
||||
if self.current_project != project_name:
|
||||
self.on_item_clicked(index)
|
||||
|
||||
if self.current_project != project_name:
|
||||
return
|
||||
|
||||
if not self._entity:
|
||||
return
|
||||
|
||||
versions = self._entity.get_available_source_versions(sorted=True)
|
||||
if not versions:
|
||||
return
|
||||
|
||||
menu = QtWidgets.QMenu(self)
|
||||
submenu = QtWidgets.QMenu("Use settings from version", menu)
|
||||
for version in reversed(versions):
|
||||
action = VersionAction(version, submenu)
|
||||
action.version_triggered.connect(
|
||||
self.version_change_requested
|
||||
)
|
||||
submenu.addAction(action)
|
||||
menu.addMenu(submenu)
|
||||
menu.exec_(QtGui.QCursor.pos())
|
||||
|
||||
def on_item_clicked(self, new_index):
|
||||
if not new_index.isValid():
|
||||
return
|
||||
new_project_name = new_index.data(PROJECT_NAME_ROLE)
|
||||
if new_project_name is None:
|
||||
new_project_name = DEFAULT_PROJECT_LABEL
|
||||
|
||||
if self.current_project == new_project_name:
|
||||
return
|
||||
|
|
@ -963,12 +1160,30 @@ class ProjectListWidget(QtWidgets.QWidget):
|
|||
index = model.indexFromItem(found_items[0])
|
||||
model.setData(index, True, PROJECT_IS_SELECTED_ROLE)
|
||||
|
||||
index = proxy.mapFromSource(index)
|
||||
src_indexes = []
|
||||
col_count = model.columnCount()
|
||||
if col_count > 1:
|
||||
for col in range(col_count):
|
||||
src_indexes.append(
|
||||
model.index(index.row(), col, index.parent())
|
||||
)
|
||||
dst_indexes = []
|
||||
for index in src_indexes:
|
||||
dst_indexes.append(proxy.mapFromSource(index))
|
||||
|
||||
self.project_list.selectionModel().clear()
|
||||
self.project_list.selectionModel().setCurrentIndex(
|
||||
index, QtCore.QItemSelectionModel.SelectionFlag.SelectCurrent
|
||||
)
|
||||
selection_model = self.project_list.selectionModel()
|
||||
selection_model.clear()
|
||||
|
||||
first = True
|
||||
for index in dst_indexes:
|
||||
if first:
|
||||
selection_model.setCurrentIndex(
|
||||
index,
|
||||
QtCore.QItemSelectionModel.SelectionFlag.SelectCurrent
|
||||
)
|
||||
first = False
|
||||
continue
|
||||
selection_model.select(index, QtCore.QItemSelectionModel.Select)
|
||||
|
||||
def get_project_names(self):
|
||||
output = []
|
||||
|
|
@ -980,7 +1195,7 @@ class ProjectListWidget(QtWidgets.QWidget):
|
|||
def refresh(self):
|
||||
selected_project = None
|
||||
for index in self.project_list.selectedIndexes():
|
||||
selected_project = index.data(QtCore.Qt.DisplayRole)
|
||||
selected_project = index.data(PROJECT_NAME_ROLE)
|
||||
break
|
||||
|
||||
mongo_url = os.environ["OPENPYPE_MONGO"]
|
||||
|
|
@ -1008,5 +1223,6 @@ class ProjectListWidget(QtWidgets.QWidget):
|
|||
self.select_project(selected_project)
|
||||
|
||||
self.current_project = self.project_list.currentIndex().data(
|
||||
QtCore.Qt.DisplayRole
|
||||
PROJECT_NAME_ROLE
|
||||
)
|
||||
self.project_list.resizeColumnToContents(0)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,11 @@ from .categories import (
|
|||
SystemWidget,
|
||||
ProjectWidget
|
||||
)
|
||||
from .widgets import ShadowWidget, RestartDialog
|
||||
from .widgets import (
|
||||
ShadowWidget,
|
||||
RestartDialog,
|
||||
SettingsTabWidget
|
||||
)
|
||||
from openpype import style
|
||||
|
||||
from openpype.lib import is_admin_password_required
|
||||
|
|
@ -34,7 +38,7 @@ class MainWidget(QtWidgets.QWidget):
|
|||
self.setStyleSheet(stylesheet)
|
||||
self.setWindowIcon(QtGui.QIcon(style.app_icon_path()))
|
||||
|
||||
header_tab_widget = QtWidgets.QTabWidget(parent=self)
|
||||
header_tab_widget = SettingsTabWidget(parent=self)
|
||||
|
||||
studio_widget = SystemWidget(user_role, header_tab_widget)
|
||||
project_widget = ProjectWidget(user_role, header_tab_widget)
|
||||
|
|
@ -65,6 +69,10 @@ class MainWidget(QtWidgets.QWidget):
|
|||
)
|
||||
tab_widget.full_path_requested.connect(self._on_full_path_request)
|
||||
|
||||
header_tab_widget.context_menu_requested.connect(
|
||||
self._on_context_menu_request
|
||||
)
|
||||
|
||||
self._header_tab_widget = header_tab_widget
|
||||
self.tab_widgets = tab_widgets
|
||||
|
||||
|
|
@ -100,6 +108,18 @@ class MainWidget(QtWidgets.QWidget):
|
|||
tab_widget.set_category_path(category, path)
|
||||
break
|
||||
|
||||
def _on_context_menu_request(self, tab_idx):
|
||||
widget = self._header_tab_widget.widget(tab_idx)
|
||||
if not widget:
|
||||
return
|
||||
|
||||
menu = QtWidgets.QMenu(self)
|
||||
widget.add_context_actions(menu)
|
||||
if menu.actions():
|
||||
result = menu.exec_(QtGui.QCursor.pos())
|
||||
if result is not None:
|
||||
self._header_tab_widget.setCurrentIndex(tab_idx)
|
||||
|
||||
def showEvent(self, event):
|
||||
super(MainWidget, self).showEvent(event)
|
||||
if self._reset_on_show:
|
||||
|
|
|
|||
|
|
@ -33,7 +33,8 @@ from openpype.settings import (
|
|||
)
|
||||
from openpype.tools.utils import (
|
||||
WrappedCallbackItem,
|
||||
paint_image_with_color
|
||||
paint_image_with_color,
|
||||
get_warning_pixmap
|
||||
)
|
||||
|
||||
from .pype_info_widget import PypeInfoWidget
|
||||
|
|
@ -76,7 +77,7 @@ class PixmapLabel(QtWidgets.QLabel):
|
|||
super(PixmapLabel, self).resizeEvent(event)
|
||||
|
||||
|
||||
class VersionDialog(QtWidgets.QDialog):
|
||||
class VersionUpdateDialog(QtWidgets.QDialog):
|
||||
restart_requested = QtCore.Signal()
|
||||
ignore_requested = QtCore.Signal()
|
||||
|
||||
|
|
@ -84,7 +85,7 @@ class VersionDialog(QtWidgets.QDialog):
|
|||
_min_height = 130
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super(VersionDialog, self).__init__(parent)
|
||||
super(VersionUpdateDialog, self).__init__(parent)
|
||||
|
||||
icon = QtGui.QIcon(resources.get_openpype_icon_filepath())
|
||||
self.setWindowIcon(icon)
|
||||
|
|
@ -152,11 +153,11 @@ class VersionDialog(QtWidgets.QDialog):
|
|||
)
|
||||
|
||||
def showEvent(self, event):
|
||||
super().showEvent(event)
|
||||
super(VersionUpdateDialog, self).showEvent(event)
|
||||
self._restart_accepted = False
|
||||
|
||||
def closeEvent(self, event):
|
||||
super().closeEvent(event)
|
||||
super(VersionUpdateDialog, self).closeEvent(event)
|
||||
if self._restart_accepted or self._current_is_higher:
|
||||
return
|
||||
# Trigger ignore requested only if restart was not clicked and current
|
||||
|
|
@ -202,6 +203,63 @@ class VersionDialog(QtWidgets.QDialog):
|
|||
self.accept()
|
||||
|
||||
|
||||
class BuildVersionDialog(QtWidgets.QDialog):
|
||||
"""Build/Installation version is too low for current OpenPype version.
|
||||
|
||||
This dialog tells to user that it's build OpenPype is too old.
|
||||
"""
|
||||
def __init__(self, parent=None):
|
||||
super(BuildVersionDialog, self).__init__(parent)
|
||||
|
||||
icon = QtGui.QIcon(resources.get_openpype_icon_filepath())
|
||||
self.setWindowIcon(icon)
|
||||
self.setWindowTitle("Outdated OpenPype installation")
|
||||
self.setWindowFlags(
|
||||
self.windowFlags()
|
||||
| QtCore.Qt.WindowStaysOnTopHint
|
||||
)
|
||||
|
||||
top_widget = QtWidgets.QWidget(self)
|
||||
|
||||
warning_pixmap = get_warning_pixmap()
|
||||
warning_icon_label = PixmapLabel(warning_pixmap, top_widget)
|
||||
|
||||
message = (
|
||||
"Your installation of OpenPype <b>does not match minimum"
|
||||
" requirements</b>.<br/><br/>Please update OpenPype installation"
|
||||
" to newer version."
|
||||
)
|
||||
content_label = QtWidgets.QLabel(message, self)
|
||||
|
||||
top_layout = QtWidgets.QHBoxLayout(top_widget)
|
||||
top_layout.setContentsMargins(0, 0, 0, 0)
|
||||
top_layout.addWidget(
|
||||
warning_icon_label, 0,
|
||||
QtCore.Qt.AlignTop | QtCore.Qt.AlignHCenter
|
||||
)
|
||||
top_layout.addWidget(content_label, 1)
|
||||
|
||||
footer_widget = QtWidgets.QWidget(self)
|
||||
ok_btn = QtWidgets.QPushButton("I understand", footer_widget)
|
||||
|
||||
footer_layout = QtWidgets.QHBoxLayout(footer_widget)
|
||||
footer_layout.setContentsMargins(0, 0, 0, 0)
|
||||
footer_layout.addStretch(1)
|
||||
footer_layout.addWidget(ok_btn)
|
||||
|
||||
main_layout = QtWidgets.QVBoxLayout(self)
|
||||
main_layout.addWidget(top_widget, 0)
|
||||
main_layout.addStretch(1)
|
||||
main_layout.addWidget(footer_widget, 0)
|
||||
|
||||
self.setStyleSheet(style.load_stylesheet())
|
||||
|
||||
ok_btn.clicked.connect(self._on_ok_clicked)
|
||||
|
||||
def _on_ok_clicked(self):
|
||||
self.close()
|
||||
|
||||
|
||||
class TrayManager:
|
||||
"""Cares about context of application.
|
||||
|
||||
|
|
@ -272,7 +330,7 @@ class TrayManager:
|
|||
return
|
||||
|
||||
if self._version_dialog is None:
|
||||
self._version_dialog = VersionDialog()
|
||||
self._version_dialog = VersionUpdateDialog()
|
||||
self._version_dialog.restart_requested.connect(
|
||||
self._restart_and_install
|
||||
)
|
||||
|
|
@ -383,6 +441,10 @@ class TrayManager:
|
|||
|
||||
self._validate_settings_defaults()
|
||||
|
||||
if not op_version_control_available():
|
||||
dialog = BuildVersionDialog()
|
||||
dialog.exec_()
|
||||
|
||||
def _validate_settings_defaults(self):
|
||||
valid = True
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -10,7 +10,10 @@ from .widgets import (
|
|||
from .error_dialog import ErrorMessageBox
|
||||
from .lib import (
|
||||
WrappedCallbackItem,
|
||||
paint_image_with_color
|
||||
paint_image_with_color,
|
||||
get_warning_pixmap,
|
||||
set_style_property,
|
||||
DynamicQThread,
|
||||
)
|
||||
|
||||
from .models import (
|
||||
|
|
@ -29,6 +32,9 @@ __all__ = (
|
|||
|
||||
"WrappedCallbackItem",
|
||||
"paint_image_with_color",
|
||||
"get_warning_pixmap",
|
||||
"set_style_property",
|
||||
"DynamicQThread",
|
||||
|
||||
"RecursiveSortFilterProxyModel",
|
||||
)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import Qt
|
|||
from Qt import QtWidgets, QtGui, QtCore
|
||||
|
||||
from avalon.lib import HeroVersionType
|
||||
from openpype.style import get_objected_colors
|
||||
from .models import TreeModel
|
||||
from . import lib
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ from openpype.api import (
|
|||
Logger
|
||||
)
|
||||
from openpype.lib import filter_profiles
|
||||
from openpype.style import get_objected_colors
|
||||
from openpype.resources import get_image_path
|
||||
|
||||
|
||||
def center_window(window):
|
||||
|
|
@ -28,6 +30,18 @@ def center_window(window):
|
|||
window.move(geo.topLeft())
|
||||
|
||||
|
||||
def set_style_property(widget, property_name, property_value):
|
||||
"""Set widget's property that may affect style.
|
||||
|
||||
If current property value is different then style of widget is polished.
|
||||
"""
|
||||
cur_value = widget.property(property_name)
|
||||
if cur_value == property_value:
|
||||
return
|
||||
widget.setProperty(property_name, property_value)
|
||||
widget.style().polish(widget)
|
||||
|
||||
|
||||
def paint_image_with_color(image, color):
|
||||
"""Redraw image with single color using it's alpha.
|
||||
|
||||
|
|
@ -670,3 +684,19 @@ class WrappedCallbackItem:
|
|||
|
||||
finally:
|
||||
self._done = True
|
||||
|
||||
|
||||
def get_warning_pixmap(color=None):
|
||||
"""Warning icon as QPixmap.
|
||||
|
||||
Args:
|
||||
color(QtGui.QColor): Color that will be used to paint warning icon.
|
||||
"""
|
||||
src_image_path = get_image_path("warning.png")
|
||||
src_image = QtGui.QImage(src_image_path)
|
||||
if color is None:
|
||||
colors = get_objected_colors()
|
||||
color_value = colors["delete-btn-bg"]
|
||||
color = color_value.get_qcolor()
|
||||
|
||||
return paint_image_with_color(src_image, color)
|
||||
|
|
|
|||
|
|
@ -3,9 +3,6 @@ import logging
|
|||
|
||||
import Qt
|
||||
from Qt import QtCore, QtGui
|
||||
from avalon.vendor import qtawesome
|
||||
from avalon import style, io
|
||||
from . import lib
|
||||
from .constants import (
|
||||
PROJECT_IS_ACTIVE_ROLE,
|
||||
PROJECT_NAME_ROLE,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue