mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
Hound
This commit is contained in:
commit
0f20372edd
48 changed files with 2577 additions and 755 deletions
|
|
@ -174,6 +174,25 @@ class ReferenceLoader(api.Loader):
|
|||
|
||||
assert os.path.exists(path), "%s does not exist." % path
|
||||
|
||||
# Need to save alembic settings and reapply, cause referencing resets
|
||||
# them to incoming data.
|
||||
alembic_attrs = ["speed", "offset", "cycleType"]
|
||||
alembic_data = {}
|
||||
if representation["name"] == "abc":
|
||||
alembic_nodes = cmds.ls(
|
||||
"{}:*".format(members[0].split(":")[0]), type="AlembicNode"
|
||||
)
|
||||
if alembic_nodes:
|
||||
for attr in alembic_attrs:
|
||||
node_attr = "{}.{}".format(alembic_nodes[0], attr)
|
||||
alembic_data[attr] = cmds.getAttr(node_attr)
|
||||
else:
|
||||
cmds.warning(
|
||||
"No alembic nodes found in {}".format(
|
||||
cmds.ls("{}:*".format(members[0].split(":")[0]))
|
||||
)
|
||||
)
|
||||
|
||||
try:
|
||||
content = cmds.file(path,
|
||||
loadReference=reference_node,
|
||||
|
|
@ -195,6 +214,16 @@ class ReferenceLoader(api.Loader):
|
|||
|
||||
self.log.warning("Ignoring file read error:\n%s", exc)
|
||||
|
||||
# Reapply alembic settings.
|
||||
if representation["name"] == "abc":
|
||||
alembic_nodes = cmds.ls(
|
||||
"{}:*".format(members[0].split(":")[0]), type="AlembicNode"
|
||||
)
|
||||
if alembic_nodes:
|
||||
for attr in alembic_attrs:
|
||||
value = alembic_data[attr]
|
||||
cmds.setAttr("{}.{}".format(alembic_nodes[0], attr), value)
|
||||
|
||||
# Fix PLN-40 for older containers created with Avalon that had the
|
||||
# `.verticesOnlySet` set to True.
|
||||
if cmds.getAttr("{}.verticesOnlySet".format(node)):
|
||||
|
|
|
|||
171
pype/lib.py
171
pype/lib.py
|
|
@ -20,7 +20,7 @@ import time
|
|||
from avalon import io, pipeline
|
||||
import six
|
||||
import avalon.api
|
||||
from .api import config, Anatomy
|
||||
from .api import config, Anatomy, Logger
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -1623,7 +1623,7 @@ class ApplicationAction(avalon.api.Action):
|
|||
parsed application `.toml` this can launch the application.
|
||||
|
||||
"""
|
||||
|
||||
_log = None
|
||||
config = None
|
||||
group = None
|
||||
variant = None
|
||||
|
|
@ -1633,6 +1633,12 @@ class ApplicationAction(avalon.api.Action):
|
|||
"AVALON_TASK"
|
||||
)
|
||||
|
||||
@property
|
||||
def log(self):
|
||||
if self._log is None:
|
||||
self._log = Logger().get_logger(self.__class__.__name__)
|
||||
return self._log
|
||||
|
||||
def is_compatible(self, session):
|
||||
for key in self.required_session_keys:
|
||||
if key not in session:
|
||||
|
|
@ -1645,10 +1651,169 @@ class ApplicationAction(avalon.api.Action):
|
|||
project_name = session["AVALON_PROJECT"]
|
||||
asset_name = session["AVALON_ASSET"]
|
||||
task_name = session["AVALON_TASK"]
|
||||
return launch_application(
|
||||
launch_application(
|
||||
project_name, asset_name, task_name, self.name
|
||||
)
|
||||
|
||||
self._ftrack_after_launch_procedure(
|
||||
project_name, asset_name, task_name
|
||||
)
|
||||
|
||||
def _ftrack_after_launch_procedure(
|
||||
self, project_name, asset_name, task_name
|
||||
):
|
||||
# TODO move to launch hook
|
||||
required_keys = ("FTRACK_SERVER", "FTRACK_API_USER", "FTRACK_API_KEY")
|
||||
for key in required_keys:
|
||||
if not os.environ.get(key):
|
||||
self.log.debug((
|
||||
"Missing required environment \"{}\""
|
||||
" for Ftrack after launch procedure."
|
||||
).format(key))
|
||||
return
|
||||
|
||||
try:
|
||||
import ftrack_api
|
||||
session = ftrack_api.Session(auto_connect_event_hub=True)
|
||||
self.log.debug("Ftrack session created")
|
||||
except Exception:
|
||||
self.log.warning("Couldn't create Ftrack session")
|
||||
return
|
||||
|
||||
try:
|
||||
entity = self._find_ftrack_task_entity(
|
||||
session, project_name, asset_name, task_name
|
||||
)
|
||||
self._ftrack_status_change(session, entity, project_name)
|
||||
self._start_timer(session, entity, ftrack_api)
|
||||
except Exception:
|
||||
self.log.warning(
|
||||
"Couldn't finish Ftrack procedure.", exc_info=True
|
||||
)
|
||||
return
|
||||
|
||||
finally:
|
||||
session.close()
|
||||
|
||||
def _find_ftrack_task_entity(
|
||||
self, session, project_name, asset_name, task_name
|
||||
):
|
||||
project_entity = session.query(
|
||||
"Project where full_name is \"{}\"".format(project_name)
|
||||
).first()
|
||||
if not project_entity:
|
||||
self.log.warning(
|
||||
"Couldn't find project \"{}\" in Ftrack.".format(project_name)
|
||||
)
|
||||
return
|
||||
|
||||
potential_task_entities = session.query((
|
||||
"TypedContext where parent.name is \"{}\" and project_id is \"{}\""
|
||||
).format(asset_name, project_entity["id"])).all()
|
||||
filtered_entities = []
|
||||
for _entity in potential_task_entities:
|
||||
if (
|
||||
_entity.entity_type.lower() == "task"
|
||||
and _entity["name"] == task_name
|
||||
):
|
||||
filtered_entities.append(_entity)
|
||||
|
||||
if not filtered_entities:
|
||||
self.log.warning((
|
||||
"Couldn't find task \"{}\" under parent \"{}\" in Ftrack."
|
||||
).format(task_name, asset_name))
|
||||
return
|
||||
|
||||
if len(filtered_entities) > 1:
|
||||
self.log.warning((
|
||||
"Found more than one task \"{}\""
|
||||
" under parent \"{}\" in Ftrack."
|
||||
).format(task_name, asset_name))
|
||||
return
|
||||
|
||||
return filtered_entities[0]
|
||||
|
||||
def _ftrack_status_change(self, session, entity, project_name):
|
||||
presets = config.get_presets(project_name)["ftrack"]["ftrack_config"]
|
||||
statuses = presets.get("status_update")
|
||||
if not statuses:
|
||||
return
|
||||
|
||||
actual_status = entity["status"]["name"].lower()
|
||||
already_tested = set()
|
||||
ent_path = "/".join(
|
||||
[ent["name"] for ent in entity["link"]]
|
||||
)
|
||||
while True:
|
||||
next_status_name = None
|
||||
for key, value in statuses.items():
|
||||
if key in already_tested:
|
||||
continue
|
||||
if actual_status in value or "_any_" in value:
|
||||
if key != "_ignore_":
|
||||
next_status_name = key
|
||||
already_tested.add(key)
|
||||
break
|
||||
already_tested.add(key)
|
||||
|
||||
if next_status_name is None:
|
||||
break
|
||||
|
||||
try:
|
||||
query = "Status where name is \"{}\"".format(
|
||||
next_status_name
|
||||
)
|
||||
status = session.query(query).one()
|
||||
|
||||
entity["status"] = status
|
||||
session.commit()
|
||||
self.log.debug("Changing status to \"{}\" <{}>".format(
|
||||
next_status_name, ent_path
|
||||
))
|
||||
break
|
||||
|
||||
except Exception:
|
||||
session.rollback()
|
||||
msg = (
|
||||
"Status \"{}\" in presets wasn't found"
|
||||
" on Ftrack entity type \"{}\""
|
||||
).format(next_status_name, entity.entity_type)
|
||||
self.log.warning(msg)
|
||||
|
||||
def _start_timer(self, session, entity, _ftrack_api):
|
||||
self.log.debug("Triggering timer start.")
|
||||
|
||||
user_entity = session.query("User where username is \"{}\"".format(
|
||||
os.environ["FTRACK_API_USER"]
|
||||
)).first()
|
||||
if not user_entity:
|
||||
self.log.warning(
|
||||
"Couldn't find user with username \"{}\" in Ftrack".format(
|
||||
os.environ["FTRACK_API_USER"]
|
||||
)
|
||||
)
|
||||
return
|
||||
|
||||
source = {
|
||||
"user": {
|
||||
"id": user_entity["id"],
|
||||
"username": user_entity["username"]
|
||||
}
|
||||
}
|
||||
event_data = {
|
||||
"actionIdentifier": "start.timer",
|
||||
"selection": [{"entityId": entity["id"], "entityType": "task"}]
|
||||
}
|
||||
session.event_hub.publish(
|
||||
_ftrack_api.event.base.Event(
|
||||
topic="ftrack.action.launch",
|
||||
data=event_data,
|
||||
source=source
|
||||
),
|
||||
on_error="ignore"
|
||||
)
|
||||
self.log.debug("Timer start triggered successfully.")
|
||||
|
||||
|
||||
def timeit(method):
|
||||
""" Decorator to print how much time function took.
|
||||
|
|
|
|||
|
|
@ -205,10 +205,16 @@ class ProcessEventHub(SocketBaseEventHub):
|
|||
else:
|
||||
try:
|
||||
self._handle(event)
|
||||
|
||||
mongo_id = event["data"].get("_event_mongo_id")
|
||||
if mongo_id is None:
|
||||
continue
|
||||
|
||||
self.dbcon.update_one(
|
||||
{"id": event["id"]},
|
||||
{"_id": mongo_id},
|
||||
{"$set": {"pype_data.is_processed": True}}
|
||||
)
|
||||
|
||||
except pymongo.errors.AutoReconnect:
|
||||
self.pypelog.error((
|
||||
"Mongo server \"{}\" is not responding, exiting."
|
||||
|
|
@ -244,6 +250,7 @@ class ProcessEventHub(SocketBaseEventHub):
|
|||
}
|
||||
try:
|
||||
event = ftrack_api.event.base.Event(**new_event_data)
|
||||
event["data"]["_event_mongo_id"] = event_data["_id"]
|
||||
except Exception:
|
||||
self.logger.exception(L(
|
||||
'Failed to convert payload into event: {0}',
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ from pype.modules.ftrack.ftrack_server.lib import (
|
|||
SocketSession, StatusEventHub,
|
||||
TOPIC_STATUS_SERVER, TOPIC_STATUS_SERVER_RESULT
|
||||
)
|
||||
from pype.api import Logger, config
|
||||
from pype.api import Logger
|
||||
|
||||
log = Logger().get_logger("Event storer")
|
||||
action_identifier = (
|
||||
|
|
@ -23,17 +23,7 @@ action_data = {
|
|||
"label": "Pype Admin",
|
||||
"variant": "- Event server Status ({})".format(host_ip),
|
||||
"description": "Get Infromation about event server",
|
||||
"actionIdentifier": action_identifier,
|
||||
"icon": "{}/ftrack/action_icons/PypeAdmin.svg".format(
|
||||
os.environ.get(
|
||||
"PYPE_STATICS_SERVER",
|
||||
"http://localhost:{}".format(
|
||||
config.get_presets().get("services", {}).get(
|
||||
"rest_api", {}
|
||||
).get("default_port", 8021)
|
||||
)
|
||||
)
|
||||
)
|
||||
"actionIdentifier": action_identifier
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
64
pype/modules/websocket_server/hosts/photoshop.py
Normal file
64
pype/modules/websocket_server/hosts/photoshop.py
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
from pype.api import Logger
|
||||
from wsrpc_aiohttp import WebSocketRoute
|
||||
import functools
|
||||
|
||||
import avalon.photoshop as photoshop
|
||||
|
||||
log = Logger().get_logger("WebsocketServer")
|
||||
|
||||
|
||||
class Photoshop(WebSocketRoute):
|
||||
"""
|
||||
One route, mimicking external application (like Harmony, etc).
|
||||
All functions could be called from client.
|
||||
'do_notify' function calls function on the client - mimicking
|
||||
notification after long running job on the server or similar
|
||||
"""
|
||||
instance = None
|
||||
|
||||
def init(self, **kwargs):
|
||||
# Python __init__ must be return "self".
|
||||
# This method might return anything.
|
||||
log.debug("someone called Photoshop route")
|
||||
self.instance = self
|
||||
return kwargs
|
||||
|
||||
# server functions
|
||||
async def ping(self):
|
||||
log.debug("someone called Photoshop route ping")
|
||||
|
||||
# This method calls function on the client side
|
||||
# client functions
|
||||
|
||||
async def read(self):
|
||||
log.debug("photoshop.read client calls server server calls "
|
||||
"Photo client")
|
||||
return await self.socket.call('Photoshop.read')
|
||||
|
||||
# panel routes for tools
|
||||
async def creator_route(self):
|
||||
self._tool_route("creator")
|
||||
|
||||
async def workfiles_route(self):
|
||||
self._tool_route("workfiles")
|
||||
|
||||
async def loader_route(self):
|
||||
self._tool_route("loader")
|
||||
|
||||
async def publish_route(self):
|
||||
self._tool_route("publish")
|
||||
|
||||
async def sceneinventory_route(self):
|
||||
self._tool_route("sceneinventory")
|
||||
|
||||
async def projectmanager_route(self):
|
||||
self._tool_route("projectmanager")
|
||||
|
||||
def _tool_route(self, tool_name):
|
||||
"""The address accessed when clicking on the buttons."""
|
||||
partial_method = functools.partial(photoshop.show, tool_name)
|
||||
|
||||
photoshop.execute_in_main_thread(partial_method)
|
||||
|
||||
# Required return statement.
|
||||
return "nothing"
|
||||
283
pype/modules/websocket_server/stubs/photoshop_server_stub.py
Normal file
283
pype/modules/websocket_server/stubs/photoshop_server_stub.py
Normal file
|
|
@ -0,0 +1,283 @@
|
|||
from pype.modules.websocket_server import WebSocketServer
|
||||
"""
|
||||
Stub handling connection from server to client.
|
||||
Used anywhere solution is calling client methods.
|
||||
"""
|
||||
import json
|
||||
from collections import namedtuple
|
||||
|
||||
|
||||
class PhotoshopServerStub():
|
||||
"""
|
||||
Stub for calling function on client (Photoshop js) side.
|
||||
Expects that client is already connected (started when avalon menu
|
||||
is opened).
|
||||
'self.websocketserver.call' is used as async wrapper
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.websocketserver = WebSocketServer.get_instance()
|
||||
self.client = self.websocketserver.get_client()
|
||||
|
||||
def open(self, path):
|
||||
"""
|
||||
Open file located at 'path' (local).
|
||||
:param path: <string> file path locally
|
||||
:return: None
|
||||
"""
|
||||
self.websocketserver.call(self.client.call
|
||||
('Photoshop.open', path=path)
|
||||
)
|
||||
|
||||
def read(self, layer, layers_meta=None):
|
||||
"""
|
||||
Parses layer metadata from Headline field of active document
|
||||
:param layer: <namedTuple Layer("id":XX, "name":"YYY")
|
||||
:param layers_meta: full list from Headline (for performance in loops)
|
||||
:return:
|
||||
"""
|
||||
if layers_meta is None:
|
||||
layers_meta = self.get_layers_metadata()
|
||||
|
||||
return layers_meta.get(str(layer.id))
|
||||
|
||||
def imprint(self, layer, data, all_layers=None, layers_meta=None):
|
||||
"""
|
||||
Save layer metadata to Headline field of active document
|
||||
:param layer: <namedTuple> Layer("id": XXX, "name":'YYY')
|
||||
:param data: <string> json representation for single layer
|
||||
:param all_layers: <list of namedTuples> - for performance, could be
|
||||
injected for usage in loop, if not, single call will be
|
||||
triggered
|
||||
:param layers_meta: <string> json representation from Headline
|
||||
(for performance - provide only if imprint is in
|
||||
loop - value should be same)
|
||||
:return: None
|
||||
"""
|
||||
if not layers_meta:
|
||||
layers_meta = self.get_layers_metadata()
|
||||
# json.dumps writes integer values in a dictionary to string, so
|
||||
# anticipating it here.
|
||||
if str(layer.id) in layers_meta and layers_meta[str(layer.id)]:
|
||||
layers_meta[str(layer.id)].update(data)
|
||||
else:
|
||||
layers_meta[str(layer.id)] = data
|
||||
|
||||
# Ensure only valid ids are stored.
|
||||
if not all_layers:
|
||||
all_layers = self.get_layers()
|
||||
layer_ids = [layer.id for layer in all_layers]
|
||||
cleaned_data = {}
|
||||
|
||||
for id in layers_meta:
|
||||
if int(id) in layer_ids:
|
||||
cleaned_data[id] = layers_meta[id]
|
||||
|
||||
payload = json.dumps(cleaned_data, indent=4)
|
||||
|
||||
self.websocketserver.call(self.client.call
|
||||
('Photoshop.imprint', payload=payload)
|
||||
)
|
||||
|
||||
def get_layers(self):
|
||||
"""
|
||||
Returns JSON document with all(?) layers in active document.
|
||||
|
||||
:return: <list of namedtuples>
|
||||
Format of tuple: { 'id':'123',
|
||||
'name': 'My Layer 1',
|
||||
'type': 'GUIDE'|'FG'|'BG'|'OBJ'
|
||||
'visible': 'true'|'false'
|
||||
"""
|
||||
res = self.websocketserver.call(self.client.call
|
||||
('Photoshop.get_layers'))
|
||||
|
||||
return self._to_records(res)
|
||||
|
||||
def get_layers_in_layers(self, layers):
|
||||
"""
|
||||
Return all layers that belong to layers (might be groups).
|
||||
:param layers: <list of namedTuples>
|
||||
:return: <list of namedTuples>
|
||||
"""
|
||||
all_layers = self.get_layers()
|
||||
ret = []
|
||||
parent_ids = set([lay.id for lay in layers])
|
||||
|
||||
for layer in all_layers:
|
||||
parents = set(layer.parents)
|
||||
if len(parent_ids & parents) > 0:
|
||||
ret.append(layer)
|
||||
if layer.id in parent_ids:
|
||||
ret.append(layer)
|
||||
|
||||
return ret
|
||||
|
||||
def create_group(self, name):
|
||||
"""
|
||||
Create new group (eg. LayerSet)
|
||||
:return: <namedTuple Layer("id":XX, "name":"YYY")>
|
||||
"""
|
||||
ret = self.websocketserver.call(self.client.call
|
||||
('Photoshop.create_group',
|
||||
name=name))
|
||||
# create group on PS is asynchronous, returns only id
|
||||
layer = {"id": ret, "name": name, "group": True}
|
||||
return namedtuple('Layer', layer.keys())(*layer.values())
|
||||
|
||||
def group_selected_layers(self, name):
|
||||
"""
|
||||
Group selected layers into new LayerSet (eg. group)
|
||||
:return: <json representation of Layer>
|
||||
"""
|
||||
res = self.websocketserver.call(self.client.call
|
||||
('Photoshop.group_selected_layers',
|
||||
name=name)
|
||||
)
|
||||
return self._to_records(res)
|
||||
|
||||
def get_selected_layers(self):
|
||||
"""
|
||||
Get a list of actually selected layers
|
||||
:return: <list of Layer('id':XX, 'name':"YYY")>
|
||||
"""
|
||||
res = self.websocketserver.call(self.client.call
|
||||
('Photoshop.get_selected_layers'))
|
||||
return self._to_records(res)
|
||||
|
||||
def select_layers(self, layers):
|
||||
"""
|
||||
Selecte specified layers in Photoshop
|
||||
:param layers: <list of Layer('id':XX, 'name':"YYY")>
|
||||
:return: None
|
||||
"""
|
||||
layer_ids = [layer.id for layer in layers]
|
||||
|
||||
self.websocketserver.call(self.client.call
|
||||
('Photoshop.get_layers',
|
||||
layers=layer_ids)
|
||||
)
|
||||
|
||||
def get_active_document_full_name(self):
|
||||
"""
|
||||
Returns full name with path of active document via ws call
|
||||
:return: <string> full path with name
|
||||
"""
|
||||
res = self.websocketserver.call(
|
||||
self.client.call('Photoshop.get_active_document_full_name'))
|
||||
|
||||
return res
|
||||
|
||||
def get_active_document_name(self):
|
||||
"""
|
||||
Returns just a name of active document via ws call
|
||||
:return: <string> file name
|
||||
"""
|
||||
res = self.websocketserver.call(self.client.call
|
||||
('Photoshop.get_active_document_name'))
|
||||
|
||||
return res
|
||||
|
||||
def is_saved(self):
|
||||
"""
|
||||
Returns true if no changes in active document
|
||||
:return: <boolean>
|
||||
"""
|
||||
return self.websocketserver.call(self.client.call
|
||||
('Photoshop.is_saved'))
|
||||
|
||||
def save(self):
|
||||
"""
|
||||
Saves active document
|
||||
:return: None
|
||||
"""
|
||||
self.websocketserver.call(self.client.call
|
||||
('Photoshop.save'))
|
||||
|
||||
def saveAs(self, image_path, ext, as_copy):
|
||||
"""
|
||||
Saves active document to psd (copy) or png or jpg
|
||||
:param image_path: <string> full local path
|
||||
:param ext: <string psd|jpg|png>
|
||||
:param as_copy: <boolean>
|
||||
:return: None
|
||||
"""
|
||||
self.websocketserver.call(self.client.call
|
||||
('Photoshop.saveAs',
|
||||
image_path=image_path,
|
||||
ext=ext,
|
||||
as_copy=as_copy))
|
||||
|
||||
def set_visible(self, layer_id, visibility):
|
||||
"""
|
||||
Set layer with 'layer_id' to 'visibility'
|
||||
:param layer_id: <int>
|
||||
:param visibility: <true - set visible, false - hide>
|
||||
:return: None
|
||||
"""
|
||||
self.websocketserver.call(self.client.call
|
||||
('Photoshop.set_visible',
|
||||
layer_id=layer_id,
|
||||
visibility=visibility))
|
||||
|
||||
def get_layers_metadata(self):
|
||||
"""
|
||||
Reads layers metadata from Headline from active document in PS.
|
||||
(Headline accessible by File > File Info)
|
||||
:return: <string> - json documents
|
||||
"""
|
||||
layers_data = {}
|
||||
res = self.websocketserver.call(self.client.call('Photoshop.read'))
|
||||
try:
|
||||
layers_data = json.loads(res)
|
||||
except json.decoder.JSONDecodeError:
|
||||
pass
|
||||
return layers_data
|
||||
|
||||
def import_smart_object(self, path):
|
||||
"""
|
||||
Import the file at `path` as a smart object to active document.
|
||||
|
||||
Args:
|
||||
path (str): File path to import.
|
||||
"""
|
||||
res = self.websocketserver.call(self.client.call
|
||||
('Photoshop.import_smart_object',
|
||||
path=path))
|
||||
|
||||
return self._to_records(res).pop()
|
||||
|
||||
def replace_smart_object(self, layer, path):
|
||||
"""
|
||||
Replace the smart object `layer` with file at `path`
|
||||
|
||||
Args:
|
||||
layer (namedTuple): Layer("id":XX, "name":"YY"..).
|
||||
path (str): File to import.
|
||||
"""
|
||||
self.websocketserver.call(self.client.call
|
||||
('Photoshop.replace_smart_object',
|
||||
layer=layer,
|
||||
path=path))
|
||||
|
||||
def close(self):
|
||||
self.client.close()
|
||||
|
||||
def _to_records(self, res):
|
||||
"""
|
||||
Converts string json representation into list of named tuples for
|
||||
dot notation access to work.
|
||||
:return: <list of named tuples>
|
||||
:param res: <string> - json representation
|
||||
"""
|
||||
try:
|
||||
layers_data = json.loads(res)
|
||||
except json.decoder.JSONDecodeError:
|
||||
raise ValueError("Received broken JSON {}".format(res))
|
||||
ret = []
|
||||
# convert to namedtuple to use dot donation
|
||||
if isinstance(layers_data, dict): # TODO refactore
|
||||
layers_data = [layers_data]
|
||||
for d in layers_data:
|
||||
ret.append(namedtuple('Layer', d.keys())(*d.values()))
|
||||
return ret
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
from pype.api import config, Logger
|
||||
from pype.api import Logger
|
||||
|
||||
import threading
|
||||
from aiohttp import web
|
||||
|
|
@ -9,6 +9,7 @@ import os
|
|||
import sys
|
||||
import pyclbr
|
||||
import importlib
|
||||
import urllib
|
||||
|
||||
log = Logger().get_logger("WebsocketServer")
|
||||
|
||||
|
|
@ -19,24 +20,23 @@ class WebSocketServer():
|
|||
Uses class in external_app_1.py to mimic implementation for single
|
||||
external application.
|
||||
'test_client' folder contains two test implementations of client
|
||||
|
||||
WIP
|
||||
"""
|
||||
_instance = None
|
||||
|
||||
def __init__(self):
|
||||
self.qaction = None
|
||||
self.failed_icon = None
|
||||
self._is_running = False
|
||||
default_port = 8099
|
||||
WebSocketServer._instance = self
|
||||
self.client = None
|
||||
self.handlers = {}
|
||||
|
||||
try:
|
||||
self.presets = config.get_presets()["services"]["websocket_server"]
|
||||
except Exception:
|
||||
self.presets = {"default_port": default_port, "exclude_ports": []}
|
||||
log.debug((
|
||||
"There are not set presets for WebsocketServer."
|
||||
" Using defaults \"{}\""
|
||||
).format(str(self.presets)))
|
||||
websocket_url = os.getenv("WEBSOCKET_URL")
|
||||
if websocket_url:
|
||||
parsed = urllib.parse.urlparse(websocket_url)
|
||||
port = parsed.port
|
||||
if not port:
|
||||
port = 8099 # fallback
|
||||
|
||||
self.app = web.Application()
|
||||
|
||||
|
|
@ -48,7 +48,7 @@ class WebSocketServer():
|
|||
directories_with_routes = ['hosts']
|
||||
self.add_routes_for_directories(directories_with_routes)
|
||||
|
||||
self.websocket_thread = WebsocketServerThread(self, default_port)
|
||||
self.websocket_thread = WebsocketServerThread(self, port)
|
||||
|
||||
def add_routes_for_directories(self, directories_with_routes):
|
||||
""" Loops through selected directories to find all modules and
|
||||
|
|
@ -78,6 +78,33 @@ class WebSocketServer():
|
|||
WebSocketAsync.add_route(class_name, cls)
|
||||
sys.path.pop()
|
||||
|
||||
def call(self, func):
|
||||
log.debug("websocket.call {}".format(func))
|
||||
future = asyncio.run_coroutine_threadsafe(func,
|
||||
self.websocket_thread.loop)
|
||||
result = future.result()
|
||||
return result
|
||||
|
||||
def get_client(self):
|
||||
"""
|
||||
Return first connected client to WebSocket
|
||||
TODO implement selection by Route
|
||||
:return: <WebSocketAsync> client
|
||||
"""
|
||||
clients = WebSocketAsync.get_clients()
|
||||
client = None
|
||||
if len(clients) > 0:
|
||||
key = list(clients.keys())[0]
|
||||
client = clients.get(key)
|
||||
|
||||
return client
|
||||
|
||||
@staticmethod
|
||||
def get_instance():
|
||||
if WebSocketServer._instance is None:
|
||||
WebSocketServer()
|
||||
return WebSocketServer._instance
|
||||
|
||||
def tray_start(self):
|
||||
self.websocket_thread.start()
|
||||
|
||||
|
|
@ -124,6 +151,7 @@ class WebsocketServerThread(threading.Thread):
|
|||
self.loop = None
|
||||
self.runner = None
|
||||
self.site = None
|
||||
self.tasks = []
|
||||
|
||||
def run(self):
|
||||
self.is_running = True
|
||||
|
|
@ -169,6 +197,12 @@ class WebsocketServerThread(threading.Thread):
|
|||
periodically.
|
||||
"""
|
||||
while self.is_running:
|
||||
while self.tasks:
|
||||
task = self.tasks.pop(0)
|
||||
log.debug("waiting for task {}".format(task))
|
||||
await task
|
||||
log.debug("returned value {}".format(task.result))
|
||||
|
||||
await asyncio.sleep(0.5)
|
||||
|
||||
log.debug("Starting shutdown")
|
||||
|
|
|
|||
|
|
@ -167,6 +167,27 @@ class IntegrateHierarchyToFtrack(pyblish.api.ContextPlugin):
|
|||
self.session.rollback()
|
||||
six.reraise(tp, value, tb)
|
||||
|
||||
# Create notes.
|
||||
user = self.session.query(
|
||||
"User where username is \"{}\"".format(self.session.api_user)
|
||||
).first()
|
||||
if user:
|
||||
for comment in entity_data.get("comments", []):
|
||||
entity.create_note(comment, user)
|
||||
else:
|
||||
self.log.warning(
|
||||
"Was not able to query current User {}".format(
|
||||
self.session.api_user
|
||||
)
|
||||
)
|
||||
try:
|
||||
self.session.commit()
|
||||
except Exception:
|
||||
tp, value, tb = sys.exc_info()
|
||||
self.session.rollback()
|
||||
six.reraise(tp, value, tb)
|
||||
|
||||
# Import children.
|
||||
if 'childs' in entity_data:
|
||||
self.import_to_ftrack(
|
||||
entity_data['childs'], entity)
|
||||
|
|
|
|||
|
|
@ -81,6 +81,11 @@ class ExtractJpegEXR(pyblish.api.InstancePlugin):
|
|||
jpeg_items.append("-i {}".format(full_input_path))
|
||||
# output arguments from presets
|
||||
jpeg_items.extend(ffmpeg_args.get("output") or [])
|
||||
|
||||
# If its a movie file, we just want one frame.
|
||||
if repre["ext"] == "mov":
|
||||
jpeg_items.append("-vframes 1")
|
||||
|
||||
# output file
|
||||
jpeg_items.append(full_output_path)
|
||||
|
||||
|
|
|
|||
|
|
@ -682,6 +682,14 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin):
|
|||
instance.data.get('subsetGroup')}}
|
||||
)
|
||||
|
||||
# Update families on subset.
|
||||
families = [instance.data["family"]]
|
||||
families.extend(instance.data.get("families", []))
|
||||
io.update_many(
|
||||
{"type": "subset", "_id": io.ObjectId(subset["_id"])},
|
||||
{"$set": {"data.families": families}}
|
||||
)
|
||||
|
||||
return subset
|
||||
|
||||
def create_version(self, subset, version_number, data=None):
|
||||
|
|
|
|||
|
|
@ -718,7 +718,8 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin):
|
|||
"resolutionWidth": data.get("resolutionWidth", 1920),
|
||||
"resolutionHeight": data.get("resolutionHeight", 1080),
|
||||
"multipartExr": data.get("multipartExr", False),
|
||||
"jobBatchName": data.get("jobBatchName", "")
|
||||
"jobBatchName": data.get("jobBatchName", ""),
|
||||
"review": data.get("review", True)
|
||||
}
|
||||
|
||||
if "prerender" in instance.data["families"]:
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ class ImagePlaneLoader(api.Loader):
|
|||
|
||||
families = ["plate", "render"]
|
||||
label = "Create imagePlane on selected camera."
|
||||
representations = ["mov", "exr", "preview"]
|
||||
representations = ["mov", "exr", "preview", "png"]
|
||||
icon = "image"
|
||||
color = "orange"
|
||||
|
||||
|
|
@ -29,6 +29,8 @@ class ImagePlaneLoader(api.Loader):
|
|||
# Getting camera from selection.
|
||||
selection = pc.ls(selection=True)
|
||||
|
||||
camera = None
|
||||
|
||||
if len(selection) > 1:
|
||||
QtWidgets.QMessageBox.critical(
|
||||
None,
|
||||
|
|
@ -39,25 +41,29 @@ class ImagePlaneLoader(api.Loader):
|
|||
return
|
||||
|
||||
if len(selection) < 1:
|
||||
QtWidgets.QMessageBox.critical(
|
||||
result = QtWidgets.QMessageBox.critical(
|
||||
None,
|
||||
"Error!",
|
||||
"No camera selected.",
|
||||
QtWidgets.QMessageBox.Ok
|
||||
"No camera selected. Do you want to create a camera?",
|
||||
QtWidgets.QMessageBox.Ok,
|
||||
QtWidgets.QMessageBox.Cancel
|
||||
)
|
||||
return
|
||||
|
||||
relatives = pc.listRelatives(selection[0], shapes=True)
|
||||
if not pc.ls(relatives, type="camera"):
|
||||
QtWidgets.QMessageBox.critical(
|
||||
None,
|
||||
"Error!",
|
||||
"Selected node is not a camera.",
|
||||
QtWidgets.QMessageBox.Ok
|
||||
)
|
||||
return
|
||||
|
||||
camera = selection[0]
|
||||
if result == QtWidgets.QMessageBox.Ok:
|
||||
camera = pc.createNode("camera")
|
||||
else:
|
||||
return
|
||||
else:
|
||||
relatives = pc.listRelatives(selection[0], shapes=True)
|
||||
if pc.ls(relatives, type="camera"):
|
||||
camera = selection[0]
|
||||
else:
|
||||
QtWidgets.QMessageBox.critical(
|
||||
None,
|
||||
"Error!",
|
||||
"Selected node is not a camera.",
|
||||
QtWidgets.QMessageBox.Ok
|
||||
)
|
||||
return
|
||||
|
||||
try:
|
||||
camera.displayResolution.set(1)
|
||||
|
|
@ -81,6 +87,7 @@ class ImagePlaneLoader(api.Loader):
|
|||
image_plane_shape.frameOffset.set(1 - start_frame)
|
||||
image_plane_shape.frameIn.set(start_frame)
|
||||
image_plane_shape.frameOut.set(end_frame)
|
||||
image_plane_shape.frameCache.set(end_frame)
|
||||
image_plane_shape.useFrameExtension.set(1)
|
||||
|
||||
movie_representations = ["mov", "preview"]
|
||||
|
|
|
|||
|
|
@ -15,10 +15,12 @@ class ExtractThumbnail(pype.api.Extractor):
|
|||
order = pyblish.api.ExtractorOrder + 0.01
|
||||
label = "Extract Thumbnail"
|
||||
|
||||
families = ["review", "render.farm"]
|
||||
families = ["review"]
|
||||
hosts = ["nuke"]
|
||||
|
||||
def process(self, instance):
|
||||
if "render.farm" in instance.data["families"]:
|
||||
return
|
||||
|
||||
with anlib.maintained_selection():
|
||||
self.log.debug("instance: {}".format(instance))
|
||||
|
|
|
|||
|
|
@ -273,8 +273,6 @@ class CollectHierarchyContext(pyblish.api.ContextPlugin):
|
|||
instance.data["clipOut"] -
|
||||
instance.data["clipIn"])
|
||||
|
||||
|
||||
|
||||
self.log.debug(
|
||||
"__ instance.data[parents]: {}".format(
|
||||
instance.data["parents"]
|
||||
|
|
@ -319,6 +317,7 @@ class CollectHierarchyContext(pyblish.api.ContextPlugin):
|
|||
})
|
||||
|
||||
in_info['tasks'] = instance.data['tasks']
|
||||
in_info["comments"] = instance.data.get("comments", [])
|
||||
|
||||
parents = instance.data.get('parents', [])
|
||||
self.log.debug("__ in_info: {}".format(in_info))
|
||||
|
|
|
|||
|
|
@ -40,11 +40,12 @@ class CollectShots(api.InstancePlugin):
|
|||
data["name"] = data["subset"] + "_" + data["asset"]
|
||||
|
||||
data["label"] = (
|
||||
"{} - {} - tasks:{} - assetbuilds:{}".format(
|
||||
"{} - {} - tasks: {} - assetbuilds: {} - comments: {}".format(
|
||||
data["asset"],
|
||||
data["subset"],
|
||||
data["tasks"],
|
||||
[x["name"] for x in data.get("assetbuilds", [])]
|
||||
[x["name"] for x in data.get("assetbuilds", [])],
|
||||
len(data.get("comments", []))
|
||||
)
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ class CollectClipTagComments(api.InstancePlugin):
|
|||
for tag in instance.data["tags"]:
|
||||
if tag["name"].lower() == "comment":
|
||||
instance.data["comments"].append(
|
||||
tag.metadata().dict()["tag.note"]
|
||||
tag["metadata"]["tag.note"]
|
||||
)
|
||||
|
||||
# Find tags on the source clip.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
from avalon import api, photoshop
|
||||
from avalon import api
|
||||
from avalon.vendor import Qt
|
||||
from avalon import photoshop
|
||||
|
||||
|
||||
class CreateImage(api.Creator):
|
||||
|
|
@ -13,11 +14,12 @@ class CreateImage(api.Creator):
|
|||
groups = []
|
||||
layers = []
|
||||
create_group = False
|
||||
group_constant = photoshop.get_com_objects().constants().psLayerSet
|
||||
|
||||
stub = photoshop.stub()
|
||||
if (self.options or {}).get("useSelection"):
|
||||
multiple_instances = False
|
||||
selection = photoshop.get_selected_layers()
|
||||
|
||||
selection = stub.get_selected_layers()
|
||||
self.log.info("selection {}".format(selection))
|
||||
if len(selection) > 1:
|
||||
# Ask user whether to create one image or image per selected
|
||||
# item.
|
||||
|
|
@ -40,19 +42,18 @@ class CreateImage(api.Creator):
|
|||
|
||||
if multiple_instances:
|
||||
for item in selection:
|
||||
if item.LayerType == group_constant:
|
||||
if item.group:
|
||||
groups.append(item)
|
||||
else:
|
||||
layers.append(item)
|
||||
else:
|
||||
group = photoshop.group_selected_layers()
|
||||
group.Name = self.name
|
||||
group = stub.group_selected_layers(self.name)
|
||||
groups.append(group)
|
||||
|
||||
elif len(selection) == 1:
|
||||
# One selected item. Use group if its a LayerSet (group), else
|
||||
# create a new group.
|
||||
if selection[0].LayerType == group_constant:
|
||||
if selection[0].group:
|
||||
groups.append(selection[0])
|
||||
else:
|
||||
layers.append(selection[0])
|
||||
|
|
@ -63,16 +64,14 @@ class CreateImage(api.Creator):
|
|||
create_group = True
|
||||
|
||||
if create_group:
|
||||
group = photoshop.app().ActiveDocument.LayerSets.Add()
|
||||
group.Name = self.name
|
||||
group = stub.create_group(self.name)
|
||||
groups.append(group)
|
||||
|
||||
for layer in layers:
|
||||
photoshop.select_layers([layer])
|
||||
group = photoshop.group_selected_layers()
|
||||
group.Name = layer.Name
|
||||
stub.select_layers([layer])
|
||||
group = stub.group_selected_layers(layer.name)
|
||||
groups.append(group)
|
||||
|
||||
for group in groups:
|
||||
self.data.update({"subset": "image" + group.Name})
|
||||
photoshop.imprint(group, self.data)
|
||||
self.data.update({"subset": "image" + group.name})
|
||||
stub.imprint(group, self.data)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
from avalon import api, photoshop
|
||||
|
||||
stub = photoshop.stub()
|
||||
|
||||
|
||||
class ImageLoader(api.Loader):
|
||||
"""Load images
|
||||
|
|
@ -12,7 +14,7 @@ class ImageLoader(api.Loader):
|
|||
|
||||
def load(self, context, name=None, namespace=None, data=None):
|
||||
with photoshop.maintained_selection():
|
||||
layer = photoshop.import_smart_object(self.fname)
|
||||
layer = stub.import_smart_object(self.fname)
|
||||
|
||||
self[:] = [layer]
|
||||
|
||||
|
|
@ -28,11 +30,11 @@ class ImageLoader(api.Loader):
|
|||
layer = container.pop("layer")
|
||||
|
||||
with photoshop.maintained_selection():
|
||||
photoshop.replace_smart_object(
|
||||
stub.replace_smart_object(
|
||||
layer, api.get_representation_path(representation)
|
||||
)
|
||||
|
||||
photoshop.imprint(
|
||||
stub.imprint(
|
||||
layer, {"representation": str(representation["_id"])}
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import os
|
||||
|
||||
import pyblish.api
|
||||
|
||||
from avalon import photoshop
|
||||
|
||||
|
||||
|
|
@ -13,5 +14,5 @@ class CollectCurrentFile(pyblish.api.ContextPlugin):
|
|||
|
||||
def process(self, context):
|
||||
context.data["currentFile"] = os.path.normpath(
|
||||
photoshop.app().ActiveDocument.FullName
|
||||
photoshop.stub().get_active_document_full_name()
|
||||
).replace("\\", "/")
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import pythoncom
|
||||
|
||||
from avalon import photoshop
|
||||
|
||||
import pyblish.api
|
||||
|
||||
from avalon import photoshop
|
||||
|
||||
|
||||
class CollectInstances(pyblish.api.ContextPlugin):
|
||||
"""Gather instances by LayerSet and file metadata
|
||||
|
|
@ -27,8 +27,11 @@ class CollectInstances(pyblish.api.ContextPlugin):
|
|||
# can be.
|
||||
pythoncom.CoInitialize()
|
||||
|
||||
for layer in photoshop.get_layers_in_document():
|
||||
layer_data = photoshop.read(layer)
|
||||
stub = photoshop.stub()
|
||||
layers = stub.get_layers()
|
||||
layers_meta = stub.get_layers_metadata()
|
||||
for layer in layers:
|
||||
layer_data = stub.read(layer, layers_meta)
|
||||
|
||||
# Skip layers without metadata.
|
||||
if layer_data is None:
|
||||
|
|
@ -38,18 +41,19 @@ class CollectInstances(pyblish.api.ContextPlugin):
|
|||
if "container" in layer_data["id"]:
|
||||
continue
|
||||
|
||||
child_layers = [*layer.Layers]
|
||||
if not child_layers:
|
||||
self.log.info("%s skipped, it was empty." % layer.Name)
|
||||
continue
|
||||
# child_layers = [*layer.Layers]
|
||||
# self.log.debug("child_layers {}".format(child_layers))
|
||||
# if not child_layers:
|
||||
# self.log.info("%s skipped, it was empty." % layer.Name)
|
||||
# continue
|
||||
|
||||
instance = context.create_instance(layer.Name)
|
||||
instance = context.create_instance(layer.name)
|
||||
instance.append(layer)
|
||||
instance.data.update(layer_data)
|
||||
instance.data["families"] = self.families_mapping[
|
||||
layer_data["family"]
|
||||
]
|
||||
instance.data["publish"] = layer.Visible
|
||||
instance.data["publish"] = layer.visible
|
||||
|
||||
# Produce diagnostic message for any graphical
|
||||
# user interface interested in visualising it.
|
||||
|
|
|
|||
|
|
@ -21,35 +21,37 @@ class ExtractImage(pype.api.Extractor):
|
|||
self.log.info("Outputting image to {}".format(staging_dir))
|
||||
|
||||
# Perform extraction
|
||||
stub = photoshop.stub()
|
||||
files = {}
|
||||
with photoshop.maintained_selection():
|
||||
self.log.info("Extracting %s" % str(list(instance)))
|
||||
with photoshop.maintained_visibility():
|
||||
# Hide all other layers.
|
||||
extract_ids = [
|
||||
x.id for x in photoshop.get_layers_in_layers([instance[0]])
|
||||
]
|
||||
for layer in photoshop.get_layers_in_document():
|
||||
if layer.id not in extract_ids:
|
||||
layer.Visible = False
|
||||
extract_ids = set([ll.id for ll in stub.
|
||||
get_layers_in_layers([instance[0]])])
|
||||
|
||||
save_options = {}
|
||||
for layer in stub.get_layers():
|
||||
# limit unnecessary calls to client
|
||||
if layer.visible and layer.id not in extract_ids:
|
||||
stub.set_visible(layer.id, False)
|
||||
if not layer.visible and layer.id in extract_ids:
|
||||
stub.set_visible(layer.id, True)
|
||||
|
||||
save_options = []
|
||||
if "png" in self.formats:
|
||||
save_options["png"] = photoshop.com_objects.PNGSaveOptions()
|
||||
save_options.append('png')
|
||||
if "jpg" in self.formats:
|
||||
save_options["jpg"] = photoshop.com_objects.JPEGSaveOptions()
|
||||
save_options.append('jpg')
|
||||
|
||||
file_basename = os.path.splitext(
|
||||
photoshop.app().ActiveDocument.Name
|
||||
stub.get_active_document_name()
|
||||
)[0]
|
||||
for extension, save_option in save_options.items():
|
||||
for extension in save_options:
|
||||
_filename = "{}.{}".format(file_basename, extension)
|
||||
files[extension] = _filename
|
||||
|
||||
full_filename = os.path.join(staging_dir, _filename)
|
||||
photoshop.app().ActiveDocument.SaveAs(
|
||||
full_filename, save_option, True
|
||||
)
|
||||
stub.saveAs(full_filename, extension, True)
|
||||
|
||||
representations = []
|
||||
for extension, filename in files.items():
|
||||
|
|
|
|||
|
|
@ -13,10 +13,11 @@ class ExtractReview(pype.api.Extractor):
|
|||
families = ["review"]
|
||||
|
||||
def process(self, instance):
|
||||
|
||||
staging_dir = self.staging_dir(instance)
|
||||
self.log.info("Outputting image to {}".format(staging_dir))
|
||||
|
||||
stub = photoshop.stub()
|
||||
|
||||
layers = []
|
||||
for image_instance in instance.context:
|
||||
if image_instance.data["family"] != "image":
|
||||
|
|
@ -25,25 +26,22 @@ class ExtractReview(pype.api.Extractor):
|
|||
|
||||
# Perform extraction
|
||||
output_image = "{}.jpg".format(
|
||||
os.path.splitext(photoshop.app().ActiveDocument.Name)[0]
|
||||
os.path.splitext(stub.get_active_document_name())[0]
|
||||
)
|
||||
output_image_path = os.path.join(staging_dir, output_image)
|
||||
with photoshop.maintained_visibility():
|
||||
# Hide all other layers.
|
||||
extract_ids = [
|
||||
x.id for x in photoshop.get_layers_in_layers(layers)
|
||||
]
|
||||
for layer in photoshop.get_layers_in_document():
|
||||
if layer.id in extract_ids:
|
||||
layer.Visible = True
|
||||
else:
|
||||
layer.Visible = False
|
||||
extract_ids = set([ll.id for ll in stub.
|
||||
get_layers_in_layers(layers)])
|
||||
self.log.info("extract_ids {}".format(extract_ids))
|
||||
for layer in stub.get_layers():
|
||||
# limit unnecessary calls to client
|
||||
if layer.visible and layer.id not in extract_ids:
|
||||
stub.set_visible(layer.id, False)
|
||||
if not layer.visible and layer.id in extract_ids:
|
||||
stub.set_visible(layer.id, True)
|
||||
|
||||
photoshop.app().ActiveDocument.SaveAs(
|
||||
output_image_path,
|
||||
photoshop.com_objects.JPEGSaveOptions(),
|
||||
True
|
||||
)
|
||||
stub.saveAs(output_image_path, 'jpg', True)
|
||||
|
||||
ffmpeg_path = pype.lib.get_ffmpeg_tool_path("ffmpeg")
|
||||
|
||||
|
|
@ -66,8 +64,6 @@ class ExtractReview(pype.api.Extractor):
|
|||
]
|
||||
output = pype.lib._subprocess(args)
|
||||
|
||||
self.log.debug(output)
|
||||
|
||||
instance.data["representations"].append({
|
||||
"name": "thumbnail",
|
||||
"ext": "jpg",
|
||||
|
|
@ -75,7 +71,6 @@ class ExtractReview(pype.api.Extractor):
|
|||
"stagingDir": staging_dir,
|
||||
"tags": ["thumbnail"]
|
||||
})
|
||||
|
||||
# Generate mov.
|
||||
mov_path = os.path.join(staging_dir, "review.mov")
|
||||
args = [
|
||||
|
|
@ -86,9 +81,7 @@ class ExtractReview(pype.api.Extractor):
|
|||
mov_path
|
||||
]
|
||||
output = pype.lib._subprocess(args)
|
||||
|
||||
self.log.debug(output)
|
||||
|
||||
instance.data["representations"].append({
|
||||
"name": "mov",
|
||||
"ext": "mov",
|
||||
|
|
|
|||
|
|
@ -11,4 +11,4 @@ class ExtractSaveScene(pype.api.Extractor):
|
|||
families = ["workfile"]
|
||||
|
||||
def process(self, instance):
|
||||
photoshop.app().ActiveDocument.Save()
|
||||
photoshop.stub().save()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import pyblish.api
|
||||
from pype.action import get_errored_plugins_from_data
|
||||
from pype.lib import version_up
|
||||
|
||||
from avalon import photoshop
|
||||
|
||||
|
||||
|
|
@ -24,6 +25,6 @@ class IncrementWorkfile(pyblish.api.InstancePlugin):
|
|||
)
|
||||
|
||||
scene_path = version_up(instance.context.data["currentFile"])
|
||||
photoshop.app().ActiveDocument.SaveAs(scene_path)
|
||||
photoshop.stub().saveAs(scene_path, 'psd', True)
|
||||
|
||||
self.log.info("Incremented workfile to: {}".format(scene_path))
|
||||
|
|
|
|||
|
|
@ -23,11 +23,12 @@ class ValidateInstanceAssetRepair(pyblish.api.Action):
|
|||
|
||||
# Apply pyblish.logic to get the instances for the plug-in
|
||||
instances = pyblish.api.instances_by_plugin(failed, plugin)
|
||||
|
||||
stub = photoshop.stub()
|
||||
for instance in instances:
|
||||
data = photoshop.read(instance[0])
|
||||
data = stub.read(instance[0])
|
||||
|
||||
data["asset"] = os.environ["AVALON_ASSET"]
|
||||
photoshop.imprint(instance[0], data)
|
||||
stub.imprint(instance[0], data)
|
||||
|
||||
|
||||
class ValidateInstanceAsset(pyblish.api.InstancePlugin):
|
||||
|
|
|
|||
|
|
@ -21,13 +21,14 @@ class ValidateNamingRepair(pyblish.api.Action):
|
|||
|
||||
# Apply pyblish.logic to get the instances for the plug-in
|
||||
instances = pyblish.api.instances_by_plugin(failed, plugin)
|
||||
|
||||
stub = photoshop.stub()
|
||||
for instance in instances:
|
||||
self.log.info("validate_naming instance {}".format(instance))
|
||||
name = instance.data["name"].replace(" ", "_")
|
||||
instance[0].Name = name
|
||||
data = photoshop.read(instance[0])
|
||||
data = stub.read(instance[0])
|
||||
data["subset"] = "image" + name
|
||||
photoshop.imprint(instance[0], data)
|
||||
stub.imprint(instance[0], data)
|
||||
|
||||
return True
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 120 KiB |
|
|
@ -19,30 +19,30 @@
|
|||
"hosts": [],
|
||||
"outputs": {
|
||||
"h264": {
|
||||
"filter": {
|
||||
"families": [
|
||||
"render",
|
||||
"review",
|
||||
"ftrack"
|
||||
]
|
||||
},
|
||||
"ext": "mp4",
|
||||
"tags": [
|
||||
"burnin",
|
||||
"ftrackreview"
|
||||
],
|
||||
"ffmpeg_args": {
|
||||
"video_filters": [],
|
||||
"audio_filters": [],
|
||||
"input": [
|
||||
"-gamma 2.2"
|
||||
],
|
||||
"video_filters": [],
|
||||
"audio_filters": [],
|
||||
"output": [
|
||||
"-pix_fmt yuv420p",
|
||||
"-crf 18",
|
||||
"-intra"
|
||||
]
|
||||
},
|
||||
"tags": [
|
||||
"burnin",
|
||||
"ftrackreview"
|
||||
]
|
||||
"filter": {
|
||||
"families": [
|
||||
"render",
|
||||
"review",
|
||||
"ftrack"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,32 +1,32 @@
|
|||
{
|
||||
"blender_2.80": true,
|
||||
"blender_2.81": true,
|
||||
"blender_2.82": true,
|
||||
"blender_2.80": false,
|
||||
"blender_2.81": false,
|
||||
"blender_2.82": false,
|
||||
"blender_2.83": true,
|
||||
"celaction_local": true,
|
||||
"celaction_remote": true,
|
||||
"harmony_17": true,
|
||||
"maya_2017": true,
|
||||
"maya_2018": true,
|
||||
"maya_2017": false,
|
||||
"maya_2018": false,
|
||||
"maya_2019": true,
|
||||
"maya_2020": true,
|
||||
"nuke_10.0": true,
|
||||
"nuke_11.2": true,
|
||||
"nuke_10.0": false,
|
||||
"nuke_11.2": false,
|
||||
"nuke_11.3": true,
|
||||
"nuke_12.0": true,
|
||||
"nukex_10.0": true,
|
||||
"nukex_11.2": true,
|
||||
"nukex_10.0": false,
|
||||
"nukex_11.2": false,
|
||||
"nukex_11.3": true,
|
||||
"nukex_12.0": true,
|
||||
"nukestudio_10.0": true,
|
||||
"nukestudio_11.2": true,
|
||||
"nukestudio_10.0": false,
|
||||
"nukestudio_11.2": false,
|
||||
"nukestudio_11.3": true,
|
||||
"nukestudio_12.0": true,
|
||||
"houdini_16": true,
|
||||
"houdini_16": false,
|
||||
"houdini_16.5": false,
|
||||
"houdini_17": true,
|
||||
"houdini_17": false,
|
||||
"houdini_18": true,
|
||||
"premiere_2019": true,
|
||||
"premiere_2019": false,
|
||||
"premiere_2020": true,
|
||||
"resolve_16": true,
|
||||
"storyboardpro_7": true,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"studio_name": "",
|
||||
"studio_code": ""
|
||||
}
|
||||
88
pype/settings/defaults/system_settings/global/modules.json
Normal file
88
pype/settings/defaults/system_settings/global/modules.json
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
{
|
||||
"Avalon": {
|
||||
"AVALON_MONGO": "mongodb://localhost:2707",
|
||||
"AVALON_DB_DATA": "{PYPE_SETUP_PATH}/../mongo_db_data",
|
||||
"AVALON_THUMBNAIL_ROOT": "{PYPE_SETUP_PATH}/../avalon_thumails"
|
||||
},
|
||||
"Ftrack": {
|
||||
"enabled": true,
|
||||
"ftrack_server": "https://pype.ftrackapp.com",
|
||||
"ftrack_actions_path": [],
|
||||
"ftrack_events_path": [],
|
||||
"FTRACK_EVENTS_MONGO_DB": "pype",
|
||||
"FTRACK_EVENTS_MONGO_COL": "ftrack_events",
|
||||
"sync_to_avalon": {
|
||||
"statuses_name_change": [
|
||||
"ready",
|
||||
"not ready"
|
||||
]
|
||||
},
|
||||
"status_version_to_task": {},
|
||||
"status_update": {
|
||||
"Ready": [
|
||||
"Not Ready"
|
||||
],
|
||||
"In Progress": [
|
||||
"_any_"
|
||||
]
|
||||
},
|
||||
"intent": {
|
||||
"items": {
|
||||
"-": "-",
|
||||
"wip": "WIP",
|
||||
"final": "Final",
|
||||
"test": "Test"
|
||||
},
|
||||
"default": "-"
|
||||
}
|
||||
},
|
||||
"Rest Api": {
|
||||
"default_port": 8021,
|
||||
"exclude_ports": []
|
||||
},
|
||||
"Timers Manager": {
|
||||
"enabled": true,
|
||||
"full_time": 15.0,
|
||||
"message_time": 0.5
|
||||
},
|
||||
"Clockify": {
|
||||
"enabled": true,
|
||||
"workspace_name": "studio name"
|
||||
},
|
||||
"Deadline": {
|
||||
"enabled": true,
|
||||
"DEADLINE_REST_URL": "http://localhost:8082"
|
||||
},
|
||||
"Muster": {
|
||||
"enabled": false,
|
||||
"MUSTER_REST_URL": "",
|
||||
"templates_mapping": {
|
||||
"file_layers": 7,
|
||||
"mentalray": 2,
|
||||
"mentalray_sf": 6,
|
||||
"redshift": 55,
|
||||
"renderman": 29,
|
||||
"software": 1,
|
||||
"software_sf": 5,
|
||||
"turtle": 10,
|
||||
"vector": 4,
|
||||
"vray": 37,
|
||||
"ffmpeg": 48
|
||||
}
|
||||
},
|
||||
"Logging": {
|
||||
"enabled": true
|
||||
},
|
||||
"Adobe Communicator": {
|
||||
"enabled": true
|
||||
},
|
||||
"User setting": {
|
||||
"enabled": true
|
||||
},
|
||||
"Standalone Publish": {
|
||||
"enabled": true
|
||||
},
|
||||
"Idle Manager": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"mtoa_3.0.1": true,
|
||||
"mtoa_3.1.1": true,
|
||||
"mtoa_3.0.1": false,
|
||||
"mtoa_3.1.1": false,
|
||||
"mtoa_3.2.0": true,
|
||||
"yeti_2.1.2": true
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
{
|
||||
"item_usage": {
|
||||
"User settings": false,
|
||||
"Ftrack": true,
|
||||
"Muster": false,
|
||||
"Avalon": true,
|
||||
"Clockify": false,
|
||||
"Standalone Publish": true,
|
||||
"Logging": true,
|
||||
"Idle Manager": true,
|
||||
"Timers Manager": true,
|
||||
"Rest Api": true,
|
||||
"Adobe Communicator": true
|
||||
},
|
||||
"attributes": {
|
||||
"Rest Api": {
|
||||
"default_port": 8021,
|
||||
"exclude_ports": []
|
||||
},
|
||||
"Timers Manager": {
|
||||
"full_time": 15.0,
|
||||
"message_time": 0.5
|
||||
},
|
||||
"Clockify": {
|
||||
"workspace_name": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
336
pype/tools/settings/settings/README.md
Normal file
336
pype/tools/settings/settings/README.md
Normal file
|
|
@ -0,0 +1,336 @@
|
|||
# Creating GUI schemas
|
||||
|
||||
## Basic rules
|
||||
- configurations does not define GUI, but GUI defines configurations!
|
||||
- output is always json (yaml is not needed for anatomy templates anymore)
|
||||
- GUI schema has multiple input types, all inputs are represented by a dictionary
|
||||
- each input may have "input modifiers" (keys in dictionary) that are required or optional
|
||||
- only required modifier for all input items is key `"type"` which says what type of item it is
|
||||
- there are special keys across all inputs
|
||||
- `"is_file"` - this key is for storing pype defaults in `pype` repo
|
||||
- reasons of existence: developing new schemas does not require to create defaults manually
|
||||
- key is validated, must be once in hierarchy else it won't be possible to store pype defaults
|
||||
- `"is_group"` - define that all values under key in hierarchy will be overriden if any value is modified, this information is also stored to overrides
|
||||
- this keys is not allowed for all inputs as they may have not reason for that
|
||||
- key is validated, can be only once in hierarchy but is not required
|
||||
- currently there are `system configurations` and `project configurations`
|
||||
|
||||
## Inner schema
|
||||
- GUI schemas are huge json files, to be able to split whole configuration into multiple schema there's type `schema`
|
||||
- system configuration schemas are stored in `~/tools/settings/settings/gui_schemas/system_schema/` and project configurations in `~/tools/settings/settings/gui_schemas/projects_schema/`
|
||||
- each schema name is filename of json file except extension (without ".json")
|
||||
|
||||
### schema
|
||||
- can have only key `"children"` which is list of strings, each string should represent another schema (order matters) string represebts name of the schema
|
||||
- will just paste schemas from other schema file in order of "children" list
|
||||
|
||||
```
|
||||
{
|
||||
"type": "schema",
|
||||
"name": "my_schema_name"
|
||||
}
|
||||
```
|
||||
|
||||
## Basic Dictionary inputs
|
||||
- these inputs wraps another inputs into {key: value} relation
|
||||
|
||||
### dict-invisible
|
||||
- this input gives ability to wrap another inputs but keep them in same widget without visible divider
|
||||
- this is for example used as first input widget
|
||||
- has required keys `"key"` and `"children"`
|
||||
- "children" says what children inputs are underneath
|
||||
- "key" is key under which will be stored value from it's children
|
||||
- output is dictionary `{the "key": children values}`
|
||||
- can't have `"is_group"` key set to True as it breaks visual override showing
|
||||
```
|
||||
{
|
||||
"type": "dict-invisible",
|
||||
"key": "global",
|
||||
"children": [
|
||||
...ITEMS...
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## dict
|
||||
- this is another dictionary input wrapping more inputs but visually makes them different
|
||||
- item may be used as widget (in `list` or `dict-modifiable`)
|
||||
- in that case the only key modifier is `children` which is list of it's keys
|
||||
- USAGE: e.g. List of dictionaries where each dictionary have same structure.
|
||||
- item options if is not used as widget
|
||||
- required keys are `"key"` under which will be stored and `"label"` which will be shown in GUI
|
||||
- this input can be expandable
|
||||
- that can be set with key `"expandable"` as `True`/`False` (Default: `True`)
|
||||
- with key `"expanded"` as `True`/`False` can be set that is expanded when GUI is opened (Default: `False`)
|
||||
- it is possible to add darker background with `"highlight_content"` (Default: `False`)
|
||||
- darker background has limits of maximum applies after 3-4 nested highlighted items there is not difference in the color
|
||||
```
|
||||
# Example
|
||||
{
|
||||
"key": "applications",
|
||||
"type": "dict",
|
||||
"label": "Applications",
|
||||
"expandable": true,
|
||||
"highlight_content": true,
|
||||
"is_group": true,
|
||||
"is_file": true,
|
||||
"children": [
|
||||
...ITEMS...
|
||||
]
|
||||
}
|
||||
|
||||
# When used as widget
|
||||
{
|
||||
"type": "list",
|
||||
"key": "profiles",
|
||||
"label": "Profiles",
|
||||
"object_type": "dict-item",
|
||||
"input_modifiers": {
|
||||
"children": [
|
||||
{
|
||||
"key": "families",
|
||||
"label": "Families",
|
||||
"type": "list",
|
||||
"object_type": "text"
|
||||
}, {
|
||||
"key": "hosts",
|
||||
"label": "Hosts",
|
||||
"type": "list",
|
||||
"object_type": "text"
|
||||
}
|
||||
...
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Inputs for setting any kind of value (`Pure` inputs)
|
||||
- all these input must have defined `"key"` under which will be stored and `"label"` which will be shown next to input
|
||||
- unless they are used in different types of inputs (later) "as widgets" in that case `"key"` and `"label"` are not required as there is not place where to set them
|
||||
|
||||
### boolean
|
||||
- simple checkbox, nothing more to set
|
||||
```
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "my_boolean_key",
|
||||
"label": "Do you want to use Pype?"
|
||||
}
|
||||
```
|
||||
|
||||
### number
|
||||
- number input, can be used for both integer and float
|
||||
- key `"decimal"` defines how many decimal places will be used, 0 is for integer input (Default: `0`)
|
||||
- key `"minimum"` as minimum allowed number to enter (Default: `-99999`)
|
||||
- key `"maxium"` as maximum allowed number to enter (Default: `99999`)
|
||||
```
|
||||
{
|
||||
"type": "number",
|
||||
"key": "fps",
|
||||
"label": "Frame rate (FPS)"
|
||||
"decimal": 2,
|
||||
"minimum": 1,
|
||||
"maximum": 300000
|
||||
}
|
||||
```
|
||||
|
||||
### text
|
||||
- simple text input
|
||||
- key `"multiline"` allows to enter multiple lines of text (Default: `False`)
|
||||
- key `"placeholder"` allows to show text inside input when is empty (Default: `None`)
|
||||
|
||||
```
|
||||
{
|
||||
"type": "text",
|
||||
"key": "deadline_pool",
|
||||
"label": "Deadline pool"
|
||||
}
|
||||
```
|
||||
|
||||
### path-input
|
||||
- enhanced text input
|
||||
- does not allow to enter backslash, is auto-converted to forward slash
|
||||
- may be added another validations, like do not allow end path with slash
|
||||
- this input is implemented to add additional features to text input
|
||||
- this is meant to be used in proxy input `path-widget`
|
||||
- DO NOT USE this input in schema please
|
||||
|
||||
### raw-json
|
||||
- a little bit enhanced text input for raw json
|
||||
- has validations of json format
|
||||
- empty value is invalid value, always must be at least `{}` of `[]`
|
||||
|
||||
```
|
||||
{
|
||||
"type": "raw-json",
|
||||
"key": "profiles",
|
||||
"label": "Extract Review profiles"
|
||||
}
|
||||
```
|
||||
|
||||
## Inputs for setting value using Pure inputs
|
||||
- these inputs also have required `"key"` and `"label"`
|
||||
- they use Pure inputs "as widgets"
|
||||
|
||||
### list
|
||||
- output is list
|
||||
- items can be added and removed
|
||||
- items in list must be the same type
|
||||
- type of items is defined with key `"object_type"` where Pure input name is entered (e.g. `number`)
|
||||
- because Pure inputs may have modifiers (`number` input has `minimum`, `maximum` and `decimals`) you can set these in key `"input_modifiers"`
|
||||
|
||||
```
|
||||
{
|
||||
"type": "list",
|
||||
"object_type": "number",
|
||||
"key": "exclude_ports",
|
||||
"label": "Exclude ports",
|
||||
"input_modifiers": {
|
||||
"minimum": 1,
|
||||
"maximum": 65535
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### dict-modifiable
|
||||
- one of dictionary inputs, this is only used as value input
|
||||
- items in this input can be removed and added same way as in `list` input
|
||||
- value items in dictionary must be the same type
|
||||
- type of items is defined with key `"object_type"` where Pure input name is entered (e.g. `number`)
|
||||
- because Pure inputs may have modifiers (`number` input has `minimum`, `maximum` and `decimals`) you can set these in key `"input_modifiers"`
|
||||
- this input can be expandable
|
||||
- that can be set with key `"expandable"` as `True`/`False` (Default: `True`)
|
||||
- with key `"expanded"` as `True`/`False` can be set that is expanded when GUI is opened (Default: `False`)
|
||||
|
||||
```
|
||||
{
|
||||
"type": "dict-modifiable",
|
||||
"object_type": "number",
|
||||
"input_modifiers": {
|
||||
"minimum": 0,
|
||||
"maximum": 300
|
||||
},
|
||||
"is_group": true,
|
||||
"key": "templates_mapping",
|
||||
"label": "Muster - Templates mapping",
|
||||
"is_file": true
|
||||
}
|
||||
```
|
||||
|
||||
### path-widget
|
||||
- input for paths, use `path-input` internally
|
||||
- has 2 input modifiers `"multiplatform"` and `"multipath"`
|
||||
- `"multiplatform"` - adds `"windows"`, `"linux"` and `"darwin"` path inputs result is dictionary
|
||||
- `"multipath"` - it is possible to enter multiple paths
|
||||
- if both are enabled result is dictionary with lists
|
||||
|
||||
```
|
||||
{
|
||||
"type": "path-widget",
|
||||
"key": "ffmpeg_path",
|
||||
"label": "FFmpeg path",
|
||||
"multiplatform": true,
|
||||
"multipath": true
|
||||
}
|
||||
```
|
||||
|
||||
### list-strict
|
||||
- input for strict number of items in list
|
||||
- each child item can be different type with different possible modifiers
|
||||
- it is possible to display them in horizontal or vertical layout
|
||||
- key `"horizontal"` as `True`/`False` (Default: `True`)
|
||||
- each child may have defined `"label"` which is shown next to input
|
||||
- label does not reflect modifications or overrides (TODO)
|
||||
- children item are defined under key `"object_types"` which is list of dictionaries
|
||||
- key `"children"` is not used because is used for hierarchy validations in schema
|
||||
- USAGE: For colors, transformations, etc. Custom number and different modifiers
|
||||
give ability to define if color is HUE or RGB, 0-255, 0-1, 0-100 etc.
|
||||
|
||||
```
|
||||
{
|
||||
"type": "list-strict",
|
||||
"key": "color",
|
||||
"label": "Color",
|
||||
"object_types": [
|
||||
{
|
||||
"label": "Red",
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 255,
|
||||
"decimal": 0
|
||||
}, {
|
||||
"label": "Green",
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 255,
|
||||
"decimal": 0
|
||||
}, {
|
||||
"label": "Blue",
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 255,
|
||||
"decimal": 0
|
||||
}, {
|
||||
"label": "Alpha",
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 1,
|
||||
"decimal": 6
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Noninteractive widgets
|
||||
- have nothing to do with data
|
||||
|
||||
### label
|
||||
- add label with note or explanations
|
||||
- it is possible to use html tags inside the label
|
||||
|
||||
```
|
||||
{
|
||||
"type": "label",
|
||||
"label": "<span style=\"color:#FF0000\";>RED LABEL:</span> Normal label"
|
||||
}
|
||||
```
|
||||
|
||||
### splitter
|
||||
- visual splitter of items (more divider than splitter)
|
||||
|
||||
```
|
||||
{
|
||||
"type": "splitter"
|
||||
}
|
||||
```
|
||||
|
||||
## Proxy wrappers
|
||||
- should wraps multiple inputs only visually
|
||||
- these does not have `"key"` key and do not allow to have `"is_file"` or `"is_group"` modifiers enabled
|
||||
|
||||
### form
|
||||
- DEPRECATED
|
||||
- may be used only in `dict` and `dict-invisible` where is currently used grid layout so form is not needed
|
||||
- item is kept as still may be used in specific cases
|
||||
- wraps inputs into form look layout
|
||||
- should be used only for Pure inputs
|
||||
|
||||
```
|
||||
{
|
||||
"type": "dict-form",
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
"key": "deadline_department",
|
||||
"label": "Deadline apartment"
|
||||
}, {
|
||||
"type": "number",
|
||||
"key": "deadline_priority",
|
||||
"label": "Deadline priority"
|
||||
}, {
|
||||
...
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
|
@ -22,9 +22,7 @@
|
|||
"children": [
|
||||
{
|
||||
"type": "schema",
|
||||
"children": [
|
||||
"1_plugins_gui_schema"
|
||||
]
|
||||
"name": "1_plugins_gui_schema"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,34 +30,29 @@
|
|||
"key": "enabled",
|
||||
"label": "Enabled"
|
||||
}, {
|
||||
"type": "dict-form",
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
"key": "deadline_department",
|
||||
"label": "Deadline apartment"
|
||||
}, {
|
||||
"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": "text",
|
||||
"key": "deadline_group",
|
||||
"label": "Deadline Group"
|
||||
}, {
|
||||
"type": "number",
|
||||
"key": "deadline_chunk_size",
|
||||
"label": "Deadline Chunk size"
|
||||
}
|
||||
]
|
||||
"type": "text",
|
||||
"key": "deadline_department",
|
||||
"label": "Deadline apartment"
|
||||
}, {
|
||||
"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": "text",
|
||||
"key": "deadline_group",
|
||||
"label": "Deadline Group"
|
||||
}, {
|
||||
"type": "number",
|
||||
"key": "deadline_chunk_size",
|
||||
"label": "Deadline Chunk size"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -174,9 +169,94 @@
|
|||
"key": "enabled",
|
||||
"label": "Enabled"
|
||||
}, {
|
||||
"type": "raw-json",
|
||||
"type": "list",
|
||||
"key": "profiles",
|
||||
"label": "Profiles"
|
||||
"label": "Profiles",
|
||||
"object_type": "dict",
|
||||
"input_modifiers": {
|
||||
"children": [
|
||||
{
|
||||
"key": "families",
|
||||
"label": "Families",
|
||||
"type": "list",
|
||||
"object_type": "text"
|
||||
}, {
|
||||
"key": "hosts",
|
||||
"label": "Hosts",
|
||||
"type": "list",
|
||||
"object_type": "text"
|
||||
}, {
|
||||
"type": "splitter"
|
||||
}, {
|
||||
"key": "outputs",
|
||||
"label": "Output Definitions",
|
||||
"type": "dict-modifiable",
|
||||
"highlight_content": true,
|
||||
"object_type": "dict",
|
||||
"input_modifiers": {
|
||||
"children": [
|
||||
{
|
||||
"key": "ext",
|
||||
"label": "Output extension",
|
||||
"type": "text"
|
||||
}, {
|
||||
"key": "tags",
|
||||
"label": "Tags",
|
||||
"type": "list",
|
||||
"object_type": "text"
|
||||
}, {
|
||||
"key": "ffmpeg_args",
|
||||
"label": "FFmpeg arguments",
|
||||
"type": "dict",
|
||||
"highlight_content": true,
|
||||
"children": [
|
||||
{
|
||||
"key": "video_filters",
|
||||
"label": "Video filters",
|
||||
"type": "list",
|
||||
"object_type": "text"
|
||||
}, {
|
||||
"type": "splitter"
|
||||
}, {
|
||||
"key": "audio_filters",
|
||||
"label": "Audio filters",
|
||||
"type": "list",
|
||||
"object_type": "text"
|
||||
}, {
|
||||
"type": "splitter"
|
||||
}, {
|
||||
"key": "input",
|
||||
"label": "Input arguments",
|
||||
"type": "list",
|
||||
"object_type": "text"
|
||||
}, {
|
||||
"type": "splitter"
|
||||
}, {
|
||||
"key": "output",
|
||||
"label": "Output arguments",
|
||||
"type": "list",
|
||||
"object_type": "text"
|
||||
}
|
||||
]
|
||||
}, {
|
||||
"key": "filter",
|
||||
"label": "Additional output filtering",
|
||||
"type": "dict",
|
||||
"highlight_content": true,
|
||||
"children": [
|
||||
{
|
||||
"key": "families",
|
||||
"label": "Families",
|
||||
"type": "list",
|
||||
"object_type": "text"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}, {
|
||||
|
|
@ -531,13 +611,6 @@
|
|||
"key": "nukestudio",
|
||||
"label": "NukeStudio",
|
||||
"children": [
|
||||
{
|
||||
"type": "raw-json",
|
||||
"collapsable": true,
|
||||
"key": "filter",
|
||||
"label": "Publish GUI Filters",
|
||||
"is_file": true
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsable": true,
|
||||
|
|
@ -649,21 +722,16 @@
|
|||
"label": "ffmpeg_args",
|
||||
"children": [
|
||||
{
|
||||
"type": "dict-form",
|
||||
"children": [
|
||||
{
|
||||
"type": "list",
|
||||
"object_type": "text",
|
||||
"key": "input",
|
||||
"label": "input"
|
||||
},
|
||||
{
|
||||
"type": "list",
|
||||
"object_type": "text",
|
||||
"key": "output",
|
||||
"label": "output"
|
||||
}
|
||||
]
|
||||
"type": "list",
|
||||
"object_type": "text",
|
||||
"key": "input",
|
||||
"label": "input"
|
||||
},
|
||||
{
|
||||
"type": "list",
|
||||
"object_type": "text",
|
||||
"key": "output",
|
||||
"label": "output"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,27 +7,16 @@
|
|||
"key": "global",
|
||||
"children": [{
|
||||
"type": "schema",
|
||||
"children": [
|
||||
"1_tray_items",
|
||||
"1_applications_gui_schema",
|
||||
"1_tools_gui_schema",
|
||||
"1_intents_gui_schema"
|
||||
]
|
||||
}]
|
||||
}, {
|
||||
"type": "dict-invisible",
|
||||
"key": "muster",
|
||||
"children": [{
|
||||
"type": "dict-modifiable",
|
||||
"object_type": "number",
|
||||
"input_modifiers": {
|
||||
"minimum": 0,
|
||||
"maximum": 300
|
||||
},
|
||||
"is_group": true,
|
||||
"key": "templates_mapping",
|
||||
"label": "Muster - Templates mapping",
|
||||
"is_file": true
|
||||
"name": "1_intents_gui_schema"
|
||||
},{
|
||||
"type": "schema",
|
||||
"name": "1_modules_gui_schema"
|
||||
}, {
|
||||
"type": "schema",
|
||||
"name": "1_applications_gui_schema"
|
||||
}, {
|
||||
"type": "schema",
|
||||
"name": "1_tools_gui_schema"
|
||||
}]
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -7,138 +7,133 @@
|
|||
"is_file": true,
|
||||
"children": [
|
||||
{
|
||||
"type": "dict-form",
|
||||
"children": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "blender_2.80",
|
||||
"label": "Blender 2.80"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "blender_2.81",
|
||||
"label": "Blender 2.81"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "blender_2.82",
|
||||
"label": "Blender 2.82"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "blender_2.83",
|
||||
"label": "Blender 2.83"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "celaction_local",
|
||||
"label": "Celaction Local"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "celaction_remote",
|
||||
"label": "Celaction Remote"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "harmony_17",
|
||||
"label": "Harmony 17"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "maya_2017",
|
||||
"label": "Autodest Maya 2017"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "maya_2018",
|
||||
"label": "Autodest Maya 2018"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "maya_2019",
|
||||
"label": "Autodest Maya 2019"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "maya_2020",
|
||||
"label": "Autodest Maya 2020"
|
||||
}, {
|
||||
"key": "nuke_10.0",
|
||||
"type": "boolean",
|
||||
"label": "Nuke 10.0"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "nuke_11.2",
|
||||
"label": "Nuke 11.2"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "nuke_11.3",
|
||||
"label": "Nuke 11.3"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "nuke_12.0",
|
||||
"label": "Nuke 12.0"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "nukex_10.0",
|
||||
"label": "NukeX 10.0"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "nukex_11.2",
|
||||
"label": "NukeX 11.2"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "nukex_11.3",
|
||||
"label": "NukeX 11.3"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "nukex_12.0",
|
||||
"label": "NukeX 12.0"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "nukestudio_10.0",
|
||||
"label": "NukeStudio 10.0"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "nukestudio_11.2",
|
||||
"label": "NukeStudio 11.2"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "nukestudio_11.3",
|
||||
"label": "NukeStudio 11.3"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "nukestudio_12.0",
|
||||
"label": "NukeStudio 12.0"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "houdini_16",
|
||||
"label": "Houdini 16"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "houdini_16.5",
|
||||
"label": "Houdini 16.5"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "houdini_17",
|
||||
"label": "Houdini 17"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "houdini_18",
|
||||
"label": "Houdini 18"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "premiere_2019",
|
||||
"label": "Premiere 2019"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "premiere_2020",
|
||||
"label": "Premiere 2020"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "resolve_16",
|
||||
"label": "BM DaVinci Resolve 16"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "storyboardpro_7",
|
||||
"label": "Storyboard Pro 7"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "unreal_4.24",
|
||||
"label": "Unreal Editor 4.24"
|
||||
}
|
||||
]
|
||||
"type": "boolean",
|
||||
"key": "blender_2.80",
|
||||
"label": "Blender 2.80"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "blender_2.81",
|
||||
"label": "Blender 2.81"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "blender_2.82",
|
||||
"label": "Blender 2.82"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "blender_2.83",
|
||||
"label": "Blender 2.83"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "celaction_local",
|
||||
"label": "Celaction Local"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "celaction_remote",
|
||||
"label": "Celaction Remote"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "harmony_17",
|
||||
"label": "Harmony 17"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "maya_2017",
|
||||
"label": "Autodest Maya 2017"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "maya_2018",
|
||||
"label": "Autodest Maya 2018"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "maya_2019",
|
||||
"label": "Autodest Maya 2019"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "maya_2020",
|
||||
"label": "Autodest Maya 2020"
|
||||
}, {
|
||||
"key": "nuke_10.0",
|
||||
"type": "boolean",
|
||||
"label": "Nuke 10.0"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "nuke_11.2",
|
||||
"label": "Nuke 11.2"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "nuke_11.3",
|
||||
"label": "Nuke 11.3"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "nuke_12.0",
|
||||
"label": "Nuke 12.0"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "nukex_10.0",
|
||||
"label": "NukeX 10.0"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "nukex_11.2",
|
||||
"label": "NukeX 11.2"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "nukex_11.3",
|
||||
"label": "NukeX 11.3"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "nukex_12.0",
|
||||
"label": "NukeX 12.0"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "nukestudio_10.0",
|
||||
"label": "NukeStudio 10.0"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "nukestudio_11.2",
|
||||
"label": "NukeStudio 11.2"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "nukestudio_11.3",
|
||||
"label": "NukeStudio 11.3"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "nukestudio_12.0",
|
||||
"label": "NukeStudio 12.0"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "houdini_16",
|
||||
"label": "Houdini 16"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "houdini_16.5",
|
||||
"label": "Houdini 16.5"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "houdini_17",
|
||||
"label": "Houdini 17"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "houdini_18",
|
||||
"label": "Houdini 18"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "premiere_2019",
|
||||
"label": "Premiere 2019"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "premiere_2020",
|
||||
"label": "Premiere 2020"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "resolve_16",
|
||||
"label": "BM DaVinci Resolve 16"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "storyboardpro_7",
|
||||
"label": "Storyboard Pro 7"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "unreal_4.24",
|
||||
"label": "Unreal Editor 4.24"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,6 +72,144 @@
|
|||
"minimum": 10,
|
||||
"maximum": 100
|
||||
}
|
||||
}, {
|
||||
"type": "list-strict",
|
||||
"key": "strict_list_labels_horizontal",
|
||||
"label": "StrictList-labels-horizontal (color)",
|
||||
"object_types": [
|
||||
{
|
||||
"label": "Red",
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 255,
|
||||
"decimal": 0
|
||||
}, {
|
||||
"label": "Green",
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 255,
|
||||
"decimal": 0
|
||||
}, {
|
||||
"label": "Blue",
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 255,
|
||||
"decimal": 0
|
||||
}, {
|
||||
"label": "Alpha",
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 1,
|
||||
"decimal": 6
|
||||
}
|
||||
]
|
||||
}, {
|
||||
"type": "list-strict",
|
||||
"key": "strict_list_labels_vertical",
|
||||
"label": "StrictList-labels-vertical (color)",
|
||||
"horizontal": false,
|
||||
"object_types": [
|
||||
{
|
||||
"label": "Red",
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 255,
|
||||
"decimal": 0
|
||||
}, {
|
||||
"label": "Green",
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 255,
|
||||
"decimal": 0
|
||||
}, {
|
||||
"label": "Blue",
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 255,
|
||||
"decimal": 0
|
||||
}, {
|
||||
"label": "Alpha",
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 1,
|
||||
"decimal": 6
|
||||
}
|
||||
]
|
||||
}, {
|
||||
"type": "list-strict",
|
||||
"key": "strict_list_nolabels_horizontal",
|
||||
"label": "StrictList-nolabels-horizontal (color)",
|
||||
"object_types": [
|
||||
{
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 255,
|
||||
"decimal": 0
|
||||
}, {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 255,
|
||||
"decimal": 0
|
||||
}, {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 255,
|
||||
"decimal": 0
|
||||
}, {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 1,
|
||||
"decimal": 6
|
||||
}
|
||||
]
|
||||
}, {
|
||||
"type": "list-strict",
|
||||
"key": "strict_list_nolabels_vertical",
|
||||
"label": "StrictList-nolabels-vertical (color)",
|
||||
"horizontal": false,
|
||||
"object_types": [
|
||||
{
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 255,
|
||||
"decimal": 0
|
||||
}, {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 255,
|
||||
"decimal": 0
|
||||
}, {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 255,
|
||||
"decimal": 0
|
||||
}, {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 1,
|
||||
"decimal": 6
|
||||
}
|
||||
]
|
||||
}, {
|
||||
"type": "list",
|
||||
"key": "dict_item",
|
||||
"label": "DictItem in List",
|
||||
"object_type": "dict-item",
|
||||
"input_modifiers": {
|
||||
"children": [
|
||||
{
|
||||
"key": "families",
|
||||
"label": "Families",
|
||||
"type": "list",
|
||||
"object_type": "text"
|
||||
}, {
|
||||
"key": "hosts",
|
||||
"label": "Hosts",
|
||||
"type": "list",
|
||||
"object_type": "text"
|
||||
}
|
||||
]
|
||||
}
|
||||
}, {
|
||||
"type": "path-widget",
|
||||
"key": "single_path_input",
|
||||
|
|
@ -207,7 +345,7 @@
|
|||
"label": "Inputs with form",
|
||||
"children": [
|
||||
{
|
||||
"type": "dict-form",
|
||||
"type": "form",
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
|
|
|
|||
|
|
@ -1,20 +1,16 @@
|
|||
{
|
||||
"key": "intent",
|
||||
"type": "dict",
|
||||
"label": "Intent Setting",
|
||||
"collapsable": true,
|
||||
"is_group": true,
|
||||
"is_file": true,
|
||||
"children": [
|
||||
{
|
||||
"type": "dict-modifiable",
|
||||
"object_type": "text",
|
||||
"key": "items",
|
||||
"label": "Intent Key/Label"
|
||||
}, {
|
||||
"type": "text",
|
||||
"key": "default",
|
||||
"label": "Default intent"
|
||||
}
|
||||
]
|
||||
}
|
||||
"key": "general",
|
||||
"type": "dict",
|
||||
"label": "General",
|
||||
"collapsable": true,
|
||||
"is_file": true,
|
||||
"children": [{
|
||||
"key": "studio_name",
|
||||
"type": "text",
|
||||
"label": "Studio Name"
|
||||
},{
|
||||
"key": "studio_code",
|
||||
"type": "text",
|
||||
"label": "Studio Short Code"
|
||||
}
|
||||
]}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,283 @@
|
|||
{
|
||||
"key": "modules",
|
||||
"type": "dict",
|
||||
"label": "Modules",
|
||||
"collapsable": true,
|
||||
"is_file": true,
|
||||
"children": [{
|
||||
"type": "dict",
|
||||
"key": "Avalon",
|
||||
"label": "Avalon",
|
||||
"collapsable": true,
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
"key": "AVALON_MONGO",
|
||||
"label": "Avalon Mongo URL"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "AVALON_DB_DATA",
|
||||
"label": "Avalon Mongo Data Location"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "AVALON_THUMBNAIL_ROOT",
|
||||
"label": "Thumbnail Storage Location"
|
||||
}
|
||||
]
|
||||
},{
|
||||
"type": "dict",
|
||||
"key": "Ftrack",
|
||||
"label": "Ftrack",
|
||||
"collapsable": true,
|
||||
"checkbox_key": "enabled",
|
||||
"children": [{
|
||||
"type": "boolean",
|
||||
"key": "enabled",
|
||||
"label": "Enabled"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "ftrack_server",
|
||||
"label": "Server"
|
||||
},
|
||||
{
|
||||
"type": "label",
|
||||
"label": "Additional Ftrack paths"
|
||||
},
|
||||
{
|
||||
"type": "list",
|
||||
"key": "ftrack_actions_path",
|
||||
"label": "Action paths",
|
||||
"object_type": "text"
|
||||
},
|
||||
{
|
||||
"type": "list",
|
||||
"key": "ftrack_events_path",
|
||||
"label": "Event paths",
|
||||
"object_type": "text"
|
||||
},
|
||||
{
|
||||
"type": "label",
|
||||
"label": "Ftrack event server advanced settings"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "FTRACK_EVENTS_MONGO_DB",
|
||||
"label": "Event Mongo DB"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "FTRACK_EVENTS_MONGO_COL",
|
||||
"label": "Events Mongo Collection"
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"key": "sync_to_avalon",
|
||||
"label": "Sync to avalon",
|
||||
"children": [{
|
||||
"type": "list",
|
||||
"key": "statuses_name_change",
|
||||
"label": "Status name change",
|
||||
"object_type": "text",
|
||||
"input_modifiers": {
|
||||
"multiline": false
|
||||
}
|
||||
}]
|
||||
},
|
||||
{
|
||||
"type": "dict-modifiable",
|
||||
"key": "status_version_to_task",
|
||||
"label": "Version to Task status mapping",
|
||||
"object_type": "text"
|
||||
},
|
||||
{
|
||||
"type": "dict-modifiable",
|
||||
"key": "status_update",
|
||||
"label": "Status Updates",
|
||||
"object_type": "list",
|
||||
"input_modifiers": {
|
||||
"object_type": "text"
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "intent",
|
||||
"type": "dict-invisible",
|
||||
"children": [
|
||||
{
|
||||
"type": "dict-modifiable",
|
||||
"object_type": "text",
|
||||
"key": "items",
|
||||
"label": "Intent Key/Label"
|
||||
},
|
||||
{
|
||||
"key": "default",
|
||||
"type": "text",
|
||||
"label": "Defautl Intent"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}, {
|
||||
"type": "dict",
|
||||
"key": "Rest Api",
|
||||
"label": "Rest Api",
|
||||
"collapsable": true,
|
||||
"children": [{
|
||||
"type": "number",
|
||||
"key": "default_port",
|
||||
"label": "Default Port",
|
||||
"minimum": 1,
|
||||
"maximum": 65535
|
||||
},
|
||||
{
|
||||
"type": "list",
|
||||
"object_type": "number",
|
||||
"key": "exclude_ports",
|
||||
"label": "Exclude ports",
|
||||
"input_modifiers": {
|
||||
"minimum": 1,
|
||||
"maximum": 65535
|
||||
}
|
||||
}
|
||||
]
|
||||
}, {
|
||||
"type": "dict",
|
||||
"key": "Timers Manager",
|
||||
"label": "Timers Manager",
|
||||
"collapsable": true,
|
||||
"checkbox_key": "enabled",
|
||||
"children": [{
|
||||
"type": "boolean",
|
||||
"key": "enabled",
|
||||
"label": "Enabled"
|
||||
},
|
||||
{
|
||||
"type": "number",
|
||||
"decimal": 2,
|
||||
"key": "full_time",
|
||||
"label": "Max idle time"
|
||||
}, {
|
||||
"type": "number",
|
||||
"decimal": 2,
|
||||
"key": "message_time",
|
||||
"label": "When dialog will show"
|
||||
}
|
||||
]
|
||||
}, {
|
||||
"type": "dict",
|
||||
"key": "Clockify",
|
||||
"label": "Clockify",
|
||||
"collapsable": true,
|
||||
"checkbox_key": "enabled",
|
||||
"children": [{
|
||||
"type": "boolean",
|
||||
"key": "enabled",
|
||||
"label": "Enabled"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "workspace_name",
|
||||
"label": "Workspace name"
|
||||
}
|
||||
]
|
||||
}, {
|
||||
"type": "dict",
|
||||
"key": "Deadline",
|
||||
"label": "Deadline",
|
||||
"collapsable": true,
|
||||
"checkbox_key": "enabled",
|
||||
"children": [{
|
||||
"type": "boolean",
|
||||
"key": "enabled",
|
||||
"label": "Enabled"
|
||||
},{
|
||||
"type": "text",
|
||||
"key": "DEADLINE_REST_URL",
|
||||
"label": "Deadline Resl URL"
|
||||
}]
|
||||
}, {
|
||||
"type": "dict",
|
||||
"key": "Muster",
|
||||
"label": "Muster",
|
||||
"collapsable": true,
|
||||
"checkbox_key": "enabled",
|
||||
"children": [{
|
||||
"type": "boolean",
|
||||
"key": "enabled",
|
||||
"label": "Enabled"
|
||||
},{
|
||||
"type": "text",
|
||||
"key": "MUSTER_REST_URL",
|
||||
"label": "Muster Resl URL"
|
||||
},{
|
||||
"type": "dict-modifiable",
|
||||
"object_type": "number",
|
||||
"input_modifiers": {
|
||||
"minimum": 0,
|
||||
"maximum": 300
|
||||
},
|
||||
"is_group": true,
|
||||
"key": "templates_mapping",
|
||||
"label": "Templates mapping",
|
||||
"is_file": true
|
||||
}]
|
||||
}, {
|
||||
"type": "dict",
|
||||
"key": "Logging",
|
||||
"label": "Logging",
|
||||
"collapsable": true,
|
||||
"checkbox_key": "enabled",
|
||||
"children": [{
|
||||
"type": "boolean",
|
||||
"key": "enabled",
|
||||
"label": "Enabled"
|
||||
}]
|
||||
}, {
|
||||
"type": "dict",
|
||||
"key": "Adobe Communicator",
|
||||
"label": "Adobe Communicator",
|
||||
"collapsable": true,
|
||||
"checkbox_key": "enabled",
|
||||
"children": [{
|
||||
"type": "boolean",
|
||||
"key": "enabled",
|
||||
"label": "Enabled"
|
||||
}]
|
||||
}, {
|
||||
"type": "dict",
|
||||
"key": "User setting",
|
||||
"label": "User setting",
|
||||
"collapsable": true,
|
||||
"checkbox_key": "enabled",
|
||||
"children": [{
|
||||
"type": "boolean",
|
||||
"key": "enabled",
|
||||
"label": "Enabled"
|
||||
}]
|
||||
}, {
|
||||
"type": "dict",
|
||||
"key": "Standalone Publish",
|
||||
"label": "Standalone Publish",
|
||||
"collapsable": true,
|
||||
"checkbox_key": "enabled",
|
||||
"children": [{
|
||||
"type": "boolean",
|
||||
"key": "enabled",
|
||||
"label": "Enabled"
|
||||
}]
|
||||
}, {
|
||||
"type": "dict",
|
||||
"key": "Idle Manager",
|
||||
"label": "Idle Manager",
|
||||
"collapsable": true,
|
||||
"checkbox_key": "enabled",
|
||||
"children": [{
|
||||
"type": "boolean",
|
||||
"key": "enabled",
|
||||
"label": "Enabled"
|
||||
}]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -7,26 +7,21 @@
|
|||
"is_file": true,
|
||||
"children": [
|
||||
{
|
||||
"type": "dict-form",
|
||||
"children": [
|
||||
{
|
||||
"key": "mtoa_3.0.1",
|
||||
"type": "boolean",
|
||||
"label": "Arnold Maya 3.0.1"
|
||||
}, {
|
||||
"key": "mtoa_3.1.1",
|
||||
"type": "boolean",
|
||||
"label": "Arnold Maya 3.1.1"
|
||||
}, {
|
||||
"key": "mtoa_3.2.0",
|
||||
"type": "boolean",
|
||||
"label": "Arnold Maya 3.2.0"
|
||||
}, {
|
||||
"key": "yeti_2.1.2",
|
||||
"type": "boolean",
|
||||
"label": "Yeti 2.1.2"
|
||||
}
|
||||
]
|
||||
"key": "mtoa_3.0.1",
|
||||
"type": "boolean",
|
||||
"label": "Arnold Maya 3.0.1"
|
||||
}, {
|
||||
"key": "mtoa_3.1.1",
|
||||
"type": "boolean",
|
||||
"label": "Arnold Maya 3.1.1"
|
||||
}, {
|
||||
"key": "mtoa_3.2.0",
|
||||
"type": "boolean",
|
||||
"label": "Arnold Maya 3.2.0"
|
||||
}, {
|
||||
"key": "yeti_2.1.2",
|
||||
"type": "boolean",
|
||||
"label": "Yeti 2.1.2"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,125 +0,0 @@
|
|||
{
|
||||
"key": "tray_modules",
|
||||
"type": "dict",
|
||||
"label": "Modules",
|
||||
"collapsable": true,
|
||||
"is_group": true,
|
||||
"is_file": true,
|
||||
"children": [
|
||||
{
|
||||
"key": "item_usage",
|
||||
"type": "dict-invisible",
|
||||
"children": [
|
||||
{
|
||||
"type": "dict-form",
|
||||
"children": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "User settings",
|
||||
"label": "User settings"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "Ftrack",
|
||||
"label": "Ftrack"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "Muster",
|
||||
"label": "Muster"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "Avalon",
|
||||
"label": "Avalon"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "Clockify",
|
||||
"label": "Clockify"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "Standalone Publish",
|
||||
"label": "Standalone Publish"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "Logging",
|
||||
"label": "Logging"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "Idle Manager",
|
||||
"label": "Idle Manager"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "Timers Manager",
|
||||
"label": "Timers Manager"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "Rest Api",
|
||||
"label": "Rest Api"
|
||||
}, {
|
||||
"type": "boolean",
|
||||
"key": "Adobe Communicator",
|
||||
"label": "Adobe Communicator"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}, {
|
||||
"key": "attributes",
|
||||
"type": "dict-invisible",
|
||||
"children": [
|
||||
{
|
||||
"type": "dict",
|
||||
"key": "Rest Api",
|
||||
"label": "Rest Api",
|
||||
"collapsable": true,
|
||||
"children": [
|
||||
{
|
||||
"type": "number",
|
||||
"key": "default_port",
|
||||
"label": "Default Port",
|
||||
"minimum": 1,
|
||||
"maximum": 65535
|
||||
}, {
|
||||
"type": "list",
|
||||
"object_type": "number",
|
||||
"key": "exclude_ports",
|
||||
"label": "Exclude ports",
|
||||
"input_modifiers": {
|
||||
"minimum": 1,
|
||||
"maximum": 65535
|
||||
}
|
||||
}
|
||||
]
|
||||
}, {
|
||||
"type": "dict",
|
||||
"key": "Timers Manager",
|
||||
"label": "Timers Manager",
|
||||
"collapsable": true,
|
||||
"children": [
|
||||
{
|
||||
"type": "number",
|
||||
"decimal": 2,
|
||||
"key": "full_time",
|
||||
"label": "Max idle time"
|
||||
}, {
|
||||
"type": "number",
|
||||
"decimal": 2,
|
||||
"key": "message_time",
|
||||
"label": "When dialog will show"
|
||||
}
|
||||
]
|
||||
}, {
|
||||
"type": "dict",
|
||||
"key": "Clockify",
|
||||
"label": "Clockify",
|
||||
"collapsable": true,
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
"key": "workspace_name",
|
||||
"label": "Workspace name"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -152,6 +152,12 @@ QPushButton[btn-type="expand-toggle"] {
|
|||
background: #141a1f;
|
||||
}
|
||||
|
||||
#DictAsWidgetBody{
|
||||
background: transparent;
|
||||
border: 2px solid #cccccc;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
#SplitterItem {
|
||||
background-color: #1d272f;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@ class SystemWidget(QtWidgets.QWidget):
|
|||
is_overidable = False
|
||||
has_studio_override = _has_studio_override = False
|
||||
is_overriden = _is_overriden = False
|
||||
as_widget = _as_widget = False
|
||||
any_parent_as_widget = _any_parent_as_widget = False
|
||||
is_group = _is_group = False
|
||||
any_parent_is_group = _any_parent_is_group = False
|
||||
|
||||
|
|
@ -266,6 +268,10 @@ class SystemWidget(QtWidgets.QWidget):
|
|||
self.input_fields.append(item)
|
||||
self.content_layout.addWidget(item)
|
||||
|
||||
# Add spacer to stretch children guis
|
||||
spacer = QtWidgets.QWidget(self.content_widget)
|
||||
self.content_layout.addWidget(spacer, 1)
|
||||
|
||||
|
||||
class ProjectListView(QtWidgets.QListView):
|
||||
left_mouse_released_at = QtCore.Signal(QtCore.QModelIndex)
|
||||
|
|
@ -396,6 +402,8 @@ class ProjectListWidget(QtWidgets.QWidget):
|
|||
class ProjectWidget(QtWidgets.QWidget):
|
||||
has_studio_override = _has_studio_override = False
|
||||
is_overriden = _is_overriden = False
|
||||
as_widget = _as_widget = False
|
||||
any_parent_as_widget = _any_parent_as_widget = False
|
||||
is_group = _is_group = False
|
||||
any_parent_is_group = _any_parent_is_group = False
|
||||
|
||||
|
|
@ -526,6 +534,10 @@ class ProjectWidget(QtWidgets.QWidget):
|
|||
self.input_fields.append(item)
|
||||
self.content_layout.addWidget(item)
|
||||
|
||||
# Add spacer to stretch children guis
|
||||
spacer = QtWidgets.QWidget(self.content_widget)
|
||||
self.content_layout.addWidget(spacer, 1)
|
||||
|
||||
def _on_project_change(self):
|
||||
project_name = self.project_list_widget.project_name()
|
||||
if project_name is None:
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -69,12 +69,11 @@ def _fill_inner_schemas(schema_data, schema_collection):
|
|||
new_children.append(new_child)
|
||||
continue
|
||||
|
||||
for schema_name in child["children"]:
|
||||
new_child = _fill_inner_schemas(
|
||||
schema_collection[schema_name],
|
||||
schema_collection
|
||||
)
|
||||
new_children.append(new_child)
|
||||
new_child = _fill_inner_schemas(
|
||||
schema_collection[child["name"]],
|
||||
schema_collection
|
||||
)
|
||||
new_children.append(new_child)
|
||||
|
||||
schema_data["children"] = new_children
|
||||
return schema_data
|
||||
|
|
|
|||
|
|
@ -226,3 +226,56 @@ class UnsavedChangesDialog(QtWidgets.QDialog):
|
|||
|
||||
def on_discard_pressed(self):
|
||||
self.done(2)
|
||||
|
||||
|
||||
class SpacerWidget(QtWidgets.QWidget):
|
||||
def __init__(self, parent=None):
|
||||
super(SpacerWidget, self).__init__(parent)
|
||||
self.setAttribute(QtCore.Qt.WA_TranslucentBackground)
|
||||
|
||||
|
||||
class GridLabelWidget(QtWidgets.QWidget):
|
||||
def __init__(self, label, parent=None):
|
||||
super(GridLabelWidget, self).__init__(parent)
|
||||
|
||||
self.input_field = None
|
||||
|
||||
self.properties = {}
|
||||
|
||||
layout = QtWidgets.QVBoxLayout(self)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setSpacing(0)
|
||||
|
||||
label_proxy = QtWidgets.QWidget(self)
|
||||
label_proxy_layout = QtWidgets.QHBoxLayout(label_proxy)
|
||||
label_proxy_layout.setContentsMargins(0, 0, 0, 0)
|
||||
label_proxy_layout.setSpacing(0)
|
||||
|
||||
label_widget = QtWidgets.QLabel(label, label_proxy)
|
||||
spacer_widget_h = SpacerWidget(label_proxy)
|
||||
label_proxy_layout.addWidget(
|
||||
spacer_widget_h, 0, alignment=QtCore.Qt.AlignRight
|
||||
)
|
||||
label_proxy_layout.addWidget(
|
||||
label_widget, 0, alignment=QtCore.Qt.AlignRight
|
||||
)
|
||||
|
||||
spacer_widget_v = SpacerWidget(self)
|
||||
|
||||
layout.addWidget(label_proxy, 0)
|
||||
layout.addWidget(spacer_widget_v, 1)
|
||||
|
||||
self.label_widget = label_widget
|
||||
|
||||
def setProperty(self, name, value):
|
||||
cur_value = self.properties.get(name)
|
||||
if cur_value == value:
|
||||
return
|
||||
|
||||
self.label_widget.setProperty(name, value)
|
||||
self.label_widget.style().polish(self.label_widget)
|
||||
|
||||
def mouseReleaseEvent(self, event):
|
||||
if self.input_field:
|
||||
return self.input_field.show_actions_menu(event)
|
||||
return super(GridLabelWidget, self).mouseReleaseEvent(event)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue