mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
Deadline Pype 3 - merge develop
This commit is contained in:
commit
4a07b9a4fa
64 changed files with 4281 additions and 1169 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"
|
||||
]
|
||||
|
|
|
|||
21
pype/cli.py
21
pype/cli.py
|
|
@ -108,6 +108,27 @@ def eventserver(debug,
|
|||
)
|
||||
|
||||
|
||||
@main.command()
|
||||
@click.argument("output_json_path")
|
||||
@click.option("--project", help="Project name", default=None)
|
||||
@click.option("--asset", help="Asset name", default=None)
|
||||
@click.option("--task", help="Task name", default=None)
|
||||
@click.option("--app", help="Application name", default=None)
|
||||
def extractenvironments(output_json_path, project, asset, task, app):
|
||||
"""Extract environment variables for entered context to a json file.
|
||||
|
||||
Entered output filepath will be created if does not exists.
|
||||
|
||||
All context options must be passed otherwise only pype's global
|
||||
environments will be extracted.
|
||||
|
||||
Context options are "project", "asset", "task", "app"
|
||||
"""
|
||||
PypeCommands.extractenvironments(
|
||||
output_json_path, project, asset, task, app
|
||||
)
|
||||
|
||||
|
||||
@main.command()
|
||||
@click.argument("paths", nargs=-1)
|
||||
@click.option("-d", "--debug", is_flag=True, help="Print debug messages")
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
"""Collect palettes from Harmony."""
|
||||
import os
|
||||
import json
|
||||
import re
|
||||
|
||||
import pyblish.api
|
||||
from avalon import harmony
|
||||
|
|
@ -13,7 +14,9 @@ class CollectPalettes(pyblish.api.ContextPlugin):
|
|||
label = "Palettes"
|
||||
order = pyblish.api.CollectorOrder + 0.003
|
||||
hosts = ["harmony"]
|
||||
allowed_tasks = [] # list of task names where collecting should happen
|
||||
|
||||
# list of regexes for task names where collecting should happen
|
||||
allowed_tasks = []
|
||||
|
||||
def process(self, context):
|
||||
"""Collector entry point."""
|
||||
|
|
@ -25,9 +28,9 @@ class CollectPalettes(pyblish.api.ContextPlugin):
|
|||
|
||||
# skip collecting if not in allowed task
|
||||
if self.allowed_tasks:
|
||||
self.allowed_tasks = [task.lower() for task in self.allowed_tasks]
|
||||
if context.data["anatomyData"]["task"].lower() \
|
||||
not in self.allowed_tasks:
|
||||
task_name = context.data["anatomyData"]["task"].lower()
|
||||
if (not any([re.search(pattern, task_name)
|
||||
for pattern in self.allowed_tasks])):
|
||||
return
|
||||
|
||||
for name, id in palettes.items():
|
||||
|
|
|
|||
334
pype/hosts/maya/api/attributes.py
Normal file
334
pype/hosts/maya/api/attributes.py
Normal file
|
|
@ -0,0 +1,334 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Code to get attributes from render layer without switching to it.
|
||||
|
||||
https://github.com/Colorbleed/colorbleed-config/blob/acre/colorbleed/maya/lib_rendersetup.py
|
||||
Credits: Roy Nieterau (BigRoy) / Colorbleed
|
||||
Modified for use in Pype
|
||||
|
||||
"""
|
||||
|
||||
from maya import cmds
|
||||
import maya.api.OpenMaya as om
|
||||
import pymel.core as pm
|
||||
|
||||
import maya.app.renderSetup.model.utils as utils
|
||||
from maya.app.renderSetup.model import renderSetup
|
||||
from maya.app.renderSetup.model.override import (
|
||||
AbsOverride,
|
||||
RelOverride,
|
||||
UniqueOverride
|
||||
)
|
||||
|
||||
EXACT_MATCH = 0
|
||||
PARENT_MATCH = 1
|
||||
CLIENT_MATCH = 2
|
||||
|
||||
DEFAULT_RENDER_LAYER = "defaultRenderLayer"
|
||||
|
||||
|
||||
def get_rendersetup_layer(layer):
|
||||
"""Return render setup layer name.
|
||||
|
||||
This also converts names from legacy renderLayer node name to render setup
|
||||
name.
|
||||
|
||||
Note: `DEFAULT_RENDER_LAYER` is not a renderSetupLayer node but it is
|
||||
however the valid layer name for Render Setup - so we return that as
|
||||
is.
|
||||
|
||||
Example:
|
||||
>>> for legacy_layer in cmds.ls(type="renderLayer"):
|
||||
>>> layer = get_rendersetup_layer(legacy_layer)
|
||||
|
||||
Returns:
|
||||
str or None: Returns renderSetupLayer node name if `layer` is a valid
|
||||
layer name in legacy renderlayers or render setup layers.
|
||||
Returns None if the layer can't be found or Render Setup is
|
||||
currently disabled.
|
||||
"""
|
||||
if layer == DEFAULT_RENDER_LAYER:
|
||||
# DEFAULT_RENDER_LAYER doesn't have a `renderSetupLayer`
|
||||
return layer
|
||||
|
||||
if not cmds.mayaHasRenderSetup():
|
||||
return None
|
||||
|
||||
if not cmds.objExists(layer):
|
||||
return None
|
||||
|
||||
if cmds.nodeType(layer) == "renderSetupLayer":
|
||||
return layer
|
||||
|
||||
# By default Render Setup renames the legacy renderlayer
|
||||
# to `rs_<layername>` but lets not rely on that as the
|
||||
# layer node can be renamed manually
|
||||
connections = cmds.listConnections(layer + ".message",
|
||||
type="renderSetupLayer",
|
||||
exactType=True,
|
||||
source=False,
|
||||
destination=True,
|
||||
plugs=True) or []
|
||||
return next((conn.split(".", 1)[0] for conn in connections
|
||||
if conn.endswith(".legacyRenderLayer")), None)
|
||||
|
||||
|
||||
def get_attr_in_layer(node_attr, layer):
|
||||
"""Return attribute value in Render Setup layer.
|
||||
|
||||
This will only work for attributes which can be
|
||||
retrieved with `maya.cmds.getAttr` and for which
|
||||
Relative and Absolute overrides are applicable.
|
||||
|
||||
Examples:
|
||||
>>> get_attr_in_layer("defaultResolution.width", layer="layer1")
|
||||
>>> get_attr_in_layer("defaultRenderGlobals.startFrame", layer="layer")
|
||||
>>> get_attr_in_layer("transform.translate", layer="layer3")
|
||||
|
||||
Args:
|
||||
attr (str): attribute name as 'node.attribute'
|
||||
layer (str): layer name
|
||||
|
||||
Returns:
|
||||
object: attribute value in layer
|
||||
|
||||
"""
|
||||
def _layer_needs_update(layer):
|
||||
"""Return whether layer needs updating."""
|
||||
# Use `getattr` as e.g. DEFAULT_RENDER_LAYER does not have
|
||||
# the attribute
|
||||
return getattr(layer, "needsMembershipUpdate", False) or \
|
||||
getattr(layer, "needsApplyUpdate", False)
|
||||
|
||||
def get_default_layer_value(node_attr_):
|
||||
"""Return attribute value in `DEFAULT_RENDER_LAYER`."""
|
||||
inputs = cmds.listConnections(node_attr_,
|
||||
source=True,
|
||||
destination=False,
|
||||
type="applyOverride") or []
|
||||
if inputs:
|
||||
override = inputs[0]
|
||||
history_overrides = cmds.ls(cmds.listHistory(override,
|
||||
pruneDagObjects=True),
|
||||
type="applyOverride")
|
||||
node = history_overrides[-1] if history_overrides else override
|
||||
node_attr_ = node + ".original"
|
||||
|
||||
return pm.getAttr(node_attr_, asString=True)
|
||||
|
||||
layer = get_rendersetup_layer(layer)
|
||||
rs = renderSetup.instance()
|
||||
current_layer = rs.getVisibleRenderLayer()
|
||||
if current_layer.name() == layer:
|
||||
|
||||
# Ensure layer is up-to-date
|
||||
if _layer_needs_update(current_layer):
|
||||
try:
|
||||
rs.switchToLayer(current_layer)
|
||||
except RuntimeError:
|
||||
# Some cases can cause errors on switching
|
||||
# the first time with Render Setup layers
|
||||
# e.g. different overrides to compounds
|
||||
# and its children plugs. So we just force
|
||||
# it another time. If it then still fails
|
||||
# we will let it error out.
|
||||
rs.switchToLayer(current_layer)
|
||||
|
||||
return pm.getAttr(node_attr, asString=True)
|
||||
|
||||
overrides = get_attr_overrides(node_attr, layer)
|
||||
default_layer_value = get_default_layer_value(node_attr)
|
||||
if not overrides:
|
||||
return default_layer_value
|
||||
|
||||
value = default_layer_value
|
||||
for match, layer_override, index in overrides:
|
||||
if isinstance(layer_override, AbsOverride):
|
||||
# Absolute override
|
||||
value = pm.getAttr(layer_override.name() + ".attrValue")
|
||||
if match == EXACT_MATCH:
|
||||
# value = value
|
||||
pass
|
||||
if match == PARENT_MATCH:
|
||||
value = value[index]
|
||||
if match == CLIENT_MATCH:
|
||||
value[index] = value
|
||||
|
||||
elif isinstance(layer_override, RelOverride):
|
||||
# Relative override
|
||||
# Value = Original * Multiply + Offset
|
||||
multiply = pm.getAttr(layer_override.name() + ".multiply")
|
||||
offset = pm.getAttr(layer_override.name() + ".offset")
|
||||
|
||||
if match == EXACT_MATCH:
|
||||
value = value * multiply + offset
|
||||
if match == PARENT_MATCH:
|
||||
value = value * multiply[index] + offset[index]
|
||||
if match == CLIENT_MATCH:
|
||||
value[index] = value[index] * multiply + offset
|
||||
|
||||
else:
|
||||
raise TypeError("Unsupported override: %s" % layer_override)
|
||||
|
||||
return value
|
||||
|
||||
|
||||
def get_attr_overrides(node_attr, layer,
|
||||
skip_disabled=True,
|
||||
skip_local_render=True,
|
||||
stop_at_absolute_override=True):
|
||||
"""Return all Overrides applicable to the attribute.
|
||||
|
||||
Overrides are returned as a 3-tuple:
|
||||
(Match, Override, Index)
|
||||
Match:
|
||||
This is any of EXACT_MATCH, PARENT_MATCH, CLIENT_MATCH
|
||||
and defines whether the override is exactly on the
|
||||
plug, on the parent or on a child plug.
|
||||
Override:
|
||||
This is the RenderSetup Override instance.
|
||||
Index:
|
||||
This is the Plug index under the parent or for
|
||||
the child that matches. The EXACT_MATCH index will
|
||||
always be None. For PARENT_MATCH the index is which
|
||||
index the plug is under the parent plug. For CLIENT_MATCH
|
||||
the index is which child index matches the plug.
|
||||
|
||||
Args:
|
||||
node_attr (str): attribute name as 'node.attribute'
|
||||
layer (str): layer name
|
||||
skip_disabled (bool): exclude disabled overrides
|
||||
skip_local_render (bool): exclude overrides marked
|
||||
as local render.
|
||||
stop_at_absolute_override: exclude overrides prior
|
||||
to the last absolute override as they have
|
||||
no influence on the resulting value.
|
||||
|
||||
Returns:
|
||||
list: Ordered Overrides in order of strength
|
||||
|
||||
"""
|
||||
def get_mplug_children(plug):
|
||||
"""Return children MPlugs of compound `MPlug`."""
|
||||
children = []
|
||||
if plug.isCompound:
|
||||
for i in range(plug.numChildren()):
|
||||
children.append(plug.child(i))
|
||||
return children
|
||||
|
||||
def get_mplug_names(mplug):
|
||||
"""Return long and short name of `MPlug`."""
|
||||
long_name = mplug.partialName(useLongNames=True)
|
||||
short_name = mplug.partialName(useLongNames=False)
|
||||
return {long_name, short_name}
|
||||
|
||||
def iter_override_targets(override):
|
||||
try:
|
||||
for target in override._targets():
|
||||
yield target
|
||||
except AssertionError:
|
||||
# Workaround: There is a bug where the private `_targets()` method
|
||||
# fails on some attribute plugs. For example overrides
|
||||
# to the defaultRenderGlobals.endFrame
|
||||
# (Tested in Maya 2020.2)
|
||||
print("Workaround for %s" % override)
|
||||
from maya.app.renderSetup.common.utils import findPlug
|
||||
|
||||
attr = override.attributeName()
|
||||
if isinstance(override, UniqueOverride):
|
||||
node = override.targetNodeName()
|
||||
yield findPlug(node, attr)
|
||||
else:
|
||||
nodes = override.parent().selector().nodes()
|
||||
for node in nodes:
|
||||
if cmds.attributeQuery(attr, node=node, exists=True):
|
||||
yield findPlug(node, attr)
|
||||
|
||||
# Get the MPlug for the node.attr
|
||||
sel = om.MSelectionList()
|
||||
sel.add(node_attr)
|
||||
plug = sel.getPlug(0)
|
||||
|
||||
layer = get_rendersetup_layer(layer)
|
||||
if layer == DEFAULT_RENDER_LAYER:
|
||||
# DEFAULT_RENDER_LAYER will never have overrides
|
||||
# since it's the default layer
|
||||
return []
|
||||
|
||||
rs_layer = renderSetup.instance().getRenderLayer(layer)
|
||||
if rs_layer is None:
|
||||
# Renderlayer does not exist
|
||||
return
|
||||
|
||||
# Get any parent or children plugs as we also
|
||||
# want to include them in the attribute match
|
||||
# for overrides
|
||||
parent = plug.parent() if plug.isChild else None
|
||||
parent_index = None
|
||||
if parent:
|
||||
parent_index = get_mplug_children(parent).index(plug)
|
||||
|
||||
children = get_mplug_children(plug)
|
||||
|
||||
# Create lookup for the attribute by both long
|
||||
# and short names
|
||||
attr_names = get_mplug_names(plug)
|
||||
for child in children:
|
||||
attr_names.update(get_mplug_names(child))
|
||||
if parent:
|
||||
attr_names.update(get_mplug_names(parent))
|
||||
|
||||
# Get all overrides of the layer
|
||||
# And find those that are relevant to the attribute
|
||||
plug_overrides = []
|
||||
|
||||
# Iterate over the overrides in reverse so we get the last
|
||||
# overrides first and can "break" whenever an absolute
|
||||
# override is reached
|
||||
layer_overrides = list(utils.getOverridesRecursive(rs_layer))
|
||||
for layer_override in reversed(layer_overrides):
|
||||
|
||||
if skip_disabled and not layer_override.isEnabled():
|
||||
# Ignore disabled overrides
|
||||
continue
|
||||
|
||||
if skip_local_render and layer_override.isLocalRender():
|
||||
continue
|
||||
|
||||
# The targets list can be very large so we'll do
|
||||
# a quick filter by attribute name to detect whether
|
||||
# it matches the attribute name, or its parent or child
|
||||
if layer_override.attributeName() not in attr_names:
|
||||
continue
|
||||
|
||||
override_match = None
|
||||
for override_plug in iter_override_targets(layer_override):
|
||||
|
||||
override_match = None
|
||||
if plug == override_plug:
|
||||
override_match = (EXACT_MATCH, layer_override, None)
|
||||
|
||||
elif parent and override_plug == parent:
|
||||
override_match = (PARENT_MATCH, layer_override, parent_index)
|
||||
|
||||
elif children and override_plug in children:
|
||||
child_index = children.index(override_plug)
|
||||
override_match = (CLIENT_MATCH, layer_override, child_index)
|
||||
|
||||
if override_match:
|
||||
plug_overrides.append(override_match)
|
||||
break
|
||||
|
||||
if (
|
||||
override_match and
|
||||
stop_at_absolute_override and
|
||||
isinstance(layer_override, AbsOverride) and
|
||||
# When the override is only on a child plug then it doesn't
|
||||
# override the entire value so we not stop at this override
|
||||
not override_match[0] == CLIENT_MATCH
|
||||
):
|
||||
# If override is absolute override, then BREAK out
|
||||
# of parent loop we don't need to look any further as
|
||||
# this is the absolute override
|
||||
break
|
||||
|
||||
return reversed(plug_overrides)
|
||||
|
|
@ -2,7 +2,7 @@ import pyblish.api
|
|||
|
||||
from maya import cmds
|
||||
|
||||
from pype.hosts.maya.api import lib
|
||||
from pype.hosts.maya.api.attributes import get_attr_in_layer
|
||||
|
||||
|
||||
class CollectRenderableCamera(pyblish.api.InstancePlugin):
|
||||
|
|
@ -24,7 +24,7 @@ class CollectRenderableCamera(pyblish.api.InstancePlugin):
|
|||
self.log.info("layer: {}".format(layer))
|
||||
cameras = cmds.ls(type="camera", long=True)
|
||||
renderable = [c for c in cameras if
|
||||
lib.get_attr_in_layer("%s.renderable" % c, layer=layer)]
|
||||
get_attr_in_layer("%s.renderable" % c, layer)]
|
||||
|
||||
self.log.info("Found cameras %s: %s" % (len(renderable), renderable))
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ class ValidateModelName(pyblish.api.InstancePlugin):
|
|||
# path to shader names definitions
|
||||
# TODO: move it to preset file
|
||||
material_file = None
|
||||
active = False
|
||||
regex = '(.*)_(\\d)*_(.*)_(GEO)'
|
||||
|
||||
@classmethod
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import pype.hosts.maya.api.action
|
|||
from pype.hosts.maya.api import lib
|
||||
|
||||
|
||||
class ValidateJointsHidden(pyblish.api.InstancePlugin):
|
||||
class ValidateRigJointsHidden(pyblish.api.InstancePlugin):
|
||||
"""Validate all joints are hidden visually.
|
||||
|
||||
This includes being hidden:
|
||||
|
|
@ -20,7 +20,6 @@ class ValidateJointsHidden(pyblish.api.InstancePlugin):
|
|||
order = pype.api.ValidateContentsOrder
|
||||
hosts = ['maya']
|
||||
families = ['rig']
|
||||
category = 'rig'
|
||||
version = (0, 1, 0)
|
||||
label = "Joints Hidden"
|
||||
actions = [pype.hosts.maya.api.action.SelectInvalidAction,
|
||||
|
|
@ -13,7 +13,6 @@ class ValidateShaderName(pyblish.api.InstancePlugin):
|
|||
|
||||
"""
|
||||
optional = True
|
||||
active = False
|
||||
order = pype.api.ValidateContentsOrder
|
||||
families = ["look"]
|
||||
hosts = ['maya']
|
||||
|
|
|
|||
|
|
@ -35,11 +35,11 @@ class ValidateTransformNamingSuffix(pyblish.api.InstancePlugin):
|
|||
version = (0, 1, 0)
|
||||
label = 'Suffix Naming Conventions'
|
||||
actions = [pype.hosts.maya.api.action.SelectInvalidAction]
|
||||
SUFFIX_NAMING_TABLE = {'mesh': ["_GEO", "_GES", "_GEP", "_OSD"],
|
||||
'nurbsCurve': ["_CRV"],
|
||||
'nurbsSurface': ["_NRB"],
|
||||
'locator': ["_LOC"],
|
||||
None: ['_GRP']}
|
||||
SUFFIX_NAMING_TABLE = {"mesh": ["_GEO", "_GES", "_GEP", "_OSD"],
|
||||
"nurbsCurve": ["_CRV"],
|
||||
"nurbsSurface": ["_NRB"],
|
||||
"locator": ["_LOC"],
|
||||
"group": ["_GRP"]}
|
||||
|
||||
ALLOW_IF_NOT_IN_SUFFIX_TABLE = True
|
||||
|
||||
|
|
@ -88,7 +88,7 @@ class ValidateTransformNamingSuffix(pyblish.api.InstancePlugin):
|
|||
fullPath=True,
|
||||
noIntermediate=True)
|
||||
|
||||
shape_type = cmds.nodeType(shapes[0]) if shapes else None
|
||||
shape_type = cmds.nodeType(shapes[0]) if shapes else "group"
|
||||
if not cls.is_valid_name(transform, shape_type,
|
||||
cls.SUFFIX_NAMING_TABLE,
|
||||
cls.ALLOW_IF_NOT_IN_SUFFIX_TABLE):
|
||||
|
|
|
|||
|
|
@ -1,40 +0,0 @@
|
|||
import os
|
||||
from maya import cmds
|
||||
|
||||
import pyblish.api
|
||||
import pype.api
|
||||
import pype.hosts.maya.api.action
|
||||
|
||||
|
||||
class ValidateUnicodeStrings(pyblish.api.Validator):
|
||||
"""Validate all environment variables are string type.
|
||||
|
||||
"""
|
||||
|
||||
order = pype.api.ValidateContentsOrder
|
||||
hosts = ['maya']
|
||||
families = ['review']
|
||||
label = 'Unicode Strings'
|
||||
actions = [pype.api.RepairAction]
|
||||
|
||||
def process(self, instance):
|
||||
invalid = self.get_invalid(instance)
|
||||
if invalid:
|
||||
raise RuntimeError("Found unicode strings in environment variables.")
|
||||
|
||||
@classmethod
|
||||
def get_invalid(cls, instance):
|
||||
invalid = []
|
||||
for key, value in os.environ.items():
|
||||
if type(value) is type(u't'):
|
||||
invalid.append((key, value))
|
||||
|
||||
return invalid
|
||||
|
||||
@classmethod
|
||||
def repair(cls, instance):
|
||||
"""Retype all unicodes to strings."""
|
||||
|
||||
for key, value in os.environ.items():
|
||||
if type(value) is type(u't'):
|
||||
os.environ[key] = str(value)
|
||||
|
|
@ -9,7 +9,7 @@ class ValidateKnobs(pyblish.api.ContextPlugin):
|
|||
|
||||
Knobs to validate and their values comes from the
|
||||
|
||||
Controled by plugin settings that require json in following structure:
|
||||
Controlled by plugin settings that require json in following structure:
|
||||
"ValidateKnobs": {
|
||||
"enabled": true,
|
||||
"knobs": {
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ def run_subprocess(*args, **kwargs):
|
|||
# not passed.
|
||||
env = kwargs.get("env") or os.environ
|
||||
# Make sure environment contains only strings
|
||||
filtered_env = {k: str(v) for k, v in env.items()}
|
||||
filtered_env = {str(k): str(v) for k, v in env.items()}
|
||||
|
||||
# Use lib's logger if was not passed with kwargs.
|
||||
logger = kwargs.pop("logger", log)
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -23,12 +23,12 @@ class NukeSubmitDeadline(pyblish.api.InstancePlugin):
|
|||
optional = True
|
||||
|
||||
# presets
|
||||
deadline_priority = 50
|
||||
deadline_chunk_size = 1
|
||||
deadline_pool = ""
|
||||
deadline_pool_secondary = ""
|
||||
deadline_group = ""
|
||||
deadline_department = ""
|
||||
priority = 50
|
||||
chunk_size = 1
|
||||
primary_pool = ""
|
||||
secondary_pool = ""
|
||||
group = ""
|
||||
department = ""
|
||||
|
||||
def process(self, instance):
|
||||
instance.data["toBeRenderedOn"] = "deadline"
|
||||
|
|
@ -142,12 +142,12 @@ class NukeSubmitDeadline(pyblish.api.InstancePlugin):
|
|||
|
||||
# define chunk and priority
|
||||
chunk_size = instance.data.get("deadlineChunkSize")
|
||||
if chunk_size == 0 and self.deadline_chunk_size:
|
||||
chunk_size = self.deadline_chunk_size
|
||||
if chunk_size == 0 and self.chunk_size:
|
||||
chunk_size = self.chunk_size
|
||||
|
||||
priority = instance.data.get("deadlinePriority")
|
||||
if not priority:
|
||||
priority = self.deadline_priority
|
||||
priority = self.priority
|
||||
|
||||
payload = {
|
||||
"JobInfo": {
|
||||
|
|
@ -165,11 +165,11 @@ class NukeSubmitDeadline(pyblish.api.InstancePlugin):
|
|||
|
||||
"Priority": priority,
|
||||
"ChunkSize": chunk_size,
|
||||
"Department": self.deadline_department,
|
||||
"Department": self.department,
|
||||
|
||||
"Pool": self.deadline_pool,
|
||||
"SecondaryPool": self.deadline_pool_secondary,
|
||||
"Group": self.deadline_group,
|
||||
"Pool": self.primary_pool,
|
||||
"SecondaryPool": self.secondary_pool,
|
||||
"Group": self.group,
|
||||
|
||||
"Plugin": "Nuke",
|
||||
"Frames": "{start}-{end}".format(
|
||||
|
|
|
|||
|
|
@ -1677,15 +1677,15 @@ class SyncToAvalonEvent(BaseEvent):
|
|||
self.updates[mongo_id]["data"] = {}
|
||||
|
||||
vis_par_id = None
|
||||
ent_path_items = [self.cur_project["full_name"]]
|
||||
if par_av_ent["type"].lower() != "project":
|
||||
vis_par_id = par_av_ent["_id"]
|
||||
ent_path_items.extend(par_av_ent["data"]["parents"])
|
||||
ent_path_items.append(par_av_ent["name"])
|
||||
|
||||
self.updates[mongo_id]["data"]["visualParent"] = vis_par_id
|
||||
self.moved_in_avalon.append(mongo_id)
|
||||
|
||||
# TODO logging
|
||||
ent_path_items = [self.cur_project["full_name"]]
|
||||
ent_path_items.extend(par_av_ent["data"]["parents"])
|
||||
ent_path_items.append(par_av_ent["name"])
|
||||
ent_path_items.append(avalon_ent["name"])
|
||||
ent_path = "/".join(ent_path_items)
|
||||
self.log.debug((
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -123,8 +123,7 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin):
|
|||
self.log.debug("Qeurying latest versions for instances.")
|
||||
|
||||
hierarchy = {}
|
||||
subset_names = set()
|
||||
asset_ids = set()
|
||||
subset_filters = []
|
||||
for instance in context:
|
||||
# Make sure `"latestVersion"` key is set
|
||||
latest_version = instance.data.get("latestVersion")
|
||||
|
|
@ -138,8 +137,6 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin):
|
|||
# Store asset ids and subset names for queries
|
||||
asset_id = asset_doc["_id"]
|
||||
subset_name = instance.data["subset"]
|
||||
asset_ids.add(asset_id)
|
||||
subset_names.add(subset_name)
|
||||
|
||||
# Prepare instance hiearchy for faster filling latest versions
|
||||
if asset_id not in hierarchy:
|
||||
|
|
@ -147,11 +144,14 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin):
|
|||
if subset_name not in hierarchy[asset_id]:
|
||||
hierarchy[asset_id][subset_name] = []
|
||||
hierarchy[asset_id][subset_name].append(instance)
|
||||
subset_filters.append({
|
||||
"parent": asset_id,
|
||||
"name": subset_name
|
||||
})
|
||||
|
||||
subset_docs = list(io.find({
|
||||
"type": "subset",
|
||||
"parent": {"$in": list(asset_ids)},
|
||||
"name": {"$in": list(subset_names)}
|
||||
"$or": subset_filters
|
||||
}))
|
||||
|
||||
subset_ids = [
|
||||
|
|
|
|||
15
pype/plugins/publish/repair_unicode_strings.py
Normal file
15
pype/plugins/publish/repair_unicode_strings.py
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import os
|
||||
import pyblish.api
|
||||
|
||||
|
||||
class RepairUnicodeStrings(pyblish.api.Collector):
|
||||
"""Validate all environment variables are string type.
|
||||
|
||||
"""
|
||||
|
||||
order = pyblish.api.CollectorOrder
|
||||
label = 'Unicode Strings'
|
||||
|
||||
def process(self, context):
|
||||
for key, value in os.environ.items():
|
||||
os.environ[str(key)] = str(value)
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
"""Implementation of Pype commands."""
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
from pype.lib import PypeLogger
|
||||
|
|
@ -85,6 +86,21 @@ class PypeCommands:
|
|||
log.info("Publish finished.")
|
||||
uninstall()
|
||||
|
||||
def extractenvironments(output_json_path, project, asset, task, app):
|
||||
env = os.environ.copy()
|
||||
if all((project, asset, task, app)):
|
||||
from pype.api import get_app_environments_for_context
|
||||
env = get_app_environments_for_context(
|
||||
project, asset, task, app, env
|
||||
)
|
||||
|
||||
output_dir = os.path.dirname(output_json_path)
|
||||
if not os.path.exists(output_dir):
|
||||
os.makedirs(output_dir)
|
||||
|
||||
with open(output_json_path, "w") as file_stream:
|
||||
json.dump(env, file_stream, indent=4)
|
||||
|
||||
def texture_copy(self, project, asset, path):
|
||||
pass
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
)
|
||||
|
|
|
|||
44
pype/settings/defaults/project_settings/deadline.json
Normal file
44
pype/settings/defaults/project_settings/deadline.json
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
{
|
||||
"publish": {
|
||||
"MayaSubmitDeadline": {
|
||||
"enabled": true,
|
||||
"optional": false,
|
||||
"tile_assembler_plugin": "oiio",
|
||||
"use_published": true,
|
||||
"asset_dependencies": true
|
||||
},
|
||||
"NukeSubmitDeadline": {
|
||||
"enabled": true,
|
||||
"optional": false,
|
||||
"use_published": true,
|
||||
"priority": 50,
|
||||
"Chunk Size": 10,
|
||||
"primary_pool": "",
|
||||
"secondary_pool": "",
|
||||
"group": "",
|
||||
"department": ""
|
||||
},
|
||||
"HarmonySubmitDeadline": {
|
||||
"enabled": true,
|
||||
"optional": false,
|
||||
"use_published": true,
|
||||
"priority": 50,
|
||||
"Chunk Size": 10000,
|
||||
"primary_pool": "",
|
||||
"secondary_pool": "",
|
||||
"group": "",
|
||||
"department": ""
|
||||
},
|
||||
"AfterEffectsSubmitDeadline": {
|
||||
"enabled": true,
|
||||
"optional": false,
|
||||
"use_published": true,
|
||||
"priority": 50,
|
||||
"Chunk Size": 10000,
|
||||
"primary_pool": "",
|
||||
"secondary_pool": "",
|
||||
"group": "",
|
||||
"department": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,20 @@
|
|||
{
|
||||
"publish": {},
|
||||
"general": {
|
||||
"skip_resolution_check": [],
|
||||
"skip_timelines_check": []
|
||||
},
|
||||
"publish": {
|
||||
"CollectPalettes": {
|
||||
"allowed_tasks": [
|
||||
"."
|
||||
]
|
||||
},
|
||||
"HarmonySubmitDeadline": {
|
||||
"use_published": false,
|
||||
"priority": 50,
|
||||
"primary_pool": "",
|
||||
"secondary_pool": "",
|
||||
"chunk_size": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -107,37 +107,249 @@
|
|||
"overscan": 1.0
|
||||
}
|
||||
},
|
||||
"ext_mapping": {},
|
||||
"create": {
|
||||
"CreateAnimation": {
|
||||
"enabled": true,
|
||||
"defaults": [
|
||||
"Main"
|
||||
]
|
||||
},
|
||||
"CreateAss": {
|
||||
"enabled": true,
|
||||
"defaults": [
|
||||
"Main"
|
||||
]
|
||||
},
|
||||
"CreateAssembly": {
|
||||
"enabled": true,
|
||||
"defaults": [
|
||||
"Main"
|
||||
]
|
||||
},
|
||||
"CreateCamera": {
|
||||
"enabled": true,
|
||||
"defaults": [
|
||||
"Main"
|
||||
]
|
||||
},
|
||||
"CreateLayout": {
|
||||
"enabled": true,
|
||||
"defaults": [
|
||||
"Main"
|
||||
]
|
||||
},
|
||||
"CreateLook": {
|
||||
"enabled": true,
|
||||
"defaults": [
|
||||
"Main"
|
||||
]
|
||||
},
|
||||
"CreateMayaScene": {
|
||||
"enabled": true,
|
||||
"defaults": [
|
||||
"Main"
|
||||
]
|
||||
},
|
||||
"CreateModel": {
|
||||
"enabled": true,
|
||||
"defaults": [
|
||||
"Main",
|
||||
"Proxy",
|
||||
"Sculpt"
|
||||
]
|
||||
},
|
||||
"CreatePointCache": {
|
||||
"enabled": true,
|
||||
"defaults": [
|
||||
"Main"
|
||||
]
|
||||
},
|
||||
"CreateRender": {
|
||||
"enabled": true,
|
||||
"defaults": [
|
||||
"Main"
|
||||
]
|
||||
},
|
||||
"CreateRenderSetup": {
|
||||
"enabled": true,
|
||||
"defaults": [
|
||||
"Main"
|
||||
]
|
||||
},
|
||||
"CreateReview": {
|
||||
"enabled": true,
|
||||
"defaults": [
|
||||
"Main"
|
||||
]
|
||||
},
|
||||
"CreateRig": {
|
||||
"enabled": true,
|
||||
"defaults": [
|
||||
"Main",
|
||||
"Sim",
|
||||
"Cloth"
|
||||
]
|
||||
},
|
||||
"CreateSetDress": {
|
||||
"enabled": true,
|
||||
"defaults": [
|
||||
"Main",
|
||||
"Anim"
|
||||
]
|
||||
},
|
||||
"CreateUnrealStaticMesh": {
|
||||
"enabled": true,
|
||||
"defaults": [
|
||||
"Main"
|
||||
]
|
||||
},
|
||||
"CreateVrayProxy": {
|
||||
"enabled": true,
|
||||
"defaults": [
|
||||
"Main"
|
||||
]
|
||||
},
|
||||
"CreateVRayScene": {
|
||||
"enabled": true,
|
||||
"defaults": [
|
||||
"Main"
|
||||
]
|
||||
},
|
||||
"CreateYetiRig": {
|
||||
"enabled": true,
|
||||
"defaults": [
|
||||
"Main"
|
||||
]
|
||||
}
|
||||
},
|
||||
"publish": {
|
||||
"CollectMayaRender": {
|
||||
"sync_workfile_version": false
|
||||
},
|
||||
"ValidateCameraAttributes": {
|
||||
"ValidateShaderName": {
|
||||
"enabled": false,
|
||||
"optional": true
|
||||
"regex": "(?P<asset>.*)_(.*)_SHD"
|
||||
},
|
||||
"ValidateAttributes": {
|
||||
"enabled": false,
|
||||
"attributes": {}
|
||||
},
|
||||
"ValidateModelName": {
|
||||
"enabled": true,
|
||||
"enabled": false,
|
||||
"material_file": {
|
||||
"windows": "",
|
||||
"darwin": "",
|
||||
"linux": ""
|
||||
},
|
||||
"regex": ""
|
||||
"regex": "(.*)_(\\\\d)*_(.*)_(GEO)"
|
||||
},
|
||||
"ValidateAssemblyName": {
|
||||
"enabled": true
|
||||
},
|
||||
"ValidateShaderName": {
|
||||
"ValidateTransformNamingSuffix": {
|
||||
"enabled": true,
|
||||
"regex": ""
|
||||
"SUFFIX_NAMING_TABLE": {
|
||||
"mesh": [
|
||||
"_GEO",
|
||||
"_GES",
|
||||
"_GEP",
|
||||
"_OSD"
|
||||
],
|
||||
"nurbsCurve": [
|
||||
"_CRV"
|
||||
],
|
||||
"nurbsSurface": [
|
||||
"_NRB"
|
||||
],
|
||||
"locator": [
|
||||
"_LOC"
|
||||
],
|
||||
"group": [
|
||||
"_GRP"
|
||||
]
|
||||
},
|
||||
"ALLOW_IF_NOT_IN_SUFFIX_TABLE": true
|
||||
},
|
||||
"ValidateColorSets": {
|
||||
"enabled": false,
|
||||
"optional": true
|
||||
},
|
||||
"ValidateMeshHasOverlappingUVs": {
|
||||
"enabled": false
|
||||
},
|
||||
"ValidateAttributes": {
|
||||
"enabled": false,
|
||||
"attributes": {}
|
||||
"optional": true
|
||||
},
|
||||
"ValidateMeshArnoldAttributes": {
|
||||
"enabled": false,
|
||||
"optional": true
|
||||
},
|
||||
"ValidateMeshShaderConnections": {
|
||||
"enabled": true,
|
||||
"optional": true
|
||||
},
|
||||
"ValidateMeshSingleUVSet": {
|
||||
"enabled": false,
|
||||
"optional": true
|
||||
},
|
||||
"ValidateMeshHasUVs": {
|
||||
"enabled": true,
|
||||
"optional": true
|
||||
},
|
||||
"ValidateMeshLaminaFaces": {
|
||||
"enabled": false,
|
||||
"optional": true
|
||||
},
|
||||
"ValidateMeshNonManifold": {
|
||||
"enabled": false,
|
||||
"optional": true
|
||||
},
|
||||
"ValidateMeshUVSetMap1": {
|
||||
"enabled": false,
|
||||
"optional": true
|
||||
},
|
||||
"ValidateMeshVerticesHaveEdges": {
|
||||
"enabled": true,
|
||||
"optional": true
|
||||
},
|
||||
"ValidateNoAnimation": {
|
||||
"enabled": false,
|
||||
"optional": true
|
||||
},
|
||||
"ValidateNoNamespace": {
|
||||
"enabled": true,
|
||||
"optional": true
|
||||
},
|
||||
"ValidateNoNullTransforms": {
|
||||
"enabled": true,
|
||||
"optional": true
|
||||
},
|
||||
"ValidateNoUnknownNodes": {
|
||||
"enabled": true,
|
||||
"optional": true
|
||||
},
|
||||
"ValidateNodeNoGhosting": {
|
||||
"enabled": false,
|
||||
"optional": true
|
||||
},
|
||||
"ValidateShapeDefaultNames": {
|
||||
"enabled": false,
|
||||
"optional": true
|
||||
},
|
||||
"ValidateShapeRenderStats": {
|
||||
"enabled": false,
|
||||
"optional": true
|
||||
},
|
||||
"ValidateTransformZero": {
|
||||
"enabled": true,
|
||||
"optional": true
|
||||
},
|
||||
"ValidateCameraAttributes": {
|
||||
"enabled": false,
|
||||
"optional": true
|
||||
},
|
||||
"ValidateAssemblyName": {
|
||||
"enabled": true,
|
||||
"optional": true
|
||||
},
|
||||
"ValidateAssRelativePaths": {
|
||||
"enabled": true,
|
||||
"optional": true
|
||||
},
|
||||
"ExtractCameraAlembic": {
|
||||
"enabled": true,
|
||||
|
|
@ -221,6 +433,39 @@
|
|||
0.8,
|
||||
0.5
|
||||
]
|
||||
},
|
||||
"ReferenceLoader": {
|
||||
"enabled": true,
|
||||
"representations": [
|
||||
"ma",
|
||||
"mb",
|
||||
"abc",
|
||||
"fbx"
|
||||
]
|
||||
},
|
||||
"AudioLoader": {
|
||||
"enabled": true,
|
||||
"representations": [
|
||||
"wav"
|
||||
]
|
||||
},
|
||||
"GpuCacheLoader": {
|
||||
"enabled": true,
|
||||
"representations": [
|
||||
"abc"
|
||||
]
|
||||
},
|
||||
"ImagePlaneLoader": {
|
||||
"enabled": true,
|
||||
"representations": [
|
||||
"jpg",
|
||||
"png",
|
||||
"mov"
|
||||
]
|
||||
},
|
||||
"MatchmoveLoader": {
|
||||
"enabled": true,
|
||||
"representations": []
|
||||
}
|
||||
},
|
||||
"workfile_build": {
|
||||
|
|
|
|||
|
|
@ -61,6 +61,22 @@
|
|||
"deadline_pool": "",
|
||||
"deadline_pool_secondary": "",
|
||||
"deadline_chunk_size": 1
|
||||
},
|
||||
"ValidateOutputResolution": {
|
||||
"enabled": true,
|
||||
"optional": true
|
||||
},
|
||||
"ValidateGizmo": {
|
||||
"enabled": true,
|
||||
"optional": true
|
||||
},
|
||||
"ValidateScript": {
|
||||
"enabled": true,
|
||||
"optional": true
|
||||
},
|
||||
"ValidateNukeWriteBoundingBox": {
|
||||
"enabled": true,
|
||||
"optional": true
|
||||
}
|
||||
},
|
||||
"workfile_build": {
|
||||
|
|
|
|||
|
|
@ -695,6 +695,12 @@ class ItemEntity(BaseItemEntity):
|
|||
is_dynamic_item (bool): Entity should behave like dynamically created
|
||||
entity.
|
||||
"""
|
||||
_default_label_wrap = {
|
||||
"use_label_wrap": False,
|
||||
"collapsible": True,
|
||||
"collapsed": False
|
||||
}
|
||||
|
||||
def __init__(self, schema_data, parent, is_dynamic_item=False):
|
||||
super(ItemEntity, self).__init__(schema_data)
|
||||
|
||||
|
|
@ -736,12 +742,50 @@ class ItemEntity(BaseItemEntity):
|
|||
self.key = self.schema_data.get("key")
|
||||
self.label = self.schema_data.get("label")
|
||||
|
||||
# GUI attributes
|
||||
_default_label_wrap = self.__class__._default_label_wrap
|
||||
for key, value in ItemEntity._default_label_wrap.items():
|
||||
if key not in _default_label_wrap:
|
||||
self.log.warning(
|
||||
"Class {} miss default label wrap key \"{}\"".format(
|
||||
self.__class__.__name__, key
|
||||
)
|
||||
)
|
||||
_default_label_wrap[key] = value
|
||||
|
||||
use_label_wrap = self.schema_data.get("use_label_wrap")
|
||||
if use_label_wrap is None:
|
||||
if not self.label:
|
||||
use_label_wrap = False
|
||||
else:
|
||||
use_label_wrap = _default_label_wrap["use_label_wrap"]
|
||||
self.use_label_wrap = use_label_wrap
|
||||
|
||||
# Used only if `use_label_wrap` is set to True
|
||||
self.collapsible = self.schema_data.get(
|
||||
"collapsible",
|
||||
_default_label_wrap["collapsible"]
|
||||
)
|
||||
self.collapsed = self.schema_data.get(
|
||||
"collapsed",
|
||||
_default_label_wrap["collapsed"]
|
||||
)
|
||||
|
||||
self._item_initalization()
|
||||
|
||||
def save(self):
|
||||
"""Call save on root item."""
|
||||
self.root_item.save()
|
||||
|
||||
def schema_validations(self):
|
||||
if not self.label and self.use_label_wrap:
|
||||
raise ValueError((
|
||||
"{} Entity has set `use_label_wrap` to true but"
|
||||
" does not have set `label`."
|
||||
).format(self.path))
|
||||
|
||||
super(ItemEntity, self).schema_validations()
|
||||
|
||||
def create_schema_object(self, *args, **kwargs):
|
||||
"""Reference method for creation of entities defined in RootEntity."""
|
||||
return self.root_item.create_schema_object(*args, **kwargs)
|
||||
|
|
|
|||
|
|
@ -27,6 +27,11 @@ class DictImmutableKeysEntity(ItemEntity):
|
|||
are not real settings values but entities representing the value.
|
||||
"""
|
||||
schema_types = ["dict"]
|
||||
_default_label_wrap = {
|
||||
"use_label_wrap": True,
|
||||
"collapsible": True,
|
||||
"collapsed": True
|
||||
}
|
||||
|
||||
def __getitem__(self, key):
|
||||
"""Return entity inder key."""
|
||||
|
|
@ -130,7 +135,7 @@ class DictImmutableKeysEntity(ItemEntity):
|
|||
continue
|
||||
|
||||
if child_obj.key in self.non_gui_children:
|
||||
raise SchemaDuplicatedKeys(self.path, child_obj.key)
|
||||
raise SchemaDuplicatedKeys("", child_obj.key)
|
||||
self.non_gui_children[child_obj.key] = child_obj
|
||||
|
||||
if not first:
|
||||
|
|
@ -169,11 +174,6 @@ class DictImmutableKeysEntity(ItemEntity):
|
|||
"highlight_content", False
|
||||
)
|
||||
self.show_borders = self.schema_data.get("show_borders", True)
|
||||
self.collapsible = self.schema_data.get("collapsible", True)
|
||||
self.collapsed = self.schema_data.get("collapsed", True)
|
||||
|
||||
# Not yet implemented
|
||||
self.use_label_wrap = self.schema_data.get("use_label_wrap") or True
|
||||
|
||||
def get_child_path(self, child_obj):
|
||||
"""Get hierarchical path of child entity.
|
||||
|
|
|
|||
|
|
@ -29,6 +29,12 @@ class DictMutableKeysEntity(EndpointEntity):
|
|||
- clear callbacks
|
||||
"""
|
||||
schema_types = ["dict-modifiable"]
|
||||
_default_label_wrap = {
|
||||
"use_label_wrap": True,
|
||||
"collapsible": True,
|
||||
"collapsed": True
|
||||
}
|
||||
|
||||
_miss_arg = object()
|
||||
|
||||
def __getitem__(self, key):
|
||||
|
|
@ -174,8 +180,6 @@ class DictMutableKeysEntity(EndpointEntity):
|
|||
self.hightlight_content = (
|
||||
self.schema_data.get("highlight_content") or False
|
||||
)
|
||||
self.collapsible = self.schema_data.get("collapsible", True)
|
||||
self.collapsed = self.schema_data.get("collapsed", True)
|
||||
|
||||
object_type = self.schema_data["object_type"]
|
||||
if not isinstance(object_type, dict):
|
||||
|
|
|
|||
|
|
@ -15,6 +15,11 @@ from .exceptions import (
|
|||
|
||||
class ListEntity(EndpointEntity):
|
||||
schema_types = ["list"]
|
||||
_default_label_wrap = {
|
||||
"use_label_wrap": False,
|
||||
"collapsible": True,
|
||||
"collapsed": False
|
||||
}
|
||||
|
||||
def __iter__(self):
|
||||
for item in self.children:
|
||||
|
|
@ -139,12 +144,6 @@ class ListEntity(EndpointEntity):
|
|||
# Value that was set on set_override_state
|
||||
self.initial_value = []
|
||||
|
||||
# GUI attributes
|
||||
self.use_label_wrap = self.schema_data.get("use_label_wrap") or False
|
||||
# Used only if `use_label_wrap` is set to True
|
||||
self.collapsible = self.schema_data.get("collapsible") or True
|
||||
self.collapsed = self.schema_data.get("collapsed") or False
|
||||
|
||||
def schema_validations(self):
|
||||
super(ListEntity, self).schema_validations()
|
||||
|
||||
|
|
|
|||
|
|
@ -45,6 +45,10 @@
|
|||
"type": "schema",
|
||||
"name": "schema_project_ftrack"
|
||||
},
|
||||
{
|
||||
"type": "schema",
|
||||
"name": "schema_project_deadline"
|
||||
},
|
||||
{
|
||||
"type": "schema",
|
||||
"name": "schema_project_maya"
|
||||
|
|
|
|||
|
|
@ -1,54 +0,0 @@
|
|||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "plugins",
|
||||
"label": "Plugins",
|
||||
"children": [
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "standalonepublisher",
|
||||
"label": "Standalone Publisher",
|
||||
"children": [
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "publish",
|
||||
"label": "Publish plugins",
|
||||
"is_file": true,
|
||||
"children": [
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "ExtractThumbnailSP",
|
||||
"label": "ExtractThumbnailSP",
|
||||
"is_group": true,
|
||||
"children": [
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": false,
|
||||
"key": "ffmpeg_args",
|
||||
"label": "ffmpeg_args",
|
||||
"children": [
|
||||
{
|
||||
"type": "list",
|
||||
"object_type": "text",
|
||||
"key": "input",
|
||||
"label": "input"
|
||||
},
|
||||
{
|
||||
"type": "list",
|
||||
"object_type": "text",
|
||||
"key": "output",
|
||||
"label": "output"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,223 @@
|
|||
{
|
||||
"type": "dict",
|
||||
"key": "deadline",
|
||||
"label": "Deadline",
|
||||
"collapsible": true,
|
||||
"is_file": true,
|
||||
"children": [
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "publish",
|
||||
"label": "Publish plugins",
|
||||
"is_file": true,
|
||||
"children": [
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "MayaSubmitDeadline",
|
||||
"label": "Submit maya job to deadline",
|
||||
"checkbox_key": "enabled",
|
||||
"children": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "enabled",
|
||||
"label": "Enabled"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "optional",
|
||||
"label": "Optional"
|
||||
},
|
||||
{
|
||||
"type": "enum",
|
||||
"key": "tile_assembler_plugin",
|
||||
"label": "Tile Assembler Plugin",
|
||||
"multiselection": false,
|
||||
"enum_items": [
|
||||
{
|
||||
"DraftTileAssembler": "Draft Tile Assembler"
|
||||
},
|
||||
{
|
||||
"oiio": "Open Image IO"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "use_published",
|
||||
"label": "Use Published scene"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "asset_dependencies",
|
||||
"label": "Use Asset dependencies"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "NukeSubmitDeadline",
|
||||
"label": "Nuke Submit to Deadline",
|
||||
"checkbox_key": "enabled",
|
||||
"children": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "enabled",
|
||||
"label": "Enabled"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "optional",
|
||||
"label": "Optional"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "use_published",
|
||||
"label": "Use Published scene"
|
||||
},
|
||||
{
|
||||
"type": "number",
|
||||
"key": "priority",
|
||||
"label": "Priority"
|
||||
},
|
||||
{
|
||||
"type": "number",
|
||||
"key": "Chunk Size",
|
||||
"label": "Chunk Size"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "primary_pool",
|
||||
"label": "Primary Pool"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "secondary_pool",
|
||||
"label": "Secondary Pool"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "group",
|
||||
"label": "Group"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "department",
|
||||
"label": "Department"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "HarmonySubmitDeadline",
|
||||
"label": "Harmony Submit to Deadline",
|
||||
"checkbox_key": "enabled",
|
||||
"children": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "enabled",
|
||||
"label": "Enabled"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "optional",
|
||||
"label": "Optional"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "use_published",
|
||||
"label": "Use Published scene"
|
||||
},
|
||||
{
|
||||
"type": "number",
|
||||
"key": "priority",
|
||||
"label": "Priority"
|
||||
},
|
||||
{
|
||||
"type": "number",
|
||||
"key": "Chunk Size",
|
||||
"label": "Chunk Size"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "primary_pool",
|
||||
"label": "Primary Pool"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "secondary_pool",
|
||||
"label": "Secondary Pool"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "group",
|
||||
"label": "Group"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "department",
|
||||
"label": "Department"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "AfterEffectsSubmitDeadline",
|
||||
"label": "After Effects Submit to Deadline",
|
||||
"checkbox_key": "enabled",
|
||||
"children": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "enabled",
|
||||
"label": "Enabled"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "optional",
|
||||
"label": "Optional"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "use_published",
|
||||
"label": "Use Published scene"
|
||||
},
|
||||
{
|
||||
"type": "number",
|
||||
"key": "priority",
|
||||
"label": "Priority"
|
||||
},
|
||||
{
|
||||
"type": "number",
|
||||
"key": "Chunk Size",
|
||||
"label": "Chunk Size"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "primary_pool",
|
||||
"label": "Primary Pool"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "secondary_pool",
|
||||
"label": "Secondary Pool"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "group",
|
||||
"label": "Group"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "department",
|
||||
"label": "Department"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -16,7 +16,8 @@
|
|||
{
|
||||
"type": "raw-json",
|
||||
"label": "Project Folder Structure",
|
||||
"key": "project_folder_structure"
|
||||
"key": "project_folder_structure",
|
||||
"use_label_wrap": true
|
||||
},
|
||||
{
|
||||
"type": "schema",
|
||||
|
|
|
|||
|
|
@ -5,13 +5,6 @@
|
|||
"label": "Harmony",
|
||||
"is_file": true,
|
||||
"children": [
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "publish",
|
||||
"label": "Publish plugins",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
|
|
@ -31,6 +24,61 @@
|
|||
"label": "Skip Timeliene Check for Tasks"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "publish",
|
||||
"label": "Publish plugins",
|
||||
"children": [
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "CollectPalettes",
|
||||
"label": "Collect Palettes",
|
||||
"children": [
|
||||
{
|
||||
"type": "list",
|
||||
"key": "allowed_tasks",
|
||||
"label": "Allowed tasks",
|
||||
"object_type": "text"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "HarmonySubmitDeadline",
|
||||
"label": "Harmony Submit to Deadline",
|
||||
"children": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "use_published",
|
||||
"label": "Use Published scene"
|
||||
},
|
||||
{
|
||||
"type": "number",
|
||||
"key": "priority",
|
||||
"label": "priority"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "primary_pool",
|
||||
"label": "Primary Pool"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "secondary_pool",
|
||||
"label": "Secondary Pool"
|
||||
},
|
||||
{
|
||||
"type": "number",
|
||||
"key": "chunk_size",
|
||||
"label": "Chunk Size"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,10 @@
|
|||
"type": "schema",
|
||||
"name": "schema_maya_capture"
|
||||
},
|
||||
{
|
||||
"type": "schema",
|
||||
"name": "schema_maya_create"
|
||||
},
|
||||
{
|
||||
"type": "schema",
|
||||
"name": "schema_maya_publish"
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "create",
|
||||
"label": "Create plugins",
|
||||
"label": "Creator plugins",
|
||||
"children": [
|
||||
{
|
||||
"type": "dict",
|
||||
|
|
@ -42,144 +42,9 @@
|
|||
]
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "publish",
|
||||
"label": "Publish plugins",
|
||||
"children": [
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "PreCollectNukeInstances",
|
||||
"label": "PreCollectNukeInstances",
|
||||
"is_group": true,
|
||||
"children": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "sync_workfile_version",
|
||||
"label": "Sync Version from workfile"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"checkbox_key": "enabled",
|
||||
"key": "ExtractThumbnail",
|
||||
"label": "ExtractThumbnail",
|
||||
"is_group": true,
|
||||
"children": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "enabled",
|
||||
"label": "Enabled"
|
||||
},
|
||||
{
|
||||
"type": "raw-json",
|
||||
"key": "nodes",
|
||||
"label": "Nodes"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"checkbox_key": "enabled",
|
||||
"key": "ValidateKnobs",
|
||||
"label": "ValidateKnobs",
|
||||
"is_group": true,
|
||||
"children": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "enabled",
|
||||
"label": "Enabled"
|
||||
},
|
||||
{
|
||||
"type": "raw-json",
|
||||
"key": "knobs",
|
||||
"label": "Knobs"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"checkbox_key": "enabled",
|
||||
"key": "ExtractReviewDataLut",
|
||||
"label": "ExtractReviewDataLut",
|
||||
"is_group": true,
|
||||
"children": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "enabled",
|
||||
"label": "Enabled"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"checkbox_key": "enabled",
|
||||
"key": "ExtractReviewDataMov",
|
||||
"label": "ExtractReviewDataMov",
|
||||
"is_group": true,
|
||||
"children": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "enabled",
|
||||
"label": "Enabled"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "viewer_lut_raw",
|
||||
"label": "Viewer LUT raw"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "ExtractSlateFrame",
|
||||
"label": "ExtractSlateFrame",
|
||||
"is_group": true,
|
||||
"children": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "viewer_lut_raw",
|
||||
"label": "Viewer LUT raw"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "NukeSubmitDeadline",
|
||||
"label": "NukeSubmitDeadline",
|
||||
"is_group": true,
|
||||
"children": [
|
||||
{
|
||||
"type": "number",
|
||||
"key": "deadline_priority",
|
||||
"label": "deadline_priority"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "deadline_pool",
|
||||
"label": "deadline_pool"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "deadline_pool_secondary",
|
||||
"label": "deadline_pool_secondary"
|
||||
},
|
||||
{
|
||||
"type": "number",
|
||||
"key": "deadline_chunk_size",
|
||||
"label": "deadline_chunk_size"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
"type": "schema",
|
||||
"name": "schema_nuke_publish",
|
||||
"template_data": []
|
||||
},
|
||||
{
|
||||
"type": "schema",
|
||||
|
|
|
|||
|
|
@ -5,44 +5,6 @@
|
|||
"label": "Standalone Publisher",
|
||||
"is_file": true,
|
||||
"children": [
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "publish",
|
||||
"label": "Publish plugins",
|
||||
"is_file": true,
|
||||
"children": [
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "ExtractThumbnailSP",
|
||||
"label": "ExtractThumbnailSP",
|
||||
"is_group": true,
|
||||
"children": [
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": false,
|
||||
"key": "ffmpeg_args",
|
||||
"label": "ffmpeg_args",
|
||||
"children": [
|
||||
{
|
||||
"type": "list",
|
||||
"object_type": "text",
|
||||
"key": "input",
|
||||
"label": "input"
|
||||
},
|
||||
{
|
||||
"type": "list",
|
||||
"object_type": "text",
|
||||
"key": "output",
|
||||
"label": "output"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "dict-modifiable",
|
||||
"collapsible": true,
|
||||
|
|
@ -88,6 +50,44 @@
|
|||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "publish",
|
||||
"label": "Publish plugins",
|
||||
"is_file": true,
|
||||
"children": [
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "ExtractThumbnailSP",
|
||||
"label": "ExtractThumbnailSP",
|
||||
"is_group": true,
|
||||
"children": [
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": false,
|
||||
"key": "ffmpeg_args",
|
||||
"label": "ffmpeg_args",
|
||||
"children": [
|
||||
{
|
||||
"type": "list",
|
||||
"object_type": "text",
|
||||
"key": "input",
|
||||
"label": "input"
|
||||
},
|
||||
{
|
||||
"type": "list",
|
||||
"object_type": "text",
|
||||
"key": "output",
|
||||
"label": "output"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,86 @@
|
|||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "create",
|
||||
"label": "Creator plugins",
|
||||
"children": [
|
||||
{
|
||||
"type": "schema_template",
|
||||
"name": "template_create_plugin",
|
||||
"template_data": [
|
||||
{
|
||||
"key": "CreateAnimation",
|
||||
"label": "Create Animation"
|
||||
},
|
||||
{
|
||||
"key": "CreateAss",
|
||||
"label": "Create Ass"
|
||||
},
|
||||
{
|
||||
"key": "CreateAssembly",
|
||||
"label": "Create Assembly"
|
||||
},
|
||||
{
|
||||
"key": "CreateCamera",
|
||||
"label": "Create Camera"
|
||||
},
|
||||
{
|
||||
"key": "CreateLayout",
|
||||
"label": "Create Layout"
|
||||
},
|
||||
{
|
||||
"key": "CreateLook",
|
||||
"label": "Create Look"
|
||||
},
|
||||
{
|
||||
"key": "CreateMayaScene",
|
||||
"label": "Create Maya Scene"
|
||||
},
|
||||
{
|
||||
"key": "CreateModel",
|
||||
"label": "Create Model"
|
||||
},
|
||||
{
|
||||
"key": "CreatePointCache",
|
||||
"label": "Create Cache"
|
||||
},
|
||||
{
|
||||
"key": "CreateRender",
|
||||
"label": "Create Render"
|
||||
},
|
||||
{
|
||||
"key": "CreateRenderSetup",
|
||||
"label": "Create Render Setup"
|
||||
},
|
||||
{
|
||||
"key": "CreateReview",
|
||||
"label": "Create Review"
|
||||
},
|
||||
{
|
||||
"key": "CreateRig",
|
||||
"label": "Create Rig"
|
||||
},
|
||||
{
|
||||
"key": "CreateSetDress",
|
||||
"label": "Create Set Dress"
|
||||
},
|
||||
{
|
||||
"key": "CreateUnrealStaticMesh",
|
||||
"label": "Create Unreal - Static Mesh"
|
||||
},
|
||||
{
|
||||
"key": "CreateVrayProxy",
|
||||
"label": "Create VRay Proxy"
|
||||
},
|
||||
{
|
||||
"key": "CreateVRayScene",
|
||||
"label": "Create VRay Scene"
|
||||
},
|
||||
{
|
||||
"key": "CreateYetiRig",
|
||||
"label": "Create Yeti Rig"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -151,6 +151,32 @@
|
|||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "schema_template",
|
||||
"name": "template_loader_plugin",
|
||||
"template_data": [
|
||||
{
|
||||
"key": "ReferenceLoader",
|
||||
"label": "Reference Loader"
|
||||
},
|
||||
{
|
||||
"key": "AudioLoader",
|
||||
"label": "Audio Loader"
|
||||
},
|
||||
{
|
||||
"key": "GpuCacheLoader",
|
||||
"label": "GpuCache Loader"
|
||||
},
|
||||
{
|
||||
"key": "ImagePlaneLoader",
|
||||
"label": "Imageplane Loader"
|
||||
},
|
||||
{
|
||||
"key": "MatchmoveLoader",
|
||||
"label": "Matchmove Loader"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,71 +26,9 @@
|
|||
},
|
||||
{
|
||||
"type": "label",
|
||||
"label": "Collectors"
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "ValidateCameraAttributes",
|
||||
"label": "Validate Camera Attributes",
|
||||
"checkbox_key": "enabled",
|
||||
"children": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "enabled",
|
||||
"label": "Enabled"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "optional",
|
||||
"label": "Optional"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "ValidateModelName",
|
||||
"label": "Validate Model Name",
|
||||
"checkbox_key": "enabled",
|
||||
"children": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "enabled",
|
||||
"label": "Enabled"
|
||||
},
|
||||
{
|
||||
"type": "label",
|
||||
"label": "Path to material file defining list of material names to check. This is material name per line simple text file.<br/>It will be checked against named group <b>shader</b> in your <em>Validation regex</em>.<p>For example: <br/> <code>^.*(?P=<shader>.+)_GEO</code></p>"
|
||||
},
|
||||
{
|
||||
"type": "path-widget",
|
||||
"key": "material_file",
|
||||
"label": "Material File",
|
||||
"multiplatform": true,
|
||||
"multipath": false
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "regex",
|
||||
"label": "Validation regex"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "ValidateAssemblyName",
|
||||
"label": "Validate Assembly Name",
|
||||
"checkbox_key": "enabled",
|
||||
"children": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "enabled",
|
||||
"label": "Enabled"
|
||||
}
|
||||
]
|
||||
"label": "Validators"
|
||||
},
|
||||
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
|
|
@ -114,20 +52,7 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "ValidateMeshHasOverlappingUVs",
|
||||
"label": "ValidateMeshHasOverlappingUVs",
|
||||
"checkbox_key": "enabled",
|
||||
"children": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "enabled",
|
||||
"label": "Enabled"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
|
|
@ -147,6 +72,169 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "collapsible-wrap",
|
||||
"label": "Model",
|
||||
"children": [
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "ValidateModelName",
|
||||
"label": "Validate Model Name",
|
||||
"checkbox_key": "enabled",
|
||||
"children": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "enabled",
|
||||
"label": "Enabled"
|
||||
},
|
||||
{
|
||||
"type": "label",
|
||||
"label": "Path to material file defining list of material names to check. This is material name per line simple text file.<br/>It will be checked against named group <b>shader</b> in your <em>Validation regex</em>.<p>For example: <br/> <code>^.*(?P=<shader>.+)_GEO</code></p>"
|
||||
},
|
||||
{
|
||||
"type": "path-widget",
|
||||
"key": "material_file",
|
||||
"label": "Material File",
|
||||
"multiplatform": true,
|
||||
"multipath": false
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "regex",
|
||||
"label": "Validation regex"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "ValidateTransformNamingSuffix",
|
||||
"label": "ValidateTransformNamingSuffix",
|
||||
"checkbox_key": "enabled",
|
||||
"children": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "enabled",
|
||||
"label": "Enabled"
|
||||
},
|
||||
{
|
||||
"type": "label",
|
||||
"label": "Validates transform suffix based on the type of its children shapes."
|
||||
},
|
||||
{
|
||||
"type": "raw-json",
|
||||
"key": "SUFFIX_NAMING_TABLE",
|
||||
"label": "Suffix Naming Table"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "ALLOW_IF_NOT_IN_SUFFIX_TABLE",
|
||||
"label": "Allow if suffix not in table"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "schema_template",
|
||||
"name": "template_publish_plugin",
|
||||
"template_data": [
|
||||
{
|
||||
"key": "ValidateColorSets",
|
||||
"label": "ValidateColorSets"
|
||||
},
|
||||
{
|
||||
"key": "ValidateMeshHasOverlappingUVs",
|
||||
"label": "ValidateMeshHasOverlappingUVs"
|
||||
},
|
||||
{
|
||||
"key": "ValidateMeshArnoldAttributes",
|
||||
"label": "ValidateMeshArnoldAttributes"
|
||||
},
|
||||
{
|
||||
"key": "ValidateMeshShaderConnections",
|
||||
"label": "ValidateMeshShaderConnections"
|
||||
},
|
||||
{
|
||||
"key": "ValidateMeshSingleUVSet",
|
||||
"label": "ValidateMeshSingleUVSet"
|
||||
},
|
||||
{
|
||||
"key": "ValidateMeshHasUVs",
|
||||
"label": "ValidateMeshHasUVs"
|
||||
},
|
||||
{
|
||||
"key": "ValidateMeshLaminaFaces",
|
||||
"label": "ValidateMeshLaminaFaces"
|
||||
},
|
||||
{
|
||||
"key": "ValidateMeshNonManifold",
|
||||
"label": "ValidateMeshNonManifold"
|
||||
},
|
||||
{
|
||||
"key": "ValidateMeshUVSetMap1",
|
||||
"label": "ValidateMeshUVSetMap1",
|
||||
"docstring": "Validate model's default uv set exists and is named 'map1'.<br><br>In Maya meshes by default have a uv set named 'map1' that cannot be deleted. It can be renamed, however,<br> introducing some issues with some renderers. As such we ensure the first (default) UV set index is named 'map1'."
|
||||
},
|
||||
{
|
||||
"key": "ValidateMeshVerticesHaveEdges",
|
||||
"label": "ValidateMeshVerticesHaveEdges"
|
||||
},
|
||||
{
|
||||
"key": "ValidateNoAnimation",
|
||||
"label": "ValidateNoAnimation",
|
||||
"docstring": "Ensure no keyframes on nodes in the Instance.<br>Even though a Model would extract without animCurves correctly this avoids getting different <br> output from a model when extracted from a different frame than the first frame. (Might be overly restrictive though)."
|
||||
},
|
||||
{
|
||||
"key": "ValidateNoNamespace",
|
||||
"label": "ValidateNoNamespace"
|
||||
},
|
||||
{
|
||||
"key": "ValidateNoNullTransforms",
|
||||
"label": "ValidateNoNullTransforms"
|
||||
},
|
||||
{
|
||||
"key": "ValidateNoUnknownNodes",
|
||||
"label": "ValidateNoUnknownNodes"
|
||||
},
|
||||
{
|
||||
"key": "ValidateNodeNoGhosting",
|
||||
"label": "ValidateNodeNoGhosting"
|
||||
},
|
||||
{
|
||||
"key": "ValidateShapeDefaultNames",
|
||||
"label": "ValidateShapeDefaultNames"
|
||||
},
|
||||
{
|
||||
"key": "ValidateShapeRenderStats",
|
||||
"label": "ValidateShapeRenderStats"
|
||||
},
|
||||
{
|
||||
"key": "ValidateTransformZero",
|
||||
"label": "ValidateTransformZero"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "schema_template",
|
||||
"name": "template_publish_plugin",
|
||||
"template_data": [
|
||||
{
|
||||
"key": "ValidateCameraAttributes",
|
||||
"label": "Validate Camera Attributes",
|
||||
"docstring": ""
|
||||
},
|
||||
{
|
||||
"key": "ValidateAssemblyName",
|
||||
"label": "Validate Assembly Name"
|
||||
},
|
||||
{
|
||||
"key": "ValidateAssRelativePaths",
|
||||
"label": "ValidateAssRelativePaths"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "splitter"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -0,0 +1,180 @@
|
|||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "publish",
|
||||
"label": "Publish plugins",
|
||||
"children": [
|
||||
{
|
||||
"type": "label",
|
||||
"label": "Collectors"
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "PreCollectNukeInstances",
|
||||
"label": "PreCollectNukeInstances",
|
||||
"is_group": true,
|
||||
"children": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "sync_workfile_version",
|
||||
"label": "Sync Version from workfile"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "splitter"
|
||||
},
|
||||
{
|
||||
"type": "label",
|
||||
"label": "Validators"
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"checkbox_key": "enabled",
|
||||
"key": "ValidateKnobs",
|
||||
"label": "ValidateKnobs",
|
||||
"is_group": true,
|
||||
"children": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "enabled",
|
||||
"label": "Enabled"
|
||||
},
|
||||
{
|
||||
"type": "raw-json",
|
||||
"key": "knobs",
|
||||
"label": "Knobs"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "schema_template",
|
||||
"name": "template_publish_plugin",
|
||||
"template_data": [
|
||||
{
|
||||
"key": "ValidateOutputResolution",
|
||||
"label": "Validate Output Resolution"
|
||||
},
|
||||
{
|
||||
"key": "ValidateGizmo",
|
||||
"label": "Validate Gizmo (Group)"
|
||||
},
|
||||
{
|
||||
"key": "ValidateScript",
|
||||
"label": "Validate script settings"
|
||||
},
|
||||
{
|
||||
"key": "ValidateNukeWriteBoundingBox",
|
||||
"label": "Validate and Write Bounding Box"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "splitter"
|
||||
},
|
||||
{
|
||||
"type": "label",
|
||||
"label": "Extractors"
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"checkbox_key": "enabled",
|
||||
"key": "ExtractThumbnail",
|
||||
"label": "ExtractThumbnail",
|
||||
"is_group": true,
|
||||
"children": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "enabled",
|
||||
"label": "Enabled"
|
||||
},
|
||||
{
|
||||
"type": "raw-json",
|
||||
"key": "nodes",
|
||||
"label": "Nodes"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"checkbox_key": "enabled",
|
||||
"key": "ExtractReviewDataLut",
|
||||
"label": "ExtractReviewDataLut",
|
||||
"is_group": true,
|
||||
"children": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "enabled",
|
||||
"label": "Enabled"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"checkbox_key": "enabled",
|
||||
"key": "ExtractReviewDataMov",
|
||||
"label": "ExtractReviewDataMov",
|
||||
"is_group": true,
|
||||
"children": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "enabled",
|
||||
"label": "Enabled"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "viewer_lut_raw",
|
||||
"label": "Viewer LUT raw"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "ExtractSlateFrame",
|
||||
"label": "ExtractSlateFrame",
|
||||
"is_group": true,
|
||||
"children": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "viewer_lut_raw",
|
||||
"label": "Viewer LUT raw"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "NukeSubmitDeadline",
|
||||
"label": "NukeSubmitDeadline",
|
||||
"is_group": true,
|
||||
"children": [
|
||||
{
|
||||
"type": "number",
|
||||
"key": "deadline_priority",
|
||||
"label": "deadline_priority"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "deadline_pool",
|
||||
"label": "deadline_pool"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "deadline_pool_secondary",
|
||||
"label": "deadline_pool_secondary"
|
||||
},
|
||||
{
|
||||
"type": "number",
|
||||
"key": "deadline_chunk_size",
|
||||
"label": "deadline_chunk_size"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
[
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "{key}",
|
||||
"label": "{label}",
|
||||
"checkbox_key": "enabled",
|
||||
"children": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "enabled",
|
||||
"label": "Enabled"
|
||||
},
|
||||
{
|
||||
"type": "list",
|
||||
"key": "defaults",
|
||||
"label": "Default Subsets",
|
||||
"object_type": "text"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
[
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "{key}",
|
||||
"label": "{label}",
|
||||
"checkbox_key": "enabled",
|
||||
"children": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "enabled",
|
||||
"label": "Enabled"
|
||||
},
|
||||
{
|
||||
"type": "list",
|
||||
"key": "representations",
|
||||
"label": "Representations",
|
||||
"object_type": "text"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
[
|
||||
{
|
||||
"__default_values__": {
|
||||
"docstring": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "{key}",
|
||||
"label": "{label}",
|
||||
"checkbox_key": "enabled",
|
||||
"children": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "enabled",
|
||||
"label": "Enabled"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "optional",
|
||||
"label": "Optional"
|
||||
},
|
||||
{
|
||||
"type": "label",
|
||||
"label": "{docstring}"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
@ -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()
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
from Qt import QtWidgets, QtGui, QtCore
|
||||
from .lib import CHILD_OFFSET
|
||||
from .widgets import ExpandingWidget
|
||||
|
||||
|
||||
class BaseWidget(QtWidgets.QWidget):
|
||||
|
|
@ -161,27 +163,92 @@ class BaseWidget(QtWidgets.QWidget):
|
|||
|
||||
|
||||
class InputWidget(BaseWidget):
|
||||
def create_ui(self):
|
||||
if self.entity.use_label_wrap:
|
||||
label = None
|
||||
self._create_label_wrap_ui()
|
||||
else:
|
||||
label = self.entity.label
|
||||
self.label_widget = None
|
||||
self.body_widget = None
|
||||
self.content_widget = self
|
||||
self.content_layout = self._create_layout(self)
|
||||
|
||||
self._add_inputs_to_layout()
|
||||
|
||||
self.entity_widget.add_widget_to_layout(self, label)
|
||||
|
||||
def _create_label_wrap_ui(self):
|
||||
content_widget = QtWidgets.QWidget(self)
|
||||
content_widget.setObjectName("ContentWidget")
|
||||
|
||||
content_widget.setProperty("content_state", "")
|
||||
content_layout_margins = (CHILD_OFFSET, 5, 0, 0)
|
||||
|
||||
body_widget = ExpandingWidget(self.entity.label, self)
|
||||
label_widget = body_widget.label_widget
|
||||
body_widget.set_content_widget(content_widget)
|
||||
|
||||
content_layout = self._create_layout(content_widget)
|
||||
content_layout.setContentsMargins(*content_layout_margins)
|
||||
|
||||
main_layout = QtWidgets.QHBoxLayout(self)
|
||||
main_layout.setContentsMargins(0, 0, 0, 0)
|
||||
main_layout.setSpacing(0)
|
||||
main_layout.addWidget(body_widget)
|
||||
|
||||
self.label_widget = label_widget
|
||||
self.body_widget = body_widget
|
||||
self.content_widget = content_widget
|
||||
self.content_layout = content_layout
|
||||
|
||||
def _create_layout(self, parent_widget):
|
||||
layout = QtWidgets.QHBoxLayout(parent_widget)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setSpacing(5)
|
||||
return layout
|
||||
|
||||
def _add_inputs_to_layout(self):
|
||||
raise NotImplementedError(
|
||||
"Method `_add_inputs_to_layout` not implemented {}".format(
|
||||
self.__class__.__name__
|
||||
)
|
||||
)
|
||||
|
||||
def update_style(self):
|
||||
has_unsaved_changes = self.entity.has_unsaved_changes
|
||||
if not has_unsaved_changes and self.entity.group_item:
|
||||
has_unsaved_changes = self.entity.group_item.has_unsaved_changes
|
||||
state = self.get_style_state(
|
||||
style_state = self.get_style_state(
|
||||
self.is_invalid,
|
||||
has_unsaved_changes,
|
||||
self.entity.has_project_override,
|
||||
self.entity.has_studio_override
|
||||
)
|
||||
if self._style_state == state:
|
||||
if self._style_state == style_state:
|
||||
return
|
||||
|
||||
self._style_state = state
|
||||
self._style_state = style_state
|
||||
|
||||
self.input_field.setProperty("input-state", state)
|
||||
self.input_field.setProperty("input-state", style_state)
|
||||
self.input_field.style().polish(self.input_field)
|
||||
if self.label_widget:
|
||||
self.label_widget.setProperty("state", state)
|
||||
self.label_widget.setProperty("state", style_state)
|
||||
self.label_widget.style().polish(self.label_widget)
|
||||
|
||||
if self.body_widget:
|
||||
if style_state:
|
||||
child_style_state = "child-{}".format(style_state)
|
||||
else:
|
||||
child_style_state = ""
|
||||
|
||||
self.body_widget.side_line_widget.setProperty(
|
||||
"state", child_style_state
|
||||
)
|
||||
self.body_widget.side_line_widget.style().polish(
|
||||
self.body_widget.side_line_widget
|
||||
)
|
||||
|
||||
@property
|
||||
def child_invalid(self):
|
||||
return self.is_invalid
|
||||
|
|
|
|||
|
|
@ -24,21 +24,30 @@ from .lib import CHILD_OFFSET
|
|||
|
||||
class DictImmutableKeysWidget(BaseWidget):
|
||||
def create_ui(self):
|
||||
self._child_style_state = ""
|
||||
self.input_fields = []
|
||||
self.checkbox_child = None
|
||||
if not self.entity.is_dynamic_item and not self.entity.label:
|
||||
self._ui_item_without_label()
|
||||
|
||||
self.label_widget = None
|
||||
self.body_widget = None
|
||||
self.content_widget = None
|
||||
self.content_layout = None
|
||||
|
||||
label = None
|
||||
if self.entity.is_dynamic_item:
|
||||
self._ui_as_dynamic_item()
|
||||
|
||||
elif self.entity.use_label_wrap:
|
||||
self._ui_label_wrap()
|
||||
self.checkbox_child = self.entity.non_gui_children.get(
|
||||
self.entity.checkbox_key
|
||||
)
|
||||
|
||||
else:
|
||||
self._ui_item_or_as_widget()
|
||||
if not self.entity.is_dynamic_item:
|
||||
self.checkbox_child = self.entity.non_gui_children.get(
|
||||
self.entity.checkbox_key
|
||||
)
|
||||
self._ui_item_base()
|
||||
label = self.entity.label
|
||||
|
||||
self.widget_mapping = {}
|
||||
self.wrapper_widgets_by_id = {}
|
||||
self._parent_widget_by_entity_id = {}
|
||||
self._added_wrapper_ids = set()
|
||||
self._prepare_entity_layouts(
|
||||
self.entity.gui_layout, self.content_widget
|
||||
)
|
||||
|
|
@ -50,13 +59,13 @@ class DictImmutableKeysWidget(BaseWidget):
|
|||
)
|
||||
)
|
||||
|
||||
self.entity_widget.add_widget_to_layout(self)
|
||||
self.entity_widget.add_widget_to_layout(self, label)
|
||||
|
||||
def _prepare_entity_layouts(self, children, widget):
|
||||
for child in children:
|
||||
if not isinstance(child, dict):
|
||||
if child is not self.checkbox_child:
|
||||
self.widget_mapping[child.id] = widget
|
||||
self._parent_widget_by_entity_id[child.id] = widget
|
||||
continue
|
||||
|
||||
if child["type"] == "collapsible-wrap":
|
||||
|
|
@ -70,73 +79,77 @@ class DictImmutableKeysWidget(BaseWidget):
|
|||
"Unknown Wrapper type \"{}\"".format(child["type"])
|
||||
)
|
||||
|
||||
self.widget_mapping[wrapper.id] = widget
|
||||
self.wrapper_widgets_by_id[wrapper.id] = wrapper
|
||||
self.add_widget_to_layout(wrapper)
|
||||
self._parent_widget_by_entity_id[wrapper.id] = widget
|
||||
|
||||
self._prepare_entity_layouts(child["children"], wrapper)
|
||||
|
||||
def _ui_item_without_label(self):
|
||||
def _ui_item_base(self):
|
||||
self.setObjectName("DictInvisible")
|
||||
|
||||
self.body_widget = None
|
||||
self.content_widget = self
|
||||
self.content_layout = QtWidgets.QGridLayout(self)
|
||||
self.content_layout.setContentsMargins(0, 0, 0, 0)
|
||||
self.content_layout.setSpacing(5)
|
||||
|
||||
def _ui_item_or_as_widget(self):
|
||||
def _ui_as_dynamic_item(self):
|
||||
content_widget = QtWidgets.QWidget(self)
|
||||
content_widget.setObjectName("DictAsWidgetBody")
|
||||
|
||||
if self.entity.is_dynamic_item:
|
||||
content_widget.setObjectName("DictAsWidgetBody")
|
||||
show_borders = str(int(self.entity.show_borders))
|
||||
content_widget.setProperty("show_borders", show_borders)
|
||||
content_layout_margins = (5, 5, 5, 5)
|
||||
main_layout_spacing = 5
|
||||
body_widget = None
|
||||
label_widget = QtWidgets.QLabel(self.entity.label)
|
||||
show_borders = str(int(self.entity.show_borders))
|
||||
content_widget.setProperty("show_borders", show_borders)
|
||||
|
||||
label_widget = QtWidgets.QLabel(self.entity.label)
|
||||
|
||||
content_layout = QtWidgets.QGridLayout(content_widget)
|
||||
content_layout.setContentsMargins(5, 5, 5, 5)
|
||||
|
||||
main_layout = QtWidgets.QHBoxLayout(self)
|
||||
main_layout.setContentsMargins(0, 0, 0, 0)
|
||||
main_layout.setSpacing(5)
|
||||
main_layout.addWidget(content_widget)
|
||||
|
||||
self.label_widget = label_widget
|
||||
self.content_widget = content_widget
|
||||
self.content_layout = content_layout
|
||||
|
||||
def _ui_label_wrap(self):
|
||||
content_widget = QtWidgets.QWidget(self)
|
||||
content_widget.setObjectName("ContentWidget")
|
||||
|
||||
if self.entity.highlight_content:
|
||||
content_state = "hightlighted"
|
||||
bottom_margin = 5
|
||||
else:
|
||||
content_widget.setObjectName("ContentWidget")
|
||||
if self.entity.highlight_content:
|
||||
content_state = "hightlighted"
|
||||
bottom_margin = 5
|
||||
else:
|
||||
content_state = ""
|
||||
bottom_margin = 0
|
||||
content_widget.setProperty("content_state", content_state)
|
||||
content_layout_margins = (CHILD_OFFSET, 5, 0, bottom_margin)
|
||||
main_layout_spacing = 0
|
||||
content_state = ""
|
||||
bottom_margin = 0
|
||||
content_widget.setProperty("content_state", content_state)
|
||||
content_layout_margins = (CHILD_OFFSET, 5, 0, bottom_margin)
|
||||
|
||||
body_widget = ExpandingWidget(self.entity.label, self)
|
||||
label_widget = body_widget.label_widget
|
||||
body_widget.set_content_widget(content_widget)
|
||||
body_widget = ExpandingWidget(self.entity.label, self)
|
||||
label_widget = body_widget.label_widget
|
||||
body_widget.set_content_widget(content_widget)
|
||||
|
||||
content_layout = QtWidgets.QGridLayout(content_widget)
|
||||
content_layout.setContentsMargins(*content_layout_margins)
|
||||
|
||||
main_layout = QtWidgets.QHBoxLayout(self)
|
||||
main_layout.setContentsMargins(0, 0, 0, 0)
|
||||
main_layout.setSpacing(main_layout_spacing)
|
||||
if not body_widget:
|
||||
main_layout.addWidget(content_widget)
|
||||
else:
|
||||
main_layout.addWidget(body_widget)
|
||||
main_layout.setSpacing(0)
|
||||
main_layout.addWidget(body_widget)
|
||||
|
||||
self.label_widget = label_widget
|
||||
self.body_widget = body_widget
|
||||
self.content_widget = content_widget
|
||||
self.content_layout = content_layout
|
||||
|
||||
if body_widget:
|
||||
if len(self.input_fields) == 1 and self.checkbox_widget:
|
||||
body_widget.hide_toolbox(hide_content=True)
|
||||
if len(self.input_fields) == 1 and self.checkbox_widget:
|
||||
body_widget.hide_toolbox(hide_content=True)
|
||||
|
||||
elif self.entity.collapsible:
|
||||
if not self.entity.collapsed:
|
||||
body_widget.toggle_content()
|
||||
else:
|
||||
body_widget.hide_toolbox(hide_content=False)
|
||||
elif self.entity.collapsible:
|
||||
if not self.entity.collapsed:
|
||||
body_widget.toggle_content()
|
||||
else:
|
||||
body_widget.hide_toolbox(hide_content=False)
|
||||
|
||||
def add_widget_to_layout(self, widget, label=None):
|
||||
if self.checkbox_child and widget.entity is self.checkbox_child:
|
||||
|
|
@ -148,9 +161,12 @@ class DictImmutableKeysWidget(BaseWidget):
|
|||
else:
|
||||
map_id = widget.entity.id
|
||||
|
||||
wrapper = self.widget_mapping[map_id]
|
||||
wrapper = self._parent_widget_by_entity_id[map_id]
|
||||
if wrapper is not self.content_widget:
|
||||
wrapper.add_widget_to_layout(widget, label)
|
||||
if wrapper.id not in self._added_wrapper_ids:
|
||||
self.add_widget_to_layout(wrapper)
|
||||
self._added_wrapper_ids.add(wrapper.id)
|
||||
return
|
||||
|
||||
row = self.content_layout.rowCount()
|
||||
|
|
@ -172,7 +188,7 @@ class DictImmutableKeysWidget(BaseWidget):
|
|||
for input_field in self.input_fields:
|
||||
input_field.hierarchical_style_update()
|
||||
|
||||
def update_style(self, is_overriden=None):
|
||||
def update_style(self):
|
||||
if not self.body_widget and not self.label_widget:
|
||||
return
|
||||
|
||||
|
|
@ -186,36 +202,8 @@ class DictImmutableKeysWidget(BaseWidget):
|
|||
has_project_override = self.entity.has_project_override
|
||||
has_studio_override = self.entity.has_studio_override
|
||||
|
||||
is_invalid = self.is_invalid
|
||||
if self.body_widget:
|
||||
child_style_state = self.get_style_state(
|
||||
is_invalid,
|
||||
has_unsaved_changes,
|
||||
has_project_override,
|
||||
has_studio_override
|
||||
)
|
||||
|
||||
if child_style_state:
|
||||
child_style_state = "child-{}".format(child_style_state)
|
||||
|
||||
if self._child_style_state != child_style_state:
|
||||
self.body_widget.side_line_widget.setProperty(
|
||||
"state", child_style_state
|
||||
)
|
||||
self.body_widget.side_line_widget.style().polish(
|
||||
self.body_widget.side_line_widget
|
||||
)
|
||||
self._child_style_state = child_style_state
|
||||
|
||||
# There is nothing to care if there is no label
|
||||
if not self.label_widget:
|
||||
return
|
||||
# Don't change label if is not group or under group item
|
||||
if not self.entity.is_group and not self.entity.group_item:
|
||||
return
|
||||
|
||||
style_state = self.get_style_state(
|
||||
is_invalid,
|
||||
self.is_invalid,
|
||||
has_unsaved_changes,
|
||||
has_project_override,
|
||||
has_studio_override
|
||||
|
|
@ -223,11 +211,32 @@ class DictImmutableKeysWidget(BaseWidget):
|
|||
if self._style_state == style_state:
|
||||
return
|
||||
|
||||
self._style_state = style_state
|
||||
|
||||
if self.body_widget:
|
||||
if style_state:
|
||||
child_style_state = "child-{}".format(style_state)
|
||||
else:
|
||||
child_style_state = ""
|
||||
|
||||
self.body_widget.side_line_widget.setProperty(
|
||||
"state", child_style_state
|
||||
)
|
||||
self.body_widget.side_line_widget.style().polish(
|
||||
self.body_widget.side_line_widget
|
||||
)
|
||||
|
||||
# There is nothing to care if there is no label
|
||||
if not self.label_widget:
|
||||
return
|
||||
|
||||
# Don't change label if is not group or under group item
|
||||
if not self.entity.is_group and not self.entity.group_item:
|
||||
return
|
||||
|
||||
self.label_widget.setProperty("state", style_state)
|
||||
self.label_widget.style().polish(self.label_widget)
|
||||
|
||||
self._style_state = style_state
|
||||
|
||||
def _on_entity_change(self):
|
||||
pass
|
||||
|
||||
|
|
@ -250,26 +259,23 @@ class DictImmutableKeysWidget(BaseWidget):
|
|||
|
||||
|
||||
class BoolWidget(InputWidget):
|
||||
def create_ui(self):
|
||||
def _add_inputs_to_layout(self):
|
||||
checkbox_height = self.style().pixelMetric(
|
||||
QtWidgets.QStyle.PM_IndicatorHeight
|
||||
)
|
||||
self.input_field = NiceCheckbox(height=checkbox_height, parent=self)
|
||||
self.input_field = NiceCheckbox(
|
||||
height=checkbox_height, parent=self.content_widget
|
||||
)
|
||||
|
||||
spacer = QtWidgets.QWidget(self)
|
||||
spacer = QtWidgets.QWidget(self.content_widget)
|
||||
spacer.setAttribute(QtCore.Qt.WA_TranslucentBackground)
|
||||
|
||||
layout = QtWidgets.QHBoxLayout(self)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setSpacing(5)
|
||||
|
||||
layout.addWidget(self.input_field, 0)
|
||||
layout.addWidget(spacer, 1)
|
||||
self.content_layout.addWidget(self.input_field, 0)
|
||||
self.content_layout.addWidget(spacer, 1)
|
||||
|
||||
self.setFocusProxy(self.input_field)
|
||||
|
||||
self.input_field.stateChanged.connect(self._on_value_change)
|
||||
self.entity_widget.add_widget_to_layout(self, self.entity.label)
|
||||
|
||||
def _on_entity_change(self):
|
||||
if self.entity.value != self.input_field.isChecked():
|
||||
|
|
@ -285,12 +291,12 @@ class BoolWidget(InputWidget):
|
|||
|
||||
|
||||
class TextWidget(InputWidget):
|
||||
def create_ui(self):
|
||||
def _add_inputs_to_layout(self):
|
||||
multiline = self.entity.multiline
|
||||
if multiline:
|
||||
self.input_field = QtWidgets.QPlainTextEdit(self)
|
||||
self.input_field = QtWidgets.QPlainTextEdit(self.content_widget)
|
||||
else:
|
||||
self.input_field = QtWidgets.QLineEdit(self)
|
||||
self.input_field = QtWidgets.QLineEdit(self.content_widget)
|
||||
|
||||
placeholder_text = self.entity.placeholder_text
|
||||
if placeholder_text:
|
||||
|
|
@ -302,15 +308,10 @@ class TextWidget(InputWidget):
|
|||
if multiline:
|
||||
layout_kwargs["alignment"] = QtCore.Qt.AlignTop
|
||||
|
||||
layout = QtWidgets.QHBoxLayout(self)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setSpacing(5)
|
||||
layout.addWidget(self.input_field, 1, **layout_kwargs)
|
||||
self.content_layout.addWidget(self.input_field, 1, **layout_kwargs)
|
||||
|
||||
self.input_field.textChanged.connect(self._on_value_change)
|
||||
|
||||
self.entity_widget.add_widget_to_layout(self, self.entity.label)
|
||||
|
||||
def _on_entity_change(self):
|
||||
if self.entity.value != self.input_value():
|
||||
self.set_entity_value()
|
||||
|
|
@ -335,26 +336,20 @@ class TextWidget(InputWidget):
|
|||
|
||||
|
||||
class NumberWidget(InputWidget):
|
||||
def create_ui(self):
|
||||
layout = QtWidgets.QHBoxLayout(self)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setSpacing(5)
|
||||
|
||||
def _add_inputs_to_layout(self):
|
||||
kwargs = {
|
||||
"minimum": self.entity.minimum,
|
||||
"maximum": self.entity.maximum,
|
||||
"decimal": self.entity.decimal
|
||||
}
|
||||
self.input_field = NumberSpinBox(self, **kwargs)
|
||||
self.input_field = NumberSpinBox(self.content_widget, **kwargs)
|
||||
|
||||
self.setFocusProxy(self.input_field)
|
||||
|
||||
layout.addWidget(self.input_field, 1)
|
||||
self.content_layout.addWidget(self.input_field, 1)
|
||||
|
||||
self.input_field.valueChanged.connect(self._on_value_change)
|
||||
|
||||
self.entity_widget.add_widget_to_layout(self, self.entity.label)
|
||||
|
||||
def _on_entity_change(self):
|
||||
if self.entity.value != self.input_field.value():
|
||||
self.set_entity_value()
|
||||
|
|
@ -419,12 +414,8 @@ class RawJsonInput(QtWidgets.QPlainTextEdit):
|
|||
|
||||
|
||||
class RawJsonWidget(InputWidget):
|
||||
def create_ui(self):
|
||||
layout = QtWidgets.QHBoxLayout(self)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setSpacing(5)
|
||||
|
||||
self.input_field = RawJsonInput(self)
|
||||
def _add_inputs_to_layout(self):
|
||||
self.input_field = RawJsonInput(self.content_widget)
|
||||
self.input_field.setSizePolicy(
|
||||
QtWidgets.QSizePolicy.Minimum,
|
||||
QtWidgets.QSizePolicy.MinimumExpanding
|
||||
|
|
@ -432,10 +423,11 @@ class RawJsonWidget(InputWidget):
|
|||
|
||||
self.setFocusProxy(self.input_field)
|
||||
|
||||
layout.addWidget(self.input_field, 1, alignment=QtCore.Qt.AlignTop)
|
||||
self.content_layout.addWidget(
|
||||
self.input_field, 1, alignment=QtCore.Qt.AlignTop
|
||||
)
|
||||
|
||||
self.input_field.textChanged.connect(self._on_value_change)
|
||||
self.entity_widget.add_widget_to_layout(self, self.entity.label)
|
||||
|
||||
def set_entity_value(self):
|
||||
self.input_field.set_value(self.entity.value)
|
||||
|
|
@ -463,31 +455,26 @@ class RawJsonWidget(InputWidget):
|
|||
|
||||
|
||||
class EnumeratorWidget(InputWidget):
|
||||
def create_ui(self):
|
||||
layout = QtWidgets.QHBoxLayout(self)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setSpacing(5)
|
||||
|
||||
def _add_inputs_to_layout(self):
|
||||
if self.entity.multiselection:
|
||||
self.input_field = MultiSelectionComboBox(
|
||||
placeholder=self.entity.placeholder, parent=self
|
||||
placeholder=self.entity.placeholder, parent=self.content_widget
|
||||
)
|
||||
model = self.input_field.model()
|
||||
for idx in range(self.input_field.count()):
|
||||
model.item(idx).setCheckable(True)
|
||||
else:
|
||||
self.input_field = ComboBox(self)
|
||||
self.input_field = ComboBox(self.content_widget)
|
||||
|
||||
for enum_item in self.entity.enum_items:
|
||||
for value, label in enum_item.items():
|
||||
self.input_field.addItem(label, value)
|
||||
|
||||
layout.addWidget(self.input_field, 0)
|
||||
self.content_layout.addWidget(self.input_field, 0)
|
||||
|
||||
self.setFocusProxy(self.input_field)
|
||||
|
||||
self.input_field.value_changed.connect(self._on_value_change)
|
||||
self.entity_widget.add_widget_to_layout(self, self.entity.label)
|
||||
|
||||
def _on_entity_change(self):
|
||||
if self.entity.value != self.input_field.value():
|
||||
|
|
@ -572,12 +559,8 @@ class PathWidget(BaseWidget):
|
|||
|
||||
|
||||
class PathInputWidget(InputWidget):
|
||||
def create_ui(self, label_widget=None):
|
||||
layout = QtWidgets.QHBoxLayout(self)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setSpacing(5)
|
||||
|
||||
self.input_field = QtWidgets.QLineEdit(self)
|
||||
def _add_inputs_to_layout(self):
|
||||
self.input_field = QtWidgets.QLineEdit(self.content_widget)
|
||||
self.args_input_field = None
|
||||
if self.entity.with_arguments:
|
||||
self.input_field.setPlaceholderText("Executable path")
|
||||
|
|
@ -585,15 +568,13 @@ class PathInputWidget(InputWidget):
|
|||
self.args_input_field.setPlaceholderText("Arguments")
|
||||
|
||||
self.setFocusProxy(self.input_field)
|
||||
layout.addWidget(self.input_field, 8)
|
||||
self.content_layout.addWidget(self.input_field, 8)
|
||||
self.input_field.textChanged.connect(self._on_value_change)
|
||||
|
||||
if self.args_input_field:
|
||||
layout.addWidget(self.args_input_field, 2)
|
||||
self.content_layout.addWidget(self.args_input_field, 2)
|
||||
self.args_input_field.textChanged.connect(self._on_value_change)
|
||||
|
||||
self.entity_widget.add_widget_to_layout(self, self.entity.label)
|
||||
|
||||
def _on_entity_change(self):
|
||||
if self.entity.value != self.input_value():
|
||||
self.set_entity_value()
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
||||
|
|
|
|||
12
start.py
12
start.py
|
|
@ -116,7 +116,8 @@ from igniter.tools import load_environments # noqa: E402
|
|||
from igniter.bootstrap_repos import PypeVersion # noqa: E402
|
||||
|
||||
bootstrap = BootstrapRepos()
|
||||
silent_commands = ["run", "igniter", "standalonepublisher"]
|
||||
silent_commands = ["run", "igniter", "standalonepublisher",
|
||||
"extractenvironments"]
|
||||
|
||||
|
||||
def set_environments() -> None:
|
||||
|
|
@ -236,8 +237,9 @@ def _process_arguments() -> tuple:
|
|||
# handle igniter
|
||||
# this is helper to run igniter before anything else
|
||||
if "igniter" in sys.argv:
|
||||
import igniter
|
||||
igniter.run()
|
||||
pass
|
||||
# import igniter
|
||||
# igniter.run()
|
||||
|
||||
return use_version, use_staging
|
||||
|
||||
|
|
@ -472,8 +474,8 @@ def boot():
|
|||
from igniter.terminal_splash import play_animation
|
||||
|
||||
# don't play for silenced commands
|
||||
if all(item not in sys.argv for item in silent_commands):
|
||||
play_animation()
|
||||
# if all(item not in sys.argv for item in silent_commands):
|
||||
# play_animation()
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
# Process arguments
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue