Deadline Pype 3 - merge develop

This commit is contained in:
Petr Kalis 2021-02-26 14:20:12 +01:00
commit 4a07b9a4fa
64 changed files with 4281 additions and 1169 deletions

View file

@ -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"
]

View file

@ -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")

View file

@ -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():

View 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)

View file

@ -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))

View file

@ -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

View file

@ -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,

View file

@ -13,7 +13,6 @@ class ValidateShaderName(pyblish.api.InstancePlugin):
"""
optional = True
active = False
order = pype.api.ValidateContentsOrder
families = ["look"]
hosts = ['maya']

View file

@ -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):

View file

@ -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)

View file

@ -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": {

View file

@ -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",

View file

@ -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)

View file

@ -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)

View file

@ -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.

View file

@ -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",

View file

@ -627,6 +627,7 @@ class TrayModulesManager(ModulesManager):
"clockify",
"standalonepublish_tool",
"log_viewer",
"local_settings",
"settings"
)

View file

@ -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(

View file

@ -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((

View file

@ -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()

View file

@ -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 = [

View 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)

View file

@ -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

View file

@ -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"
)

View 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": ""
}
}
}

View file

@ -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
}
}
}

View file

@ -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": {

View file

@ -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": {

View file

@ -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)

View file

@ -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.

View file

@ -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):

View file

@ -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()

View file

@ -45,6 +45,10 @@
"type": "schema",
"name": "schema_project_ftrack"
},
{
"type": "schema",
"name": "schema_project_deadline"
},
{
"type": "schema",
"name": "schema_project_maya"

View file

@ -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"
}
]
}
]
}
]
}
]
}
]
}

View file

@ -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"
}
]
}
]
}
]
}

View file

@ -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",

View file

@ -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"
}
]
}
]
}
]
}

View file

@ -9,6 +9,10 @@
"type": "schema",
"name": "schema_maya_capture"
},
{
"type": "schema",
"name": "schema_maya_create"
},
{
"type": "schema",
"name": "schema_maya_publish"

View file

@ -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",

View file

@ -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"
}
]
}
]
}
]
}
]
}

View file

@ -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"
}
]
}
]
}

View file

@ -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"
}
]
}
]
}

View file

@ -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=&lt;shader&gt;.+)_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=&lt;shader&gt;.+)_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"
},

View file

@ -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"
}
]
}
]
}

View file

@ -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"
}
]
}
]

View file

@ -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"
}
]
}
]

View file

@ -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}"
}
]
}
]

View file

@ -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()

View file

@ -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

View 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>"
}
}
}
}
}
```

View file

@ -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"
)

View file

@ -0,0 +1,6 @@
from .window import LocalSettingsWindow
__all__ = (
"LocalSettingsWindow",
)

View 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

View 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"
)

View 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

View 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_()

View 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

View 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"
)

View 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()

View file

@ -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

View file

@ -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()

View file

@ -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))

View file

@ -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