mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-25 05:14:40 +01:00
Merge pull request #1049 from pypeclub/feature/local_settings
Local settings
This commit is contained in:
commit
d8d16ca80e
21 changed files with 1833 additions and 13 deletions
|
|
@ -22,7 +22,9 @@ from .lib import (
|
|||
get_app_environments_for_context,
|
||||
source_hash,
|
||||
get_latest_version,
|
||||
get_global_environments
|
||||
get_global_environments,
|
||||
get_local_site_id,
|
||||
change_pype_mongo_url
|
||||
)
|
||||
|
||||
from .lib.mongo import (
|
||||
|
|
@ -109,5 +111,8 @@ __all__ = [
|
|||
|
||||
"run_subprocess",
|
||||
"get_latest_version",
|
||||
"get_global_environments"
|
||||
"get_global_environments",
|
||||
|
||||
"get_local_site_id",
|
||||
"change_pype_mongo_url"
|
||||
]
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ from .mongo import (
|
|||
decompose_url,
|
||||
compose_url,
|
||||
get_default_components,
|
||||
validate_mongo_connection,
|
||||
PypeMongoConnection
|
||||
)
|
||||
from .anatomy import (
|
||||
|
|
@ -91,10 +92,12 @@ from .plugin_tools import (
|
|||
should_decompress
|
||||
)
|
||||
|
||||
from .user_settings import (
|
||||
from .local_settings import (
|
||||
IniSettingRegistry,
|
||||
JSONSettingRegistry,
|
||||
PypeSettingsRegistry
|
||||
PypeSettingsRegistry,
|
||||
get_local_site_id,
|
||||
change_pype_mongo_url
|
||||
)
|
||||
|
||||
from .path_tools import (
|
||||
|
|
@ -191,11 +194,15 @@ __all__ = [
|
|||
"decompose_url",
|
||||
"compose_url",
|
||||
"get_default_components",
|
||||
"validate_mongo_connection",
|
||||
"PypeMongoConnection",
|
||||
|
||||
"IniSettingRegistry",
|
||||
"JSONSettingRegistry",
|
||||
"PypeSettingsRegistry",
|
||||
"get_local_site_id",
|
||||
"change_pype_mongo_url",
|
||||
|
||||
"timeit",
|
||||
|
||||
"is_overlapping_otio_ranges",
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ import platform
|
|||
import appdirs
|
||||
import six
|
||||
|
||||
from .import validate_mongo_connection
|
||||
|
||||
|
||||
@six.add_metaclass(ABCMeta)
|
||||
class ASettingRegistry():
|
||||
|
|
@ -118,7 +120,7 @@ class ASettingRegistry():
|
|||
"""Delete item from settings.
|
||||
|
||||
Note:
|
||||
see :meth:`pype.lib.user_settings.ARegistrySettings.delete_item`
|
||||
see :meth:`pype.lib.local_settings.ARegistrySettings.delete_item`
|
||||
|
||||
"""
|
||||
pass
|
||||
|
|
@ -464,3 +466,43 @@ class PypeSettingsRegistry(JSONSettingRegistry):
|
|||
self.product = "pype"
|
||||
path = appdirs.user_data_dir(self.product, self.vendor)
|
||||
super(PypeSettingsRegistry, self).__init__("pype_settings", path)
|
||||
|
||||
|
||||
def _create_local_site_id(registry=None):
|
||||
"""Create a local site identifier."""
|
||||
from uuid import uuid4
|
||||
|
||||
if registry is None:
|
||||
registry = PypeSettingsRegistry()
|
||||
|
||||
new_id = str(uuid4())
|
||||
|
||||
print("Created local site id \"{}\"".format(new_id))
|
||||
|
||||
registry.set_item("localId", new_id)
|
||||
|
||||
return new_id
|
||||
|
||||
|
||||
def get_local_site_id():
|
||||
"""Get local site identifier.
|
||||
|
||||
Identifier is created if does not exists yet.
|
||||
"""
|
||||
registry = PypeSettingsRegistry()
|
||||
try:
|
||||
return registry.get_item("localId")
|
||||
except ValueError:
|
||||
return _create_local_site_id()
|
||||
|
||||
|
||||
def change_pype_mongo_url(new_mongo_url):
|
||||
"""Change mongo url in pype registry.
|
||||
|
||||
Change of Pype mongo URL require restart of running pype processes or
|
||||
processes using pype.
|
||||
"""
|
||||
|
||||
validate_mongo_connection(new_mongo_url)
|
||||
registry = PypeSettingsRegistry()
|
||||
registry.set_secure_item("pypeMongo", new_mongo_url)
|
||||
|
|
@ -93,6 +93,42 @@ def extract_port_from_url(url):
|
|||
return parsed_url.port
|
||||
|
||||
|
||||
def validate_mongo_connection(mongo_uri):
|
||||
"""Check if provided mongodb URL is valid.
|
||||
|
||||
Args:
|
||||
mongo_uri (str): URL to validate.
|
||||
|
||||
Raises:
|
||||
ValueError: When port in mongo uri is not valid.
|
||||
pymongo.errors.InvalidURI: If passed mongo is invalid.
|
||||
pymongo.errors.ServerSelectionTimeoutError: If connection timeout
|
||||
passed so probably couldn't connect to mongo server.
|
||||
|
||||
"""
|
||||
parsed = urlparse(mongo_uri)
|
||||
# Force validation of scheme
|
||||
if parsed.scheme not in ["mongodb", "mongodb+srv"]:
|
||||
raise pymongo.errors.InvalidURI((
|
||||
"Invalid URI scheme:"
|
||||
" URI must begin with 'mongodb://' or 'mongodb+srv://'"
|
||||
))
|
||||
# we have mongo connection string. Let's try if we can connect.
|
||||
components = decompose_url(mongo_uri)
|
||||
mongo_args = {
|
||||
"host": compose_url(**components),
|
||||
"serverSelectionTimeoutMS": 1000
|
||||
}
|
||||
port = components.get("port")
|
||||
if port is not None:
|
||||
mongo_args["port"] = int(port)
|
||||
|
||||
# Create connection
|
||||
client = pymongo.MongoClient(**mongo_args)
|
||||
client.server_info()
|
||||
client.close()
|
||||
|
||||
|
||||
class PypeMongoConnection:
|
||||
"""Singleton MongoDB connection.
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,10 @@ from .base import (
|
|||
ModulesManager,
|
||||
TrayModulesManager
|
||||
)
|
||||
from .settings_action import SettingsAction
|
||||
from .settings_action import (
|
||||
SettingsAction,
|
||||
LocalSettingsAction
|
||||
)
|
||||
from .rest_api import (
|
||||
RestApiModule,
|
||||
IRestApi
|
||||
|
|
@ -52,6 +55,7 @@ __all__ = (
|
|||
"TrayModulesManager",
|
||||
|
||||
"SettingsAction",
|
||||
"LocalSettingsAction",
|
||||
|
||||
"UserModule",
|
||||
"IUserModule",
|
||||
|
|
|
|||
|
|
@ -627,6 +627,7 @@ class TrayModulesManager(ModulesManager):
|
|||
"clockify",
|
||||
"standalonepublish_tool",
|
||||
"log_viewer",
|
||||
"local_settings",
|
||||
"settings"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -58,3 +58,58 @@ class SettingsAction(PypeModule, ITrayAction):
|
|||
# Reset content if was not visible
|
||||
if not was_visible:
|
||||
self.settings_window.reset()
|
||||
|
||||
|
||||
class LocalSettingsAction(PypeModule, ITrayAction):
|
||||
"""Action to show Setttings tool."""
|
||||
name = "local_settings"
|
||||
label = "Local Settings"
|
||||
|
||||
def initialize(self, _modules_settings):
|
||||
# This action is always enabled
|
||||
self.enabled = True
|
||||
|
||||
# Tray attributes
|
||||
self.settings_window = None
|
||||
|
||||
def connect_with_modules(self, *_a, **_kw):
|
||||
return
|
||||
|
||||
def tray_init(self):
|
||||
"""Initialization in tray implementation of ITrayAction."""
|
||||
self.create_settings_window()
|
||||
|
||||
def on_action_trigger(self):
|
||||
"""Implementation for action trigger of ITrayAction."""
|
||||
self.show_settings_window()
|
||||
|
||||
def create_settings_window(self):
|
||||
"""Initializa Settings Qt window."""
|
||||
if self.settings_window:
|
||||
return
|
||||
from pype.tools.settings import LocalSettingsWindow
|
||||
self.settings_window = LocalSettingsWindow()
|
||||
|
||||
def show_settings_window(self):
|
||||
"""Show settings tool window.
|
||||
|
||||
Raises:
|
||||
AssertionError: Window must be already created. Call
|
||||
`create_settings_window` before callint this method.
|
||||
"""
|
||||
if not self.settings_window:
|
||||
raise AssertionError("Window is not initialized.")
|
||||
|
||||
# Store if was visible
|
||||
was_visible = self.settings_window.isVisible()
|
||||
|
||||
# Show settings gui
|
||||
self.settings_window.show()
|
||||
|
||||
# Pull window to the front.
|
||||
self.settings_window.raise_()
|
||||
self.settings_window.activateWindow()
|
||||
|
||||
# Reset content if was not visible
|
||||
if not was_visible:
|
||||
self.settings_window.reset()
|
||||
|
|
|
|||
|
|
@ -15,7 +15,9 @@ METADATA_KEYS = (
|
|||
SYSTEM_SETTINGS_KEY = "system_settings"
|
||||
PROJECT_SETTINGS_KEY = "project_settings"
|
||||
PROJECT_ANATOMY_KEY = "project_anatomy"
|
||||
LOCAL_SETTING_KEY = "local_settings"
|
||||
|
||||
DEFAULT_PROJECT_KEY = "__default_project__"
|
||||
|
||||
__all__ = (
|
||||
"M_OVERRIDEN_KEY",
|
||||
|
|
@ -26,5 +28,6 @@ __all__ = (
|
|||
|
||||
"SYSTEM_SETTINGS_KEY",
|
||||
"PROJECT_SETTINGS_KEY",
|
||||
"PROJECT_ANATOMY_KEY"
|
||||
"PROJECT_ANATOMY_KEY",
|
||||
"LOCAL_SETTING_KEY"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -10,7 +10,8 @@ import pype
|
|||
from .constants import (
|
||||
SYSTEM_SETTINGS_KEY,
|
||||
PROJECT_SETTINGS_KEY,
|
||||
PROJECT_ANATOMY_KEY
|
||||
PROJECT_ANATOMY_KEY,
|
||||
LOCAL_SETTING_KEY
|
||||
)
|
||||
from .lib import load_json_file
|
||||
|
||||
|
|
@ -103,6 +104,28 @@ class SettingsHandler:
|
|||
pass
|
||||
|
||||
|
||||
@six.add_metaclass(ABCMeta)
|
||||
class LocalSettingsHandler:
|
||||
"""Handler that should handle about storing and loading of local settings.
|
||||
|
||||
Local settings are "workstation" specific modifications that modify how
|
||||
system and project settings look on the workstation and only there.
|
||||
"""
|
||||
@abstractmethod
|
||||
def save_local_settings(self, data):
|
||||
"""Save local data of local settings.
|
||||
|
||||
Args:
|
||||
data(dict): Data of local data with override metadata.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_local_settings(self):
|
||||
"""Studio overrides of system settings."""
|
||||
pass
|
||||
|
||||
|
||||
class SettingsFileHandler(SettingsHandler):
|
||||
def __init__(self):
|
||||
self.log = logging.getLogger("SettingsFileHandler")
|
||||
|
|
@ -495,3 +518,76 @@ class MongoSettingsHandler(SettingsHandler):
|
|||
if not project_name:
|
||||
return {}
|
||||
return self._get_project_anatomy_overrides(project_name)
|
||||
|
||||
|
||||
class MongoLocalSettingsHandler(LocalSettingsHandler):
|
||||
"""Settings handler that use mongo for store and load local settings.
|
||||
|
||||
Data have 2 query criteria. First is key "type" stored in constant
|
||||
`LOCAL_SETTING_KEY`. Second is key "site_id" which value can be obstained
|
||||
with `get_local_site_id` function.
|
||||
"""
|
||||
|
||||
def __init__(self, local_site_id=None):
|
||||
# Get mongo connection
|
||||
from pype.lib import (
|
||||
PypeMongoConnection,
|
||||
get_local_site_id
|
||||
)
|
||||
|
||||
if local_site_id is None:
|
||||
local_site_id = get_local_site_id()
|
||||
settings_collection = PypeMongoConnection.get_mongo_client()
|
||||
|
||||
# TODO prepare version of pype
|
||||
# - pype version should define how are settings saved and loaded
|
||||
|
||||
# TODO modify to not use hardcoded keys
|
||||
database_name = "pype"
|
||||
collection_name = "settings"
|
||||
|
||||
self.settings_collection = settings_collection
|
||||
|
||||
self.database_name = database_name
|
||||
self.collection_name = collection_name
|
||||
|
||||
self.collection = settings_collection[database_name][collection_name]
|
||||
|
||||
self.local_site_id = local_site_id
|
||||
|
||||
self.local_settings_cache = CacheValues()
|
||||
|
||||
def save_local_settings(self, data):
|
||||
"""Save local settings.
|
||||
|
||||
Args:
|
||||
data(dict): Data of studio overrides with override metadata.
|
||||
"""
|
||||
data = data or {}
|
||||
|
||||
self.local_settings_cache.update_data(data)
|
||||
|
||||
self.collection.replace_one(
|
||||
{
|
||||
"type": LOCAL_SETTING_KEY,
|
||||
"site_id": self.local_site_id
|
||||
},
|
||||
{
|
||||
"type": LOCAL_SETTING_KEY,
|
||||
"site_id": self.local_site_id,
|
||||
"value": self.local_settings_cache.to_json_string()
|
||||
},
|
||||
upsert=True
|
||||
)
|
||||
|
||||
def get_local_settings(self):
|
||||
"""Local settings for local site id."""
|
||||
if self.local_settings_cache.is_outdated:
|
||||
document = self.collection.find_one({
|
||||
"type": LOCAL_SETTING_KEY,
|
||||
"site_id": self.local_site_id
|
||||
})
|
||||
|
||||
self.local_settings_cache.update_from_document(document)
|
||||
|
||||
return self.local_settings_cache.data_copy()
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import os
|
|||
import json
|
||||
import functools
|
||||
import logging
|
||||
import platform
|
||||
import copy
|
||||
from .constants import (
|
||||
M_OVERRIDEN_KEY,
|
||||
|
|
@ -11,7 +12,8 @@ from .constants import (
|
|||
|
||||
SYSTEM_SETTINGS_KEY,
|
||||
PROJECT_SETTINGS_KEY,
|
||||
PROJECT_ANATOMY_KEY
|
||||
PROJECT_ANATOMY_KEY,
|
||||
DEFAULT_PROJECT_KEY
|
||||
)
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
|
@ -32,6 +34,9 @@ _DEFAULT_SETTINGS = None
|
|||
# Handler of studio overrides
|
||||
_SETTINGS_HANDLER = None
|
||||
|
||||
# Handler of local settings
|
||||
_LOCAL_SETTINGS_HANDLER = None
|
||||
|
||||
|
||||
def require_handler(func):
|
||||
@functools.wraps(func)
|
||||
|
|
@ -43,6 +48,16 @@ def require_handler(func):
|
|||
return wrapper
|
||||
|
||||
|
||||
def require_local_handler(func):
|
||||
@functools.wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
global _LOCAL_SETTINGS_HANDLER
|
||||
if _LOCAL_SETTINGS_HANDLER is None:
|
||||
_LOCAL_SETTINGS_HANDLER = create_local_settings_handler()
|
||||
return func(*args, **kwargs)
|
||||
return wrapper
|
||||
|
||||
|
||||
def create_settings_handler():
|
||||
from .handlers import MongoSettingsHandler
|
||||
# Handler can't be created in global space on initialization but only when
|
||||
|
|
@ -50,6 +65,11 @@ def create_settings_handler():
|
|||
return MongoSettingsHandler()
|
||||
|
||||
|
||||
def create_local_settings_handler():
|
||||
from .handlers import MongoLocalSettingsHandler
|
||||
return MongoLocalSettingsHandler()
|
||||
|
||||
|
||||
@require_handler
|
||||
def save_studio_settings(data):
|
||||
return _SETTINGS_HANDLER.save_studio_settings(data)
|
||||
|
|
@ -90,6 +110,16 @@ def get_project_anatomy_overrides(project_name):
|
|||
return _SETTINGS_HANDLER.get_project_anatomy_overrides(project_name)
|
||||
|
||||
|
||||
@require_local_handler
|
||||
def save_local_settings(data):
|
||||
return _LOCAL_SETTINGS_HANDLER.save_local_settings(data)
|
||||
|
||||
|
||||
@require_local_handler
|
||||
def get_local_settings():
|
||||
return _LOCAL_SETTINGS_HANDLER.get_local_settings()
|
||||
|
||||
|
||||
class DuplicatedEnvGroups(Exception):
|
||||
def __init__(self, duplicated):
|
||||
self.origin_duplicated = duplicated
|
||||
|
|
@ -309,6 +339,109 @@ def apply_overrides(source_data, override_data):
|
|||
return merge_overrides(_source_data, override_data)
|
||||
|
||||
|
||||
def apply_local_settings_on_system_settings(system_settings, local_settings):
|
||||
"""Apply local settings on studio system settings.
|
||||
|
||||
ATM local settings can modify only application executables. Executable
|
||||
values are not overriden but prepended.
|
||||
"""
|
||||
if not local_settings or "applications" not in local_settings:
|
||||
return
|
||||
|
||||
current_platform = platform.system().lower()
|
||||
for app_group_name, value in local_settings["applications"].items():
|
||||
if not value or app_group_name not in system_settings["applications"]:
|
||||
continue
|
||||
|
||||
variants = system_settings["applications"][app_group_name]["variants"]
|
||||
for app_name, app_value in value.items():
|
||||
if not app_value or app_name not in variants:
|
||||
continue
|
||||
|
||||
executable = app_value.get("executable")
|
||||
if not executable:
|
||||
continue
|
||||
platform_executables = variants[app_name]["executables"].get(
|
||||
current_platform
|
||||
)
|
||||
# TODO This is temporary fix until launch arguments will be stored
|
||||
# per platform and not per executable.
|
||||
# - local settings store only executable
|
||||
new_executables = [[executable, ""]]
|
||||
new_executables.extend(platform_executables)
|
||||
variants[app_name]["executables"] = new_executables
|
||||
|
||||
|
||||
def apply_local_settings_on_anatomy_settings(
|
||||
anatomy_settings, local_settings, project_name
|
||||
):
|
||||
"""Apply local settings on anatomy settings.
|
||||
|
||||
ATM local settings can modify project roots. Project name is required as
|
||||
local settings have data stored data by project's name.
|
||||
|
||||
Local settings override root values in this order:
|
||||
1.) Check if local settings contain overrides for default project and
|
||||
apply it's values on roots if there are any.
|
||||
2.) If passed `project_name` is not None then check project specific
|
||||
overrides in local settings for the project and apply it's value on
|
||||
roots if there are any.
|
||||
|
||||
NOTE: Root values of default project from local settings are always applied
|
||||
if are set.
|
||||
|
||||
Args:
|
||||
anatomy_settings (dict): Data for anatomy settings.
|
||||
local_settings (dict): Data of local settings.
|
||||
project_name (str): Name of project for which anatomy data are.
|
||||
"""
|
||||
if not local_settings:
|
||||
return
|
||||
|
||||
local_project_settings = local_settings.get("projects")
|
||||
if not local_project_settings:
|
||||
return
|
||||
|
||||
project_locals = local_project_settings.get(project_name) or {}
|
||||
default_locals = local_project_settings.get(DEFAULT_PROJECT_KEY) or {}
|
||||
active_site = project_locals.get("active_site")
|
||||
if not active_site:
|
||||
active_site = default_locals.get("active_site")
|
||||
|
||||
if not active_site:
|
||||
project_settings = get_project_settings(project_name)
|
||||
active_site = (
|
||||
project_settings
|
||||
["global"]
|
||||
["sync_server"]
|
||||
["config"]
|
||||
["active_site"]
|
||||
)
|
||||
|
||||
# QUESTION should raise an exception?
|
||||
if not active_site:
|
||||
return
|
||||
|
||||
roots_locals = default_locals.get("roots", {}).get(active_site, {})
|
||||
if project_name != DEFAULT_PROJECT_KEY:
|
||||
roots_locals.update(
|
||||
project_locals.get("roots", {}).get(active_site, {})
|
||||
)
|
||||
|
||||
if not roots_locals:
|
||||
return
|
||||
|
||||
current_platform = platform.system().lower()
|
||||
|
||||
root_data = anatomy_settings["roots"]
|
||||
for root_name, path in roots_locals.items():
|
||||
if root_name not in root_data:
|
||||
continue
|
||||
anatomy_settings["roots"][root_name][current_platform] = (
|
||||
path
|
||||
)
|
||||
|
||||
|
||||
def get_system_settings(clear_metadata=True):
|
||||
"""System settings with applied studio overrides."""
|
||||
default_values = get_default_settings()[SYSTEM_SETTINGS_KEY]
|
||||
|
|
@ -316,6 +449,10 @@ def get_system_settings(clear_metadata=True):
|
|||
result = apply_overrides(default_values, studio_values)
|
||||
if clear_metadata:
|
||||
clear_metadata_from_settings(result)
|
||||
# TODO local settings may be required to apply for environments
|
||||
local_settings = get_local_settings()
|
||||
apply_local_settings_on_system_settings(result, local_settings)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
|
|
@ -343,6 +480,8 @@ def get_default_anatomy_settings(clear_metadata=True):
|
|||
result[key] = value
|
||||
if clear_metadata:
|
||||
clear_metadata_from_settings(result)
|
||||
local_settings = get_local_settings()
|
||||
apply_local_settings_on_anatomy_settings(result, local_settings, None)
|
||||
return result
|
||||
|
||||
|
||||
|
|
@ -368,6 +507,10 @@ def get_anatomy_settings(project_name, clear_metadata=True):
|
|||
result[key] = value
|
||||
if clear_metadata:
|
||||
clear_metadata_from_settings(result)
|
||||
local_settings = get_local_settings()
|
||||
apply_local_settings_on_anatomy_settings(
|
||||
result, local_settings, project_name
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
|
|
|
|||
79
pype/settings/local_settings.md
Normal file
79
pype/settings/local_settings.md
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
# Structure of local settings
|
||||
- local settings do not have any validation schemas right now this should help to see what is stored to local settings and how it works
|
||||
- they are stored by identifier site_id which should be unified identifier of workstation
|
||||
- all keys may and may not available on load
|
||||
- contain main categories: `general`, `applications`, `projects`
|
||||
|
||||
## Categories
|
||||
### General
|
||||
- ATM contain only label of site
|
||||
```json
|
||||
{
|
||||
"general": {
|
||||
"site_label": "MySite"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Applications
|
||||
- modifications of application executables
|
||||
- output should match application groups and variants
|
||||
```json
|
||||
{
|
||||
"applications": {
|
||||
"<app group>": {
|
||||
"<app name>": {
|
||||
"executable": "/my/path/to/nuke_12_2"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Projects
|
||||
- project specific modifications
|
||||
- default project is stored under constant key defined in `pype.settings.contants`
|
||||
```json
|
||||
{
|
||||
"projects": {
|
||||
"<project name>": {
|
||||
"active_site": "<name of active site>",
|
||||
"remote_site": "<name of remote site>",
|
||||
"roots": {
|
||||
"<site name>": {
|
||||
"<root name>": "<root dir path>"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Final document
|
||||
```json
|
||||
{
|
||||
"_id": "<ObjectId(...)>",
|
||||
"site_id": "<site id>",
|
||||
"general": {
|
||||
"site_label": "MySite"
|
||||
},
|
||||
"applications": {
|
||||
"<app group>": {
|
||||
"<app name>": {
|
||||
"executable": "<path to app executable>"
|
||||
}
|
||||
}
|
||||
},
|
||||
"projects": {
|
||||
"<project name>": {
|
||||
"active_site": "<name of active site>",
|
||||
"remote_site": "<name of remote site>",
|
||||
"roots": {
|
||||
"<site name>": {
|
||||
"<root name>": "<root dir path>"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import sys
|
||||
from Qt import QtWidgets, QtGui
|
||||
|
||||
from .local_settings import LocalSettingsWindow
|
||||
from .settings import (
|
||||
style,
|
||||
MainWidget,
|
||||
|
|
@ -33,5 +33,6 @@ __all__ = (
|
|||
"style",
|
||||
"MainWidget",
|
||||
"ProjectListWidget",
|
||||
"LocalSettingsWindow",
|
||||
"main"
|
||||
)
|
||||
|
|
|
|||
6
pype/tools/settings/local_settings/__init__.py
Normal file
6
pype/tools/settings/local_settings/__init__.py
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
from .window import LocalSettingsWindow
|
||||
|
||||
|
||||
__all__ = (
|
||||
"LocalSettingsWindow",
|
||||
)
|
||||
205
pype/tools/settings/local_settings/apps_widget.py
Normal file
205
pype/tools/settings/local_settings/apps_widget.py
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
import platform
|
||||
from Qt import QtWidgets
|
||||
from .widgets import (
|
||||
Separator,
|
||||
ExpandingWidget
|
||||
)
|
||||
from .constants import CHILD_OFFSET
|
||||
|
||||
|
||||
class AppVariantWidget(QtWidgets.QWidget):
|
||||
exec_placeholder = "< Specific path for this machine >"
|
||||
|
||||
def __init__(self, group_label, variant_entity, parent):
|
||||
super(AppVariantWidget, self).__init__(parent)
|
||||
|
||||
self.executable_input_widget = None
|
||||
|
||||
label = " ".join([group_label, variant_entity.label])
|
||||
|
||||
expading_widget = ExpandingWidget(label, self)
|
||||
content_widget = QtWidgets.QWidget(expading_widget)
|
||||
content_layout = QtWidgets.QVBoxLayout(content_widget)
|
||||
content_layout.setContentsMargins(CHILD_OFFSET, 5, 0, 0)
|
||||
|
||||
expading_widget.set_content_widget(content_widget)
|
||||
|
||||
# Add expanding widget to main layout
|
||||
layout = QtWidgets.QHBoxLayout(self)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.addWidget(expading_widget)
|
||||
|
||||
# TODO For celaction - not sure what is "Celaction publish" for
|
||||
if not variant_entity["executables"].multiplatform:
|
||||
warn_label = QtWidgets.QLabel(
|
||||
"Application without multiplatform paths"
|
||||
)
|
||||
content_layout.addWidget(warn_label)
|
||||
return
|
||||
|
||||
executable_input_widget = QtWidgets.QLineEdit(content_widget)
|
||||
executable_input_widget.setPlaceholderText(self.exec_placeholder)
|
||||
content_layout.addWidget(executable_input_widget)
|
||||
|
||||
self.executable_input_widget = executable_input_widget
|
||||
|
||||
studio_executables = (
|
||||
variant_entity["executables"][platform.system().lower()]
|
||||
)
|
||||
if len(studio_executables) < 1:
|
||||
return
|
||||
|
||||
content_layout.addWidget(Separator(parent=self))
|
||||
content_layout.addWidget(
|
||||
QtWidgets.QLabel("Studio paths:", self)
|
||||
)
|
||||
|
||||
for item in studio_executables:
|
||||
path_widget = QtWidgets.QLineEdit(content_widget)
|
||||
path_widget.setText(item.value[0])
|
||||
path_widget.setEnabled(False)
|
||||
content_layout.addWidget(path_widget)
|
||||
|
||||
def update_local_settings(self, value):
|
||||
if not self.executable_input_widget:
|
||||
return
|
||||
|
||||
if not value:
|
||||
value = {}
|
||||
elif not isinstance(value, dict):
|
||||
print("Got invalid value type {}. Expected {}".format(
|
||||
type(value), dict
|
||||
))
|
||||
value = {}
|
||||
|
||||
executable_path = value.get("executable")
|
||||
if not executable_path:
|
||||
executable_path = ""
|
||||
elif isinstance(executable_path, list):
|
||||
print("Got list in executable path so using first item as value")
|
||||
executable_path = executable_path[0]
|
||||
|
||||
if not isinstance(executable_path, str):
|
||||
executable_path = ""
|
||||
print((
|
||||
"Got invalid value type of app executable {}. Expected {}"
|
||||
).format(type(value), str))
|
||||
|
||||
self.executable_input_widget.setText(executable_path)
|
||||
|
||||
def settings_value(self):
|
||||
if not self.executable_input_widget:
|
||||
return None
|
||||
value = self.executable_input_widget.text()
|
||||
if not value:
|
||||
return None
|
||||
return {"executable": value}
|
||||
|
||||
|
||||
class AppGroupWidget(QtWidgets.QWidget):
|
||||
def __init__(self, group_entity, parent):
|
||||
super(AppGroupWidget, self).__init__(parent)
|
||||
|
||||
valid_variants = {}
|
||||
for key, entity in group_entity["variants"].items():
|
||||
if entity["enabled"].value:
|
||||
valid_variants[key] = entity
|
||||
|
||||
group_label = group_entity.label
|
||||
expading_widget = ExpandingWidget(group_label, self)
|
||||
content_widget = QtWidgets.QWidget(expading_widget)
|
||||
content_layout = QtWidgets.QVBoxLayout(content_widget)
|
||||
content_layout.setContentsMargins(CHILD_OFFSET, 5, 0, 0)
|
||||
|
||||
widgets_by_variant_name = {}
|
||||
for variant_name, variant_entity in valid_variants.items():
|
||||
variant_widget = AppVariantWidget(
|
||||
group_label, variant_entity, content_widget
|
||||
)
|
||||
widgets_by_variant_name[variant_name] = variant_widget
|
||||
content_layout.addWidget(variant_widget)
|
||||
|
||||
expading_widget.set_content_widget(content_widget)
|
||||
|
||||
layout = QtWidgets.QHBoxLayout(self)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.addWidget(expading_widget)
|
||||
|
||||
self.widgets_by_variant_name = widgets_by_variant_name
|
||||
|
||||
def update_local_settings(self, value):
|
||||
if not value:
|
||||
value = {}
|
||||
|
||||
for variant_name, widget in self.widgets_by_variant_name.items():
|
||||
widget.update_local_settings(value.get(variant_name))
|
||||
|
||||
def settings_value(self):
|
||||
output = {}
|
||||
for variant_name, widget in self.widgets_by_variant_name.items():
|
||||
value = widget.settings_value()
|
||||
if value:
|
||||
output[variant_name] = value
|
||||
|
||||
if not output:
|
||||
return None
|
||||
return output
|
||||
|
||||
|
||||
class LocalApplicationsWidgets(QtWidgets.QWidget):
|
||||
def __init__(self, system_settings_entity, parent):
|
||||
super(LocalApplicationsWidgets, self).__init__(parent)
|
||||
|
||||
self.widgets_by_group_name = {}
|
||||
self.system_settings_entity = system_settings_entity
|
||||
|
||||
layout = QtWidgets.QVBoxLayout(self)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
self.content_layout = layout
|
||||
|
||||
def _reset_app_widgets(self):
|
||||
while self.content_layout.count() > 0:
|
||||
item = self.content_layout.itemAt(0)
|
||||
item.widget().hide()
|
||||
self.content_layout.removeItem(item)
|
||||
self.widgets_by_group_name.clear()
|
||||
|
||||
for key, entity in self.system_settings_entity["applications"].items():
|
||||
# Filter not enabled app groups
|
||||
if not entity["enabled"].value:
|
||||
continue
|
||||
|
||||
# Check if has enabled any variant
|
||||
enabled_variant = False
|
||||
for variant_entity in entity["variants"].values():
|
||||
if variant_entity["enabled"].value:
|
||||
enabled_variant = True
|
||||
break
|
||||
|
||||
if not enabled_variant:
|
||||
continue
|
||||
|
||||
# Create App group specific widget and store it by the key
|
||||
group_widget = AppGroupWidget(entity, self)
|
||||
self.widgets_by_group_name[key] = group_widget
|
||||
self.content_layout.addWidget(group_widget)
|
||||
|
||||
def update_local_settings(self, value):
|
||||
if not value:
|
||||
value = {}
|
||||
|
||||
self._reset_app_widgets()
|
||||
|
||||
for group_name, widget in self.widgets_by_group_name.items():
|
||||
widget.update_local_settings(value.get(group_name))
|
||||
|
||||
def settings_value(self):
|
||||
output = {}
|
||||
for group_name, widget in self.widgets_by_group_name.items():
|
||||
value = widget.settings_value()
|
||||
if value:
|
||||
output[group_name] = value
|
||||
if not output:
|
||||
return None
|
||||
return output
|
||||
32
pype/tools/settings/local_settings/constants.py
Normal file
32
pype/tools/settings/local_settings/constants.py
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
# Action labels
|
||||
LABEL_REMOVE_DEFAULT = "Remove from default"
|
||||
LABEL_ADD_DEFAULT = "Add to default"
|
||||
LABEL_REMOVE_PROJECT = "Remove from project"
|
||||
LABEL_ADD_PROJECT = "Add to project"
|
||||
LABEL_DISCARD_CHANGES = "Discard changes"
|
||||
|
||||
# Local setting contants
|
||||
# TODO move to settings constants
|
||||
LOCAL_GENERAL_KEY = "general"
|
||||
LOCAL_PROJECTS_KEY = "projects"
|
||||
LOCAL_APPS_KEY = "applications"
|
||||
|
||||
# Roots key constant
|
||||
LOCAL_ROOTS_KEY = "roots"
|
||||
|
||||
# Child offset in expandable widget
|
||||
CHILD_OFFSET = 15
|
||||
|
||||
__all__ = (
|
||||
"LABEL_REMOVE_DEFAULT",
|
||||
"LABEL_ADD_DEFAULT",
|
||||
"LABEL_REMOVE_PROJECT",
|
||||
"LABEL_ADD_PROJECT",
|
||||
"LABEL_DISCARD_CHANGES",
|
||||
|
||||
"LOCAL_GENERAL_KEY",
|
||||
"LOCAL_PROJECTS_KEY",
|
||||
"LOCAL_APPS_KEY",
|
||||
|
||||
"LOCAL_ROOTS_KEY"
|
||||
)
|
||||
32
pype/tools/settings/local_settings/general_widget.py
Normal file
32
pype/tools/settings/local_settings/general_widget.py
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
from Qt import QtWidgets
|
||||
|
||||
|
||||
class LocalGeneralWidgets(QtWidgets.QWidget):
|
||||
def __init__(self, parent):
|
||||
super(LocalGeneralWidgets, self).__init__(parent)
|
||||
|
||||
local_site_name_input = QtWidgets.QLineEdit(self)
|
||||
|
||||
layout = QtWidgets.QFormLayout(self)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
layout.addRow("Local site label", local_site_name_input)
|
||||
|
||||
self.local_site_name_input = local_site_name_input
|
||||
|
||||
def update_local_settings(self, value):
|
||||
site_label = ""
|
||||
if value:
|
||||
site_label = value.get("site_label", site_label)
|
||||
self.local_site_name_input.setText(site_label)
|
||||
|
||||
def settings_value(self):
|
||||
# Add changed
|
||||
# If these have changed then
|
||||
output = {}
|
||||
local_site_name = self.local_site_name_input.text()
|
||||
if local_site_name:
|
||||
output["site_label"] = local_site_name
|
||||
# Do not return output yet since we don't have mechanism to save or
|
||||
# load these data through api calls
|
||||
return output
|
||||
80
pype/tools/settings/local_settings/mongo_widget.py
Normal file
80
pype/tools/settings/local_settings/mongo_widget.py
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
import os
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
from Qt import QtWidgets
|
||||
from pymongo.errors import ServerSelectionTimeoutError
|
||||
|
||||
from pype.api import change_pype_mongo_url
|
||||
|
||||
|
||||
class PypeMongoWidget(QtWidgets.QWidget):
|
||||
def __init__(self, parent):
|
||||
super(PypeMongoWidget, self).__init__(parent)
|
||||
|
||||
# Warning label
|
||||
warning_label = QtWidgets.QLabel((
|
||||
"WARNING: Requires restart. Change of Pype Mongo requires to"
|
||||
" restart of all running Pype processes and process using Pype"
|
||||
" (Including this)."
|
||||
"\n- all changes in different categories won't be saved."
|
||||
), self)
|
||||
warning_label.setStyleSheet("font-weight: bold;")
|
||||
|
||||
# Label
|
||||
mongo_url_label = QtWidgets.QLabel("Pype Mongo URL", self)
|
||||
|
||||
# Input
|
||||
mongo_url_input = QtWidgets.QLineEdit(self)
|
||||
mongo_url_input.setPlaceholderText("< Pype Mongo URL >")
|
||||
mongo_url_input.setText(os.environ["PYPE_MONGO"])
|
||||
|
||||
# Confirm button
|
||||
mongo_url_change_btn = QtWidgets.QPushButton("Confirm Change", self)
|
||||
|
||||
layout = QtWidgets.QGridLayout(self)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.addWidget(warning_label, 0, 0, 1, 3)
|
||||
layout.addWidget(mongo_url_label, 1, 0)
|
||||
layout.addWidget(mongo_url_input, 1, 1)
|
||||
layout.addWidget(mongo_url_change_btn, 1, 2)
|
||||
|
||||
mongo_url_change_btn.clicked.connect(self._on_confirm_click)
|
||||
|
||||
self.mongo_url_input = mongo_url_input
|
||||
|
||||
def _on_confirm_click(self):
|
||||
value = self.mongo_url_input.text()
|
||||
|
||||
dialog = QtWidgets.QMessageBox(self)
|
||||
|
||||
title = "Pype mongo changed"
|
||||
message = (
|
||||
"Pype mongo url was successfully changed. Restart Pype please."
|
||||
)
|
||||
details = None
|
||||
|
||||
try:
|
||||
change_pype_mongo_url(value)
|
||||
except Exception as exc:
|
||||
if isinstance(exc, ServerSelectionTimeoutError):
|
||||
error_message = (
|
||||
"Connection timeout passed."
|
||||
" Probably can't connect to the Mongo server."
|
||||
)
|
||||
else:
|
||||
error_message = str(exc)
|
||||
|
||||
title = "Pype mongo change failed"
|
||||
# TODO catch exception message more gracefully
|
||||
message = (
|
||||
"Pype mongo change was not successful."
|
||||
" Full traceback can be found in details section.\n\n"
|
||||
"Error message:\n{}"
|
||||
).format(error_message)
|
||||
details = "\n".join(traceback.format_exception(*sys.exc_info()))
|
||||
dialog.setWindowTitle(title)
|
||||
dialog.setText(message)
|
||||
if details:
|
||||
dialog.setDetailedText(details)
|
||||
dialog.exec_()
|
||||
731
pype/tools/settings/local_settings/projects_widget.py
Normal file
731
pype/tools/settings/local_settings/projects_widget.py
Normal file
|
|
@ -0,0 +1,731 @@
|
|||
import platform
|
||||
import copy
|
||||
from Qt import QtWidgets, QtCore, QtGui
|
||||
from pype.tools.settings.settings import ProjectListWidget
|
||||
from pype.settings.constants import (
|
||||
PROJECT_ANATOMY_KEY,
|
||||
DEFAULT_PROJECT_KEY
|
||||
)
|
||||
from .widgets import (
|
||||
SpacerWidget,
|
||||
ProxyLabelWidget
|
||||
)
|
||||
from .constants import (
|
||||
LABEL_REMOVE_DEFAULT,
|
||||
LABEL_ADD_DEFAULT,
|
||||
LABEL_REMOVE_PROJECT,
|
||||
LABEL_ADD_PROJECT,
|
||||
LABEL_DISCARD_CHANGES,
|
||||
LOCAL_ROOTS_KEY
|
||||
)
|
||||
|
||||
NOT_SET = type("NOT_SET", (), {})()
|
||||
|
||||
|
||||
def get_active_sites(project_settings):
|
||||
global_entity = project_settings["project_settings"]["global"]
|
||||
sites_entity = global_entity["sync_server"]["sites"]
|
||||
return tuple(sites_entity.keys())
|
||||
|
||||
|
||||
class _ProjectListWidget(ProjectListWidget):
|
||||
def on_item_clicked(self, new_index):
|
||||
new_project_name = new_index.data(QtCore.Qt.DisplayRole)
|
||||
if new_project_name is None:
|
||||
return
|
||||
|
||||
if self.current_project == new_project_name:
|
||||
return
|
||||
|
||||
self.select_project(new_project_name)
|
||||
self.current_project = new_project_name
|
||||
self.project_changed.emit()
|
||||
|
||||
|
||||
class RootInputWidget(QtWidgets.QWidget):
|
||||
def __init__(
|
||||
self,
|
||||
local_project_settings,
|
||||
local_project_settings_orig,
|
||||
platform_root_entity,
|
||||
root_name,
|
||||
project_name,
|
||||
site_name,
|
||||
parent
|
||||
):
|
||||
super(RootInputWidget, self).__init__(parent)
|
||||
|
||||
self.local_project_settings = local_project_settings
|
||||
self.local_project_settings_orig = local_project_settings_orig
|
||||
self.platform_root_entity = platform_root_entity
|
||||
self.root_name = root_name
|
||||
self.site_name = site_name
|
||||
self.project_name = project_name
|
||||
|
||||
self.origin_value = self._get_site_value_for_project(
|
||||
self.project_name, self.local_project_settings_orig
|
||||
) or ""
|
||||
|
||||
is_default_project = bool(project_name == DEFAULT_PROJECT_KEY)
|
||||
|
||||
default_input_value = self._get_site_value_for_project(
|
||||
DEFAULT_PROJECT_KEY
|
||||
)
|
||||
if is_default_project:
|
||||
input_value = default_input_value
|
||||
project_value = None
|
||||
else:
|
||||
input_value = self._get_site_value_for_project(self.project_name)
|
||||
project_value = input_value
|
||||
|
||||
# Placeholder
|
||||
placeholder = None
|
||||
if not is_default_project:
|
||||
placeholder = default_input_value
|
||||
|
||||
if not placeholder:
|
||||
placeholder = platform_root_entity.value
|
||||
|
||||
key_label = ProxyLabelWidget(
|
||||
root_name,
|
||||
self._mouse_release_callback,
|
||||
self
|
||||
)
|
||||
value_input = QtWidgets.QLineEdit(self)
|
||||
value_input.setPlaceholderText("< {} >".format(placeholder))
|
||||
|
||||
# Root value
|
||||
if input_value:
|
||||
value_input.setText(input_value)
|
||||
|
||||
value_input.textChanged.connect(self._on_value_change)
|
||||
|
||||
root_layout = QtWidgets.QHBoxLayout(self)
|
||||
root_layout.addWidget(key_label)
|
||||
root_layout.addWidget(value_input)
|
||||
|
||||
self.value_input = value_input
|
||||
self.label_widget = key_label
|
||||
|
||||
self.studio_value = platform_root_entity.value
|
||||
self.default_value = default_input_value
|
||||
self.project_value = project_value
|
||||
self.placeholder_value = placeholder
|
||||
|
||||
self._update_style()
|
||||
|
||||
def is_modified(self):
|
||||
return self.origin_value != self.value_input.text()
|
||||
|
||||
def _mouse_release_callback(self, event):
|
||||
if event.button() != QtCore.Qt.RightButton:
|
||||
return
|
||||
self._show_actions()
|
||||
event.accept()
|
||||
|
||||
def _get_style_state(self):
|
||||
if self.project_name is None:
|
||||
return ""
|
||||
|
||||
if self.is_modified():
|
||||
return "modified"
|
||||
|
||||
current_value = self.value_input.text()
|
||||
if self.project_name == DEFAULT_PROJECT_KEY:
|
||||
if current_value:
|
||||
return "studio"
|
||||
else:
|
||||
if current_value:
|
||||
return "overriden"
|
||||
|
||||
studio_value = self._get_site_value_for_project(
|
||||
DEFAULT_PROJECT_KEY
|
||||
)
|
||||
if studio_value:
|
||||
return "studio"
|
||||
return ""
|
||||
|
||||
def _update_style(self):
|
||||
state = self._get_style_state()
|
||||
|
||||
self.value_input.setProperty("input-state", state)
|
||||
self.value_input.style().polish(self.value_input)
|
||||
|
||||
self.label_widget.set_label_property("state", state)
|
||||
|
||||
def _remove_from_local(self):
|
||||
self.value_input.setText("")
|
||||
self._update_style()
|
||||
|
||||
def _add_to_local(self):
|
||||
self.value_input.setText(self.placeholder_value)
|
||||
self._update_style()
|
||||
|
||||
def discard_changes(self):
|
||||
self.value_input.setText(self.origin_value)
|
||||
self._update_style()
|
||||
|
||||
def _show_actions(self):
|
||||
if self.project_name is None:
|
||||
return
|
||||
|
||||
menu = QtWidgets.QMenu(self)
|
||||
actions_mapping = {}
|
||||
|
||||
if self.project_name == DEFAULT_PROJECT_KEY:
|
||||
remove_label = LABEL_REMOVE_DEFAULT
|
||||
add_label = LABEL_ADD_DEFAULT
|
||||
else:
|
||||
remove_label = LABEL_REMOVE_PROJECT
|
||||
add_label = LABEL_ADD_PROJECT
|
||||
|
||||
if self.value_input.text():
|
||||
action = QtWidgets.QAction(remove_label)
|
||||
callback = self._remove_from_local
|
||||
else:
|
||||
action = QtWidgets.QAction(add_label)
|
||||
callback = self._add_to_local
|
||||
|
||||
actions_mapping[action] = callback
|
||||
menu.addAction(action)
|
||||
|
||||
if self.is_modified():
|
||||
discard_changes_action = QtWidgets.QAction(LABEL_DISCARD_CHANGES)
|
||||
actions_mapping[discard_changes_action] = self.discard_changes
|
||||
menu.addAction(discard_changes_action)
|
||||
|
||||
result = menu.exec_(QtGui.QCursor.pos())
|
||||
if result:
|
||||
to_run = actions_mapping[result]
|
||||
if to_run:
|
||||
to_run()
|
||||
|
||||
def _get_site_value_for_project(self, project_name, data=None):
|
||||
if data is None:
|
||||
data = self.local_project_settings
|
||||
project_values = data.get(project_name)
|
||||
site_value = {}
|
||||
if project_values:
|
||||
root_value = project_values.get(LOCAL_ROOTS_KEY)
|
||||
if root_value:
|
||||
site_value = root_value.get(self.site_name) or {}
|
||||
return site_value.get(self.root_name)
|
||||
|
||||
def _on_value_change(self):
|
||||
value = self.value_input.text()
|
||||
data = self.local_project_settings
|
||||
for key in (self.project_name, LOCAL_ROOTS_KEY, self.site_name):
|
||||
if key not in data:
|
||||
data[key] = {}
|
||||
data = data[key]
|
||||
data[self.root_name] = value
|
||||
self._update_style()
|
||||
|
||||
|
||||
class RootsWidget(QtWidgets.QWidget):
|
||||
def __init__(self, project_settings, parent):
|
||||
super(RootsWidget, self).__init__(parent)
|
||||
|
||||
self.project_settings = project_settings
|
||||
self.site_widgets = []
|
||||
self.local_project_settings = None
|
||||
self.local_project_settings_orig = None
|
||||
self._project_name = None
|
||||
|
||||
self.content_layout = QtWidgets.QVBoxLayout(self)
|
||||
|
||||
def _clear_widgets(self):
|
||||
while self.content_layout.count():
|
||||
item = self.content_layout.itemAt(0)
|
||||
item.widget().hide()
|
||||
self.content_layout.removeItem(item)
|
||||
self.site_widgets = []
|
||||
|
||||
def refresh(self):
|
||||
self._clear_widgets()
|
||||
|
||||
if self._project_name is None:
|
||||
return
|
||||
|
||||
roots_entity = (
|
||||
self.project_settings[PROJECT_ANATOMY_KEY][LOCAL_ROOTS_KEY]
|
||||
)
|
||||
# Site label
|
||||
for site_name in get_active_sites(self.project_settings):
|
||||
site_widget = QtWidgets.QWidget(self)
|
||||
site_layout = QtWidgets.QVBoxLayout(site_widget)
|
||||
|
||||
site_label = QtWidgets.QLabel(site_name, site_widget)
|
||||
|
||||
site_layout.addWidget(site_label)
|
||||
|
||||
# Root inputs
|
||||
for root_name, path_entity in roots_entity.items():
|
||||
platform_entity = path_entity[platform.system().lower()]
|
||||
root_widget = RootInputWidget(
|
||||
self.local_project_settings,
|
||||
self.local_project_settings_orig,
|
||||
platform_entity,
|
||||
root_name,
|
||||
self._project_name,
|
||||
site_name,
|
||||
site_widget
|
||||
)
|
||||
|
||||
site_layout.addWidget(root_widget)
|
||||
|
||||
self.site_widgets.append(site_widget)
|
||||
self.content_layout.addWidget(site_widget)
|
||||
|
||||
# Add spacer so other widgets are squeezed to top
|
||||
self.content_layout.addWidget(SpacerWidget(self), 1)
|
||||
|
||||
def update_local_settings(self, local_project_settings):
|
||||
self.local_project_settings = local_project_settings
|
||||
self.local_project_settings_orig = copy.deepcopy(
|
||||
dict(local_project_settings)
|
||||
)
|
||||
|
||||
def change_project(self, project_name):
|
||||
self._project_name = project_name
|
||||
self.refresh()
|
||||
|
||||
|
||||
class _SiteCombobox(QtWidgets.QWidget):
|
||||
input_label = None
|
||||
|
||||
def __init__(self, project_settings, parent):
|
||||
super(_SiteCombobox, self).__init__(parent)
|
||||
self.project_settings = project_settings
|
||||
|
||||
self.local_project_settings = None
|
||||
self.local_project_settings_orig = None
|
||||
self.project_name = None
|
||||
|
||||
self.default_override_value = None
|
||||
self.project_override_value = None
|
||||
|
||||
label_widget = ProxyLabelWidget(
|
||||
self.input_label,
|
||||
self._mouse_release_callback,
|
||||
self
|
||||
)
|
||||
combobox_input = QtWidgets.QComboBox(self)
|
||||
|
||||
main_layout = QtWidgets.QHBoxLayout(self)
|
||||
main_layout.addWidget(label_widget)
|
||||
main_layout.addWidget(combobox_input)
|
||||
|
||||
combobox_input.currentIndexChanged.connect(self._on_index_change)
|
||||
self.label_widget = label_widget
|
||||
self.combobox_input = combobox_input
|
||||
|
||||
def _set_current_text(self, text):
|
||||
index = None
|
||||
if text:
|
||||
idx = self.combobox_input.findText(text)
|
||||
if idx >= 0:
|
||||
index = idx
|
||||
|
||||
if index is not None:
|
||||
self.combobox_input.setCurrentIndex(index)
|
||||
return True
|
||||
return False
|
||||
|
||||
def is_modified(self, current_value=NOT_SET, orig_value=NOT_SET):
|
||||
if current_value is NOT_SET:
|
||||
current_value = self._get_local_settings_item(self.project_name)
|
||||
if orig_value is NOT_SET:
|
||||
orig_value = self._get_local_settings_item(
|
||||
self.project_name, self.local_project_settings_orig
|
||||
)
|
||||
if current_value and orig_value:
|
||||
modified = current_value != orig_value
|
||||
elif not current_value and not orig_value:
|
||||
modified = False
|
||||
else:
|
||||
modified = True
|
||||
return modified
|
||||
|
||||
def _get_style_state(self):
|
||||
if self.project_name is None:
|
||||
return ""
|
||||
|
||||
current_value = self._get_local_settings_item(self.project_name)
|
||||
orig_value = self._get_local_settings_item(
|
||||
self.project_name, self.local_project_settings_orig
|
||||
)
|
||||
|
||||
if self.is_modified(current_value, orig_value):
|
||||
return "modified"
|
||||
|
||||
if self.project_name == DEFAULT_PROJECT_KEY:
|
||||
if current_value:
|
||||
return "studio"
|
||||
else:
|
||||
if current_value:
|
||||
return "overriden"
|
||||
|
||||
studio_value = self._get_local_settings_item(DEFAULT_PROJECT_KEY)
|
||||
if studio_value:
|
||||
return "studio"
|
||||
return ""
|
||||
|
||||
def _update_style(self):
|
||||
state = self._get_style_state()
|
||||
|
||||
self.combobox_input.setProperty("input-state", state)
|
||||
self.combobox_input.style().polish(self.combobox_input)
|
||||
|
||||
self.label_widget.set_label_property("state", state)
|
||||
|
||||
def _mouse_release_callback(self, event):
|
||||
if event.button() != QtCore.Qt.RightButton:
|
||||
return
|
||||
self._show_actions()
|
||||
|
||||
def _remove_from_local(self):
|
||||
settings_value = self._get_value_from_project_settings()
|
||||
combobox_value = None
|
||||
if self.project_name == DEFAULT_PROJECT_KEY:
|
||||
combobox_value = self._get_local_settings_item(DEFAULT_PROJECT_KEY)
|
||||
if combobox_value:
|
||||
idx = self.combobox_input.findText(combobox_value)
|
||||
if idx < 0:
|
||||
combobox_value = None
|
||||
|
||||
if not combobox_value:
|
||||
combobox_value = settings_value
|
||||
|
||||
if combobox_value:
|
||||
_project_name = self.project_name
|
||||
self.project_name = None
|
||||
self._set_current_text(combobox_value)
|
||||
self.project_name = _project_name
|
||||
|
||||
self._set_local_settings_value("")
|
||||
self._update_style()
|
||||
|
||||
def _add_to_local(self):
|
||||
self._set_local_settings_value(self.current_text())
|
||||
self._update_style()
|
||||
|
||||
def discard_changes(self):
|
||||
orig_value = self._get_local_settings_item(
|
||||
self.project_name, self.local_project_settings_orig
|
||||
)
|
||||
self._set_current_text(orig_value)
|
||||
|
||||
def _show_actions(self):
|
||||
if self.project_name is None:
|
||||
return
|
||||
|
||||
menu = QtWidgets.QMenu(self)
|
||||
actions_mapping = {}
|
||||
|
||||
if self.project_name == DEFAULT_PROJECT_KEY:
|
||||
remove_label = LABEL_REMOVE_DEFAULT
|
||||
add_label = LABEL_ADD_DEFAULT
|
||||
else:
|
||||
remove_label = LABEL_REMOVE_PROJECT
|
||||
add_label = LABEL_ADD_PROJECT
|
||||
|
||||
has_value = self._get_local_settings_item(self.project_name)
|
||||
if has_value:
|
||||
action = QtWidgets.QAction(remove_label)
|
||||
callback = self._remove_from_local
|
||||
else:
|
||||
action = QtWidgets.QAction(add_label)
|
||||
callback = self._add_to_local
|
||||
|
||||
actions_mapping[action] = callback
|
||||
menu.addAction(action)
|
||||
|
||||
if self.is_modified():
|
||||
discard_changes_action = QtWidgets.QAction(LABEL_DISCARD_CHANGES)
|
||||
actions_mapping[discard_changes_action] = self.discard_changes
|
||||
menu.addAction(discard_changes_action)
|
||||
|
||||
result = menu.exec_(QtGui.QCursor.pos())
|
||||
if result:
|
||||
to_run = actions_mapping[result]
|
||||
if to_run:
|
||||
to_run()
|
||||
|
||||
def update_local_settings(self, local_project_settings):
|
||||
self.local_project_settings = local_project_settings
|
||||
self.local_project_settings_orig = copy.deepcopy(
|
||||
dict(local_project_settings)
|
||||
)
|
||||
|
||||
def current_text(self):
|
||||
return self.combobox_input.currentText()
|
||||
|
||||
def change_project(self, project_name):
|
||||
self.default_override_value = None
|
||||
self.project_override_value = None
|
||||
|
||||
self.project_name = None
|
||||
self.combobox_input.clear()
|
||||
if project_name is None:
|
||||
self._update_style()
|
||||
return
|
||||
|
||||
is_default_project = bool(project_name == DEFAULT_PROJECT_KEY)
|
||||
site_items = self._get_project_sites()
|
||||
self.combobox_input.addItems(site_items)
|
||||
|
||||
default_item = self._get_local_settings_item(DEFAULT_PROJECT_KEY)
|
||||
if is_default_project:
|
||||
project_item = None
|
||||
else:
|
||||
project_item = self._get_local_settings_item(project_name)
|
||||
|
||||
index = None
|
||||
if project_item:
|
||||
idx = self.combobox_input.findText(project_item)
|
||||
if idx >= 0:
|
||||
self.project_override_value = project_item
|
||||
index = idx
|
||||
|
||||
if default_item:
|
||||
idx = self.combobox_input.findText(default_item)
|
||||
if idx >= 0:
|
||||
self.default_override_value = default_item
|
||||
if index is None:
|
||||
index = idx
|
||||
|
||||
if index is None:
|
||||
settings_value = self._get_value_from_project_settings()
|
||||
idx = self.combobox_input.findText(settings_value)
|
||||
if idx >= 0:
|
||||
index = idx
|
||||
|
||||
if index is not None:
|
||||
self.combobox_input.setCurrentIndex(index)
|
||||
|
||||
self.project_name = project_name
|
||||
self._update_style()
|
||||
|
||||
def _on_index_change(self):
|
||||
if self.project_name is None:
|
||||
return
|
||||
|
||||
self._set_local_settings_value(self.current_text())
|
||||
self._update_style()
|
||||
|
||||
def _set_local_settings_value(self, value):
|
||||
raise NotImplementedError(
|
||||
"{} `_set_local_settings_value` not implemented".format(
|
||||
self.__class__.__name__
|
||||
)
|
||||
)
|
||||
|
||||
def _get_project_sites(self):
|
||||
raise NotImplementedError(
|
||||
"{} `_get_project_sites` not implemented".format(
|
||||
self.__class__.__name__
|
||||
)
|
||||
)
|
||||
|
||||
def _get_local_settings_item(self, project_name=None, data=None):
|
||||
raise NotImplementedError(
|
||||
"{}`_get_local_settings_item` not implemented".format(
|
||||
self.__class__.__name__
|
||||
)
|
||||
)
|
||||
|
||||
def _get_value_from_project_settings(self):
|
||||
raise NotImplementedError(
|
||||
"{}`_get_value_from_project_settings` not implemented".format(
|
||||
self.__class__.__name__
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class AciveSiteCombo(_SiteCombobox):
|
||||
input_label = "Active site"
|
||||
|
||||
def _get_project_sites(self):
|
||||
return get_active_sites(self.project_settings)
|
||||
|
||||
def _get_local_settings_item(self, project_name=None, data=None):
|
||||
if project_name is None:
|
||||
project_name = self.project_name
|
||||
|
||||
if data is None:
|
||||
data = self.local_project_settings
|
||||
project_values = data.get(project_name)
|
||||
value = None
|
||||
if project_values:
|
||||
value = project_values.get("active_site")
|
||||
return value
|
||||
|
||||
def _get_value_from_project_settings(self):
|
||||
global_entity = self.project_settings["project_settings"]["global"]
|
||||
return global_entity["sync_server"]["config"]["active_site"].value
|
||||
|
||||
def _set_local_settings_value(self, value):
|
||||
if self.project_name not in self.local_project_settings:
|
||||
self.local_project_settings[self.project_name] = {}
|
||||
self.local_project_settings[self.project_name]["active_site"] = value
|
||||
|
||||
|
||||
class RemoteSiteCombo(_SiteCombobox):
|
||||
input_label = "Remote site"
|
||||
|
||||
def _get_project_sites(self):
|
||||
global_entity = self.project_settings["project_settings"]["global"]
|
||||
sites_entity = global_entity["sync_server"]["sites"]
|
||||
return tuple(sites_entity.keys())
|
||||
|
||||
def _get_local_settings_item(self, project_name=None, data=None):
|
||||
if project_name is None:
|
||||
project_name = self.project_name
|
||||
if data is None:
|
||||
data = self.local_project_settings
|
||||
project_values = data.get(project_name)
|
||||
value = None
|
||||
if project_values:
|
||||
value = project_values.get("remote_site")
|
||||
return value
|
||||
|
||||
def _get_value_from_project_settings(self):
|
||||
global_entity = self.project_settings["project_settings"]["global"]
|
||||
return global_entity["sync_server"]["config"]["remote_site"].value
|
||||
|
||||
def _set_local_settings_value(self, value):
|
||||
if self.project_name not in self.local_project_settings:
|
||||
self.local_project_settings[self.project_name] = {}
|
||||
self.local_project_settings[self.project_name]["remote_site"] = value
|
||||
|
||||
|
||||
class RootSiteWidget(QtWidgets.QWidget):
|
||||
def __init__(self, project_settings, parent):
|
||||
self._parent_widget = parent
|
||||
super(RootSiteWidget, self).__init__(parent)
|
||||
|
||||
self.project_settings = project_settings
|
||||
self._project_name = None
|
||||
|
||||
sites_widget = QtWidgets.QWidget(self)
|
||||
|
||||
active_site_widget = AciveSiteCombo(project_settings, sites_widget)
|
||||
remote_site_widget = RemoteSiteCombo(project_settings, sites_widget)
|
||||
|
||||
sites_layout = QtWidgets.QHBoxLayout(sites_widget)
|
||||
sites_layout.setContentsMargins(0, 0, 0, 0)
|
||||
sites_layout.addWidget(active_site_widget)
|
||||
sites_layout.addWidget(remote_site_widget)
|
||||
sites_layout.addWidget(SpacerWidget(self), 1)
|
||||
|
||||
roots_widget = RootsWidget(project_settings, self)
|
||||
|
||||
main_layout = QtWidgets.QVBoxLayout(self)
|
||||
main_layout.addWidget(sites_widget)
|
||||
main_layout.addWidget(roots_widget)
|
||||
main_layout.addWidget(SpacerWidget(self), 1)
|
||||
|
||||
self.active_site_widget = active_site_widget
|
||||
self.remote_site_widget = remote_site_widget
|
||||
self.roots_widget = roots_widget
|
||||
|
||||
def _active_site_values(self):
|
||||
global_entity = self.project_settings["project_settings"]["global"]
|
||||
sites_entity = global_entity["sync_server"]["sites"]
|
||||
return tuple(sites_entity.keys())
|
||||
|
||||
def _remote_site_values(self):
|
||||
global_entity = self.project_settings["project_settings"]["global"]
|
||||
sites_entity = global_entity["sync_server"]["sites"]
|
||||
return tuple(sites_entity.keys())
|
||||
|
||||
def update_local_settings(self, local_project_settings):
|
||||
self.local_project_settings = local_project_settings
|
||||
self.active_site_widget.update_local_settings(local_project_settings)
|
||||
self.remote_site_widget.update_local_settings(local_project_settings)
|
||||
self.roots_widget.update_local_settings(local_project_settings)
|
||||
project_name = self._project_name
|
||||
if project_name is None:
|
||||
project_name = DEFAULT_PROJECT_KEY
|
||||
|
||||
self.change_project(project_name)
|
||||
|
||||
def change_project(self, project_name):
|
||||
self._project_name = project_name
|
||||
# Set roots project to None so all changes below are ignored
|
||||
self.roots_widget.change_project(None)
|
||||
|
||||
# Aply changes in site comboboxes
|
||||
self.active_site_widget.change_project(project_name)
|
||||
self.remote_site_widget.change_project(project_name)
|
||||
|
||||
# Change project name in roots widget
|
||||
self.roots_widget.change_project(project_name)
|
||||
|
||||
|
||||
class ProjectValue(dict):
|
||||
pass
|
||||
|
||||
|
||||
class ProjectSettingsWidget(QtWidgets.QWidget):
|
||||
def __init__(self, project_settings, parent):
|
||||
super(ProjectSettingsWidget, self).__init__(parent)
|
||||
|
||||
self.local_project_settings = {}
|
||||
|
||||
projects_widget = _ProjectListWidget(self)
|
||||
roos_site_widget = RootSiteWidget(project_settings, self)
|
||||
|
||||
main_layout = QtWidgets.QHBoxLayout(self)
|
||||
main_layout.setContentsMargins(0, 0, 0, 0)
|
||||
main_layout.addWidget(projects_widget, 0)
|
||||
main_layout.addWidget(roos_site_widget, 1)
|
||||
|
||||
projects_widget.project_changed.connect(self._on_project_change)
|
||||
|
||||
self.project_settings = project_settings
|
||||
|
||||
self.projects_widget = projects_widget
|
||||
self.roos_site_widget = roos_site_widget
|
||||
|
||||
def project_name(self):
|
||||
return self.projects_widget.project_name()
|
||||
|
||||
def _on_project_change(self):
|
||||
project_name = self.project_name()
|
||||
self.project_settings.change_project(project_name)
|
||||
if project_name is None:
|
||||
project_name = DEFAULT_PROJECT_KEY
|
||||
self.roos_site_widget.change_project(project_name)
|
||||
|
||||
def update_local_settings(self, value):
|
||||
if not value:
|
||||
value = {}
|
||||
self.local_project_settings = ProjectValue(value)
|
||||
|
||||
self.roos_site_widget.update_local_settings(
|
||||
self.local_project_settings
|
||||
)
|
||||
|
||||
self.projects_widget.refresh()
|
||||
|
||||
def _clear_value(self, value):
|
||||
if not value:
|
||||
return None
|
||||
|
||||
if not isinstance(value, dict):
|
||||
return value
|
||||
|
||||
output = {}
|
||||
for _key, _value in value.items():
|
||||
_modified_value = self._clear_value(_value)
|
||||
if _modified_value:
|
||||
output[_key] = _modified_value
|
||||
return output
|
||||
|
||||
def settings_value(self):
|
||||
output = self._clear_value(self.local_project_settings)
|
||||
if not output:
|
||||
return None
|
||||
return output
|
||||
59
pype/tools/settings/local_settings/widgets.py
Normal file
59
pype/tools/settings/local_settings/widgets.py
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
from Qt import QtWidgets, QtCore
|
||||
from pype.tools.settings.settings.widgets.widgets import (
|
||||
ExpandingWidget,
|
||||
SpacerWidget
|
||||
)
|
||||
|
||||
|
||||
class Separator(QtWidgets.QFrame):
|
||||
def __init__(self, height=None, parent=None):
|
||||
super(Separator, self).__init__(parent)
|
||||
if height is None:
|
||||
height = 2
|
||||
|
||||
splitter_item = QtWidgets.QWidget(self)
|
||||
splitter_item.setStyleSheet("background-color: #21252B;")
|
||||
splitter_item.setMinimumHeight(height)
|
||||
splitter_item.setMaximumHeight(height)
|
||||
|
||||
layout = QtWidgets.QHBoxLayout(self)
|
||||
layout.setContentsMargins(5, 5, 5, 5)
|
||||
layout.addWidget(splitter_item)
|
||||
|
||||
|
||||
class ProxyLabelWidget(QtWidgets.QWidget):
|
||||
def __init__(self, label, mouse_release_callback, parent=None):
|
||||
super(ProxyLabelWidget, self).__init__(parent)
|
||||
|
||||
self.mouse_release_callback = mouse_release_callback
|
||||
|
||||
layout = QtWidgets.QVBoxLayout(self)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setSpacing(0)
|
||||
|
||||
label_widget = QtWidgets.QLabel(label, self)
|
||||
layout.addWidget(label_widget)
|
||||
|
||||
self.setAttribute(QtCore.Qt.WA_TranslucentBackground)
|
||||
|
||||
self.label_widget = label_widget
|
||||
|
||||
def setText(self, text):
|
||||
self.label_widget.setText(text)
|
||||
|
||||
def set_label_property(self, *args, **kwargs):
|
||||
self.label_widget.setProperty(*args, **kwargs)
|
||||
self.label_widget.style().polish(self.label_widget)
|
||||
|
||||
def mouseReleaseEvent(self, event):
|
||||
if self.mouse_release_callback:
|
||||
return self.mouse_release_callback(event)
|
||||
return super(ProxyLabelWidget, self).mouseReleaseEvent(event)
|
||||
|
||||
|
||||
__all__ = (
|
||||
"ExpandingWidget",
|
||||
"SpacerWidget",
|
||||
"Separator",
|
||||
"SpacerWidget"
|
||||
)
|
||||
204
pype/tools/settings/local_settings/window.py
Normal file
204
pype/tools/settings/local_settings/window.py
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
import logging
|
||||
from Qt import QtWidgets, QtGui
|
||||
|
||||
from ..settings import style
|
||||
|
||||
from pype.settings.lib import (
|
||||
get_local_settings,
|
||||
save_local_settings
|
||||
)
|
||||
from pype.api import (
|
||||
SystemSettings,
|
||||
ProjectSettings
|
||||
)
|
||||
|
||||
from .widgets import (
|
||||
SpacerWidget,
|
||||
ExpandingWidget
|
||||
)
|
||||
from .mongo_widget import PypeMongoWidget
|
||||
from .general_widget import LocalGeneralWidgets
|
||||
from .apps_widget import LocalApplicationsWidgets
|
||||
from .projects_widget import ProjectSettingsWidget
|
||||
|
||||
from .constants import (
|
||||
CHILD_OFFSET,
|
||||
LOCAL_GENERAL_KEY,
|
||||
LOCAL_PROJECTS_KEY,
|
||||
LOCAL_APPS_KEY
|
||||
)
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class LocalSettingsWidget(QtWidgets.QWidget):
|
||||
def __init__(self, parent=None):
|
||||
super(LocalSettingsWidget, self).__init__(parent)
|
||||
|
||||
self.system_settings = SystemSettings()
|
||||
self.project_settings = ProjectSettings()
|
||||
|
||||
self.main_layout = QtWidgets.QVBoxLayout(self)
|
||||
|
||||
self.pype_mongo_widget = None
|
||||
self.general_widget = None
|
||||
self.apps_widget = None
|
||||
self.projects_widget = None
|
||||
|
||||
self._create_pype_mongo_ui()
|
||||
self._create_general_ui()
|
||||
self._create_app_ui()
|
||||
self._create_project_ui()
|
||||
|
||||
# Add spacer to main layout
|
||||
self.main_layout.addWidget(SpacerWidget(self), 1)
|
||||
|
||||
def _create_pype_mongo_ui(self):
|
||||
pype_mongo_expand_widget = ExpandingWidget("Pype Mongo URL", self)
|
||||
pype_mongo_content = QtWidgets.QWidget(self)
|
||||
pype_mongo_layout = QtWidgets.QVBoxLayout(pype_mongo_content)
|
||||
pype_mongo_layout.setContentsMargins(CHILD_OFFSET, 5, 0, 0)
|
||||
pype_mongo_expand_widget.set_content_widget(pype_mongo_content)
|
||||
|
||||
pype_mongo_widget = PypeMongoWidget(self)
|
||||
pype_mongo_layout.addWidget(pype_mongo_widget)
|
||||
|
||||
self.main_layout.addWidget(pype_mongo_expand_widget)
|
||||
|
||||
self.pype_mongo_widget = pype_mongo_widget
|
||||
|
||||
def _create_general_ui(self):
|
||||
# General
|
||||
general_expand_widget = ExpandingWidget("General", self)
|
||||
|
||||
general_content = QtWidgets.QWidget(self)
|
||||
general_layout = QtWidgets.QVBoxLayout(general_content)
|
||||
general_layout.setContentsMargins(CHILD_OFFSET, 5, 0, 0)
|
||||
general_expand_widget.set_content_widget(general_content)
|
||||
|
||||
general_widget = LocalGeneralWidgets(general_content)
|
||||
general_layout.addWidget(general_widget)
|
||||
|
||||
self.main_layout.addWidget(general_expand_widget)
|
||||
|
||||
self.general_widget = general_widget
|
||||
|
||||
def _create_app_ui(self):
|
||||
# Applications
|
||||
app_expand_widget = ExpandingWidget("Applications", self)
|
||||
|
||||
app_content = QtWidgets.QWidget(self)
|
||||
app_layout = QtWidgets.QVBoxLayout(app_content)
|
||||
app_layout.setContentsMargins(CHILD_OFFSET, 5, 0, 0)
|
||||
app_expand_widget.set_content_widget(app_content)
|
||||
|
||||
app_widget = LocalApplicationsWidgets(
|
||||
self.system_settings, app_content
|
||||
)
|
||||
app_layout.addWidget(app_widget)
|
||||
|
||||
self.main_layout.addWidget(app_expand_widget)
|
||||
|
||||
self.app_widget = app_widget
|
||||
|
||||
def _create_project_ui(self):
|
||||
project_expand_widget = ExpandingWidget("Project settings", self)
|
||||
project_content = QtWidgets.QWidget(self)
|
||||
project_layout = QtWidgets.QVBoxLayout(project_content)
|
||||
project_layout.setContentsMargins(CHILD_OFFSET, 5, 0, 0)
|
||||
project_expand_widget.set_content_widget(project_content)
|
||||
|
||||
projects_widget = ProjectSettingsWidget(self.project_settings, self)
|
||||
project_layout.addWidget(projects_widget)
|
||||
|
||||
self.main_layout.addWidget(project_expand_widget)
|
||||
|
||||
self.projects_widget = projects_widget
|
||||
|
||||
def update_local_settings(self, value):
|
||||
if not value:
|
||||
value = {}
|
||||
|
||||
self.system_settings.reset()
|
||||
self.project_settings.reset()
|
||||
|
||||
self.general_widget.update_local_settings(
|
||||
value.get(LOCAL_GENERAL_KEY)
|
||||
)
|
||||
self.app_widget.update_local_settings(
|
||||
value.get(LOCAL_APPS_KEY)
|
||||
)
|
||||
self.projects_widget.update_local_settings(
|
||||
value.get(LOCAL_PROJECTS_KEY)
|
||||
)
|
||||
|
||||
def settings_value(self):
|
||||
output = {}
|
||||
general_value = self.general_widget.settings_value()
|
||||
if general_value:
|
||||
output[LOCAL_GENERAL_KEY] = general_value
|
||||
|
||||
app_value = self.app_widget.settings_value()
|
||||
if app_value:
|
||||
output[LOCAL_APPS_KEY] = app_value
|
||||
|
||||
projects_value = self.projects_widget.settings_value()
|
||||
if projects_value:
|
||||
output[LOCAL_PROJECTS_KEY] = projects_value
|
||||
return output
|
||||
|
||||
|
||||
class LocalSettingsWindow(QtWidgets.QWidget):
|
||||
def __init__(self, parent=None):
|
||||
super(LocalSettingsWindow, self).__init__(parent)
|
||||
|
||||
self.resize(1000, 600)
|
||||
|
||||
self.setWindowTitle("Pype Local settings")
|
||||
|
||||
stylesheet = style.load_stylesheet()
|
||||
self.setStyleSheet(stylesheet)
|
||||
self.setWindowIcon(QtGui.QIcon(style.app_icon_path()))
|
||||
|
||||
scroll_widget = QtWidgets.QScrollArea(self)
|
||||
scroll_widget.setObjectName("GroupWidget")
|
||||
settings_widget = LocalSettingsWidget(scroll_widget)
|
||||
|
||||
scroll_widget.setWidget(settings_widget)
|
||||
scroll_widget.setWidgetResizable(True)
|
||||
|
||||
footer = QtWidgets.QWidget(self)
|
||||
|
||||
save_btn = QtWidgets.QPushButton("Save", footer)
|
||||
reset_btn = QtWidgets.QPushButton("Reset", footer)
|
||||
|
||||
footer_layout = QtWidgets.QHBoxLayout(footer)
|
||||
footer_layout.addWidget(reset_btn, 0)
|
||||
footer_layout.addWidget(SpacerWidget(footer), 1)
|
||||
footer_layout.addWidget(save_btn, 0)
|
||||
|
||||
main_layout = QtWidgets.QVBoxLayout(self)
|
||||
main_layout.setContentsMargins(0, 0, 0, 0)
|
||||
main_layout.addWidget(scroll_widget, 1)
|
||||
main_layout.addWidget(footer, 0)
|
||||
|
||||
save_btn.clicked.connect(self._on_save_clicked)
|
||||
reset_btn.clicked.connect(self._on_reset_clicked)
|
||||
|
||||
self.settings_widget = settings_widget
|
||||
self.reset_btn = reset_btn
|
||||
self.save_btn = save_btn
|
||||
|
||||
self.reset()
|
||||
|
||||
def reset(self):
|
||||
value = get_local_settings()
|
||||
self.settings_widget.update_local_settings(value)
|
||||
|
||||
def _on_reset_clicked(self):
|
||||
self.reset()
|
||||
|
||||
def _on_save_clicked(self):
|
||||
value = self.settings_widget.settings_value()
|
||||
save_local_settings(value)
|
||||
self.reset()
|
||||
|
|
@ -664,9 +664,8 @@ class ProjectListWidget(QtWidgets.QWidget):
|
|||
self.current_project = None
|
||||
|
||||
if self.dbcon:
|
||||
for project_doc in tuple(self.dbcon.projects()):
|
||||
items.append(project_doc["name"])
|
||||
|
||||
for project_name in self.dbcon.database.collection_names():
|
||||
items.append(project_name)
|
||||
for item in items:
|
||||
model.appendRow(QtGui.QStandardItem(item))
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue