mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
Merge pull request #1571 from pypeclub/feature/895-add-option-to-define-paht-to-workfile-template
This commit is contained in:
commit
17d8409cd1
32 changed files with 754 additions and 105 deletions
|
|
@ -8,8 +8,19 @@ class AddLastWorkfileToLaunchArgs(PreLaunchHook):
|
|||
This is not possible to do for all applications the same way.
|
||||
"""
|
||||
|
||||
order = 0
|
||||
app_groups = ["maya", "nuke", "nukex", "hiero", "nukestudio"]
|
||||
# Execute after workfile template copy
|
||||
order = 10
|
||||
app_groups = [
|
||||
"maya",
|
||||
"nuke",
|
||||
"nukex",
|
||||
"hiero",
|
||||
"nukestudio",
|
||||
"blender",
|
||||
"photoshop",
|
||||
"tvpaint",
|
||||
"afftereffects"
|
||||
]
|
||||
|
||||
def execute(self):
|
||||
if not self.data.get("start_last_workfile"):
|
||||
|
|
|
|||
127
openpype/hooks/pre_copy_template_workfile.py
Normal file
127
openpype/hooks/pre_copy_template_workfile.py
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
import os
|
||||
import shutil
|
||||
from openpype.lib import (
|
||||
PreLaunchHook,
|
||||
get_custom_workfile_template_by_context,
|
||||
get_custom_workfile_template_by_string_context
|
||||
)
|
||||
from openpype.settings import get_project_settings
|
||||
|
||||
|
||||
class CopyTemplateWorkfile(PreLaunchHook):
|
||||
"""Copy workfile template.
|
||||
|
||||
This is not possible to do for all applications the same way.
|
||||
|
||||
Prelaunch hook works only if last workfile leads to not existing file.
|
||||
- That is possible only if it's first version.
|
||||
"""
|
||||
|
||||
# Before `AddLastWorkfileToLaunchArgs`
|
||||
order = 0
|
||||
app_groups = ["blender", "photoshop", "tvpaint", "afftereffects"]
|
||||
|
||||
def execute(self):
|
||||
"""Check if can copy template for context and do it if possible.
|
||||
|
||||
First check if host for current project should create first workfile.
|
||||
Second check is if template is reachable and can be copied.
|
||||
|
||||
Args:
|
||||
last_workfile(str): Path where template will be copied.
|
||||
|
||||
Returns:
|
||||
None: This is a void method.
|
||||
"""
|
||||
|
||||
last_workfile = self.data.get("last_workfile_path")
|
||||
if not last_workfile:
|
||||
self.log.warning((
|
||||
"Last workfile was not collected."
|
||||
" Can't add it to launch arguments or determine if should"
|
||||
" copy template."
|
||||
))
|
||||
return
|
||||
|
||||
if os.path.exists(last_workfile):
|
||||
self.log.debug("Last workfile exits. Skipping {} process.".format(
|
||||
self.__class__.__name__
|
||||
))
|
||||
return
|
||||
|
||||
self.log.info("Last workfile does not exits.")
|
||||
|
||||
project_name = self.data["project_name"]
|
||||
asset_name = self.data["asset_name"]
|
||||
task_name = self.data["task_name"]
|
||||
|
||||
project_settings = get_project_settings(project_name)
|
||||
host_settings = project_settings[self.application.host_name]
|
||||
|
||||
workfile_builder_settings = host_settings.get("workfile_builder")
|
||||
if not workfile_builder_settings:
|
||||
# TODO remove warning when deprecated
|
||||
self.log.warning((
|
||||
"Seems like old version of settings is used."
|
||||
" Can't access custom templates in host \"{}\"."
|
||||
).format(self.application.full_label))
|
||||
return
|
||||
|
||||
if not workfile_builder_settings["create_first_version"]:
|
||||
self.log.info((
|
||||
"Project \"{}\" has turned off to create first workfile for"
|
||||
" application \"{}\""
|
||||
).format(project_name, self.application.full_label))
|
||||
return
|
||||
|
||||
# Backwards compatibility
|
||||
template_profiles = workfile_builder_settings.get("custom_templates")
|
||||
if not template_profiles:
|
||||
self.log.info(
|
||||
"Custom templates are not filled. Skipping template copy."
|
||||
)
|
||||
return
|
||||
|
||||
project_doc = self.data.get("project_doc")
|
||||
asset_doc = self.data.get("asset_doc")
|
||||
anatomy = self.data.get("anatomy")
|
||||
if project_doc and asset_doc:
|
||||
self.log.debug("Started filtering of custom template paths.")
|
||||
template_path = get_custom_workfile_template_by_context(
|
||||
template_profiles, project_doc, asset_doc, task_name, anatomy
|
||||
)
|
||||
|
||||
else:
|
||||
self.log.warning((
|
||||
"Global data collection probably did not execute."
|
||||
" Using backup solution."
|
||||
))
|
||||
dbcon = self.data.get("dbcon")
|
||||
template_path = get_custom_workfile_template_by_string_context(
|
||||
template_profiles, project_name, asset_name, task_name,
|
||||
dbcon, anatomy
|
||||
)
|
||||
|
||||
if not template_path:
|
||||
self.log.info(
|
||||
"Registered custom templates didn't match current context."
|
||||
)
|
||||
return
|
||||
|
||||
if not os.path.exists(template_path):
|
||||
self.log.warning(
|
||||
"Couldn't find workfile template file \"{}\"".format(
|
||||
template_path
|
||||
)
|
||||
)
|
||||
return
|
||||
|
||||
self.log.info(
|
||||
f"Creating workfile from template: \"{template_path}\""
|
||||
)
|
||||
|
||||
# Copy template workfile to new destinantion
|
||||
shutil.copy2(
|
||||
os.path.normpath(template_path),
|
||||
os.path.normpath(last_workfile)
|
||||
)
|
||||
|
|
@ -80,7 +80,7 @@ def install():
|
|||
# Set context settings.
|
||||
nuke.addOnCreate(workfile_settings.set_context_settings, nodeClass="Root")
|
||||
nuke.addOnCreate(workfile_settings.set_favorites, nodeClass="Root")
|
||||
nuke.addOnCreate(lib.open_last_workfile, nodeClass="Root")
|
||||
nuke.addOnCreate(lib.process_workfile_builder, nodeClass="Root")
|
||||
nuke.addOnCreate(lib.launch_workfiles_app, nodeClass="Root")
|
||||
menu.install()
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ from avalon.nuke import (
|
|||
from openpype.api import (
|
||||
Logger,
|
||||
Anatomy,
|
||||
BuildWorkfile,
|
||||
get_version_from_path,
|
||||
get_anatomy_settings,
|
||||
get_hierarchy,
|
||||
|
|
@ -1641,23 +1642,69 @@ def launch_workfiles_app():
|
|||
workfiles.show(os.environ["AVALON_WORKDIR"])
|
||||
|
||||
|
||||
def open_last_workfile():
|
||||
# get state from settings
|
||||
open_last_version = get_current_project_settings()["nuke"].get(
|
||||
"general", {}).get("create_initial_workfile")
|
||||
def process_workfile_builder():
|
||||
from openpype.lib import (
|
||||
env_value_to_bool,
|
||||
get_custom_workfile_template
|
||||
)
|
||||
|
||||
# get state from settings
|
||||
workfile_builder = get_current_project_settings()["nuke"].get(
|
||||
"workfile_builder", {})
|
||||
|
||||
# get all imortant settings
|
||||
openlv_on = env_value_to_bool(
|
||||
env_key="AVALON_OPEN_LAST_WORKFILE",
|
||||
default=None)
|
||||
|
||||
# get settings
|
||||
createfv_on = workfile_builder.get("create_first_version") or None
|
||||
custom_templates = workfile_builder.get("custom_templates") or None
|
||||
builder_on = workfile_builder.get("builder_on_start") or None
|
||||
|
||||
log.info("Opening last workfile...")
|
||||
last_workfile_path = os.environ.get("AVALON_LAST_WORKFILE")
|
||||
|
||||
if not os.path.exists(last_workfile_path):
|
||||
# return if none is defined
|
||||
if not open_last_version:
|
||||
return
|
||||
# generate first version in file not existing and feature is enabled
|
||||
if createfv_on and not os.path.exists(last_workfile_path):
|
||||
# get custom template path if any
|
||||
custom_template_path = get_custom_workfile_template(
|
||||
custom_templates
|
||||
)
|
||||
|
||||
# if custom template is defined
|
||||
if custom_template_path:
|
||||
log.info("Adding nodes from `{}`...".format(
|
||||
custom_template_path
|
||||
))
|
||||
try:
|
||||
# import nodes into current script
|
||||
nuke.nodePaste(custom_template_path)
|
||||
except RuntimeError:
|
||||
raise RuntimeError((
|
||||
"Template defined for project: {} is not working. "
|
||||
"Talk to your manager for an advise").format(
|
||||
custom_template_path))
|
||||
|
||||
# if builder at start is defined
|
||||
if builder_on:
|
||||
log.info("Building nodes from presets...")
|
||||
# build nodes by defined presets
|
||||
BuildWorkfile().process()
|
||||
|
||||
log.info("Saving script as version `{}`...".format(
|
||||
last_workfile_path
|
||||
))
|
||||
# safe file as version
|
||||
save_file(last_workfile_path)
|
||||
else:
|
||||
# to avoid looping of the callback, remove it!
|
||||
nuke.removeOnCreate(open_last_workfile, nodeClass="Root")
|
||||
return
|
||||
|
||||
# open workfile
|
||||
open_file(last_workfile_path)
|
||||
# skip opening of last version if it is not enabled
|
||||
if not openlv_on or not os.path.exists(last_workfile_path):
|
||||
return
|
||||
|
||||
# to avoid looping of the callback, remove it!
|
||||
nuke.removeOnCreate(process_workfile_builder, nodeClass="Root")
|
||||
|
||||
log.info("Opening last workfile...")
|
||||
# open workfile
|
||||
open_file(last_workfile_path)
|
||||
|
|
|
|||
|
|
@ -34,20 +34,6 @@ class TvpaintPrelaunchHook(PreLaunchHook):
|
|||
"run", self.launch_script_path(), executable_path
|
||||
)
|
||||
|
||||
# Add workfile to launch arguments
|
||||
workfile_path = self.workfile_path()
|
||||
if workfile_path:
|
||||
new_launch_args.append(workfile_path)
|
||||
|
||||
# How to create new command line
|
||||
# if platform.system().lower() == "windows":
|
||||
# new_launch_args = [
|
||||
# "cmd.exe",
|
||||
# "/c",
|
||||
# "Call cmd.exe /k",
|
||||
# *new_launch_args
|
||||
# ]
|
||||
|
||||
# Append as whole list as these areguments should not be separated
|
||||
self.launch_context.launch_args.append(new_launch_args)
|
||||
|
||||
|
|
@ -64,38 +50,4 @@ class TvpaintPrelaunchHook(PreLaunchHook):
|
|||
"tvpaint",
|
||||
"launch_script.py"
|
||||
)
|
||||
return script_path
|
||||
|
||||
def workfile_path(self):
|
||||
workfile_path = self.data["last_workfile_path"]
|
||||
|
||||
# copy workfile from template if doesnt exist any on path
|
||||
if not os.path.exists(workfile_path):
|
||||
# TODO add ability to set different template workfile path via
|
||||
# settings
|
||||
pype_dir = os.path.dirname(os.path.abspath(tvpaint.__file__))
|
||||
template_path = os.path.join(
|
||||
pype_dir, "resources", "template.tvpp"
|
||||
)
|
||||
|
||||
if not os.path.exists(template_path):
|
||||
self.log.warning(
|
||||
"Couldn't find workfile template file in {}".format(
|
||||
template_path
|
||||
)
|
||||
)
|
||||
return
|
||||
|
||||
self.log.info(
|
||||
f"Creating workfile from template: \"{template_path}\""
|
||||
)
|
||||
|
||||
# Copy template workfile to new destinantion
|
||||
shutil.copy2(
|
||||
os.path.normpath(template_path),
|
||||
os.path.normpath(workfile_path)
|
||||
)
|
||||
|
||||
self.log.info(f"Workfile to open: \"{workfile_path}\"")
|
||||
|
||||
return workfile_path
|
||||
return script_path
|
||||
|
|
@ -81,7 +81,13 @@ from .avalon_context import (
|
|||
|
||||
get_creator_by_name,
|
||||
|
||||
change_timer_to_current_context
|
||||
get_custom_workfile_template,
|
||||
|
||||
change_timer_to_current_context,
|
||||
|
||||
get_custom_workfile_template_by_context,
|
||||
get_custom_workfile_template_by_string_context,
|
||||
get_custom_workfile_template
|
||||
)
|
||||
|
||||
from .local_settings import (
|
||||
|
|
@ -192,6 +198,10 @@ __all__ = [
|
|||
|
||||
"change_timer_to_current_context",
|
||||
|
||||
"get_custom_workfile_template_by_context",
|
||||
"get_custom_workfile_template_by_string_context",
|
||||
"get_custom_workfile_template",
|
||||
|
||||
"IniSettingRegistry",
|
||||
"JSONSettingRegistry",
|
||||
"OpenPypeSecureRegistry",
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import os
|
|||
import json
|
||||
import re
|
||||
import copy
|
||||
import platform
|
||||
import logging
|
||||
import collections
|
||||
import functools
|
||||
|
|
@ -755,18 +756,22 @@ class BuildWorkfile:
|
|||
"""
|
||||
host_name = avalon.api.registered_host().__name__.rsplit(".", 1)[-1]
|
||||
presets = get_project_settings(avalon.io.Session["AVALON_PROJECT"])
|
||||
|
||||
# Get presets for host
|
||||
build_presets = (
|
||||
presets.get(host_name, {})
|
||||
.get("workfile_build")
|
||||
.get("profiles")
|
||||
)
|
||||
if not build_presets:
|
||||
wb_settings = presets.get(host_name, {}).get("workfile_builder")
|
||||
|
||||
if not wb_settings:
|
||||
# backward compatibility
|
||||
wb_settings = presets.get(host_name, {}).get("workfile_build")
|
||||
|
||||
builder_presets = wb_settings.get("profiles")
|
||||
|
||||
if not builder_presets:
|
||||
return
|
||||
|
||||
task_name_low = task_name.lower()
|
||||
per_task_preset = None
|
||||
for preset in build_presets:
|
||||
for preset in builder_presets:
|
||||
preset_tasks = preset.get("tasks") or []
|
||||
preset_tasks_low = [task.lower() for task in preset_tasks]
|
||||
if task_name_low in preset_tasks_low:
|
||||
|
|
@ -1266,3 +1271,201 @@ def change_timer_to_current_context():
|
|||
}
|
||||
|
||||
requests.post(rest_api_url, json=data)
|
||||
|
||||
|
||||
def _get_task_context_data_for_anatomy(
|
||||
project_doc, asset_doc, task_name, anatomy=None
|
||||
):
|
||||
"""Prepare Task context for anatomy data.
|
||||
|
||||
WARNING: this data structure is currently used only in workfile templates.
|
||||
Key "task" is currently in rest of pipeline used as string with task
|
||||
name.
|
||||
|
||||
Args:
|
||||
project_doc (dict): Project document with available "name" and
|
||||
"data.code" keys.
|
||||
asset_doc (dict): Asset document from MongoDB.
|
||||
task_name (str): Name of context task.
|
||||
anatomy (Anatomy): Optionally Anatomy for passed project name can be
|
||||
passed as Anatomy creation may be slow.
|
||||
|
||||
Returns:
|
||||
dict: With Anatomy context data.
|
||||
"""
|
||||
|
||||
if anatomy is None:
|
||||
anatomy = Anatomy(project_doc["name"])
|
||||
|
||||
asset_name = asset_doc["name"]
|
||||
project_task_types = anatomy["tasks"]
|
||||
|
||||
# get relevant task type from asset doc
|
||||
assert task_name in asset_doc["data"]["tasks"], (
|
||||
"Task name \"{}\" not found on asset \"{}\"".format(
|
||||
task_name, asset_name
|
||||
)
|
||||
)
|
||||
|
||||
task_type = asset_doc["data"]["tasks"][task_name].get("type")
|
||||
|
||||
assert task_type, (
|
||||
"Task name \"{}\" on asset \"{}\" does not have specified task type."
|
||||
).format(asset_name, task_name)
|
||||
|
||||
# get short name for task type defined in default anatomy settings
|
||||
project_task_type_data = project_task_types.get(task_type)
|
||||
assert project_task_type_data, (
|
||||
"Something went wrong. Default anatomy tasks are not holding"
|
||||
"requested task type: `{}`".format(task_type)
|
||||
)
|
||||
|
||||
return {
|
||||
"project": {
|
||||
"name": project_doc["name"],
|
||||
"code": project_doc["data"].get("code")
|
||||
},
|
||||
"asset": asset_name,
|
||||
"task": {
|
||||
"name": task_name,
|
||||
"type": task_type,
|
||||
"short_name": project_task_type_data["short_name"]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def get_custom_workfile_template_by_context(
|
||||
template_profiles, project_doc, asset_doc, task_name, anatomy=None
|
||||
):
|
||||
"""Filter and fill workfile template profiles by passed context.
|
||||
|
||||
It is expected that passed argument are already queried documents of
|
||||
project and asset as parents of processing task name.
|
||||
|
||||
Existence of formatted path is not validated.
|
||||
|
||||
Args:
|
||||
template_profiles(list): Template profiles from settings.
|
||||
project_doc(dict): Project document from MongoDB.
|
||||
asset_doc(dict): Asset document from MongoDB.
|
||||
task_name(str): Name of task for which templates are filtered.
|
||||
anatomy(Anatomy): Optionally passed anatomy object for passed project
|
||||
name.
|
||||
|
||||
Returns:
|
||||
str: Path to template or None if none of profiles match current
|
||||
context. (Existence of formatted path is not validated.)
|
||||
"""
|
||||
|
||||
from openpype.lib import filter_profiles
|
||||
|
||||
if anatomy is None:
|
||||
anatomy = Anatomy(project_doc["name"])
|
||||
|
||||
# get project, asset, task anatomy context data
|
||||
anatomy_context_data = _get_task_context_data_for_anatomy(
|
||||
project_doc, asset_doc, task_name, anatomy
|
||||
)
|
||||
# add root dict
|
||||
anatomy_context_data["root"] = anatomy.roots
|
||||
|
||||
# get task type for the task in context
|
||||
current_task_type = anatomy_context_data["task"]["type"]
|
||||
|
||||
# get path from matching profile
|
||||
matching_item = filter_profiles(
|
||||
template_profiles,
|
||||
{"task_type": current_task_type}
|
||||
)
|
||||
# when path is available try to format it in case
|
||||
# there are some anatomy template strings
|
||||
if matching_item:
|
||||
template = matching_item["path"][platform.system().lower()]
|
||||
return template.format(**anatomy_context_data)
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def get_custom_workfile_template_by_string_context(
|
||||
template_profiles, project_name, asset_name, task_name,
|
||||
dbcon=None, anatomy=None
|
||||
):
|
||||
"""Filter and fill workfile template profiles by passed context.
|
||||
|
||||
Passed context are string representations of project, asset and task.
|
||||
Function will query documents of project and asset to be able use
|
||||
`get_custom_workfile_template_by_context` for rest of logic.
|
||||
|
||||
Args:
|
||||
template_profiles(list): Loaded workfile template profiles.
|
||||
project_name(str): Project name.
|
||||
asset_name(str): Asset name.
|
||||
task_name(str): Task name.
|
||||
dbcon(AvalonMongoDB): Optional avalon implementation of mongo
|
||||
connection with context Session.
|
||||
anatomy(Anatomy): Optionally prepared anatomy object for passed
|
||||
project.
|
||||
|
||||
Returns:
|
||||
str: Path to template or None if none of profiles match current
|
||||
context. (Existence of formatted path is not validated.)
|
||||
"""
|
||||
|
||||
if dbcon is None:
|
||||
from avalon.api import AvalonMongoDB
|
||||
|
||||
dbcon = AvalonMongoDB()
|
||||
|
||||
dbcon.install()
|
||||
|
||||
if dbcon.Session["AVALON_PROJECT"] != project_name:
|
||||
dbcon.Session["AVALON_PROJECT"] = project_name
|
||||
|
||||
project_doc = dbcon.find_one(
|
||||
{"type": "project"},
|
||||
# All we need is "name" and "data.code" keys
|
||||
{
|
||||
"name": 1,
|
||||
"data.code": 1
|
||||
}
|
||||
)
|
||||
asset_doc = dbcon.find_one(
|
||||
{
|
||||
"type": "asset",
|
||||
"name": asset_name
|
||||
},
|
||||
# All we need is "name" and "data.tasks" keys
|
||||
{
|
||||
"name": 1,
|
||||
"data.tasks": 1
|
||||
}
|
||||
)
|
||||
|
||||
return get_custom_workfile_template_by_context(
|
||||
template_profiles, project_doc, asset_doc, task_name, anatomy
|
||||
)
|
||||
|
||||
|
||||
def get_custom_workfile_template(template_profiles):
|
||||
"""Filter and fill workfile template profiles by current context.
|
||||
|
||||
Current context is defined by `avalon.api.Session`. That's why this
|
||||
function should be used only inside host where context is set and stable.
|
||||
|
||||
Args:
|
||||
template_profiles(list): Template profiles from settings.
|
||||
|
||||
Returns:
|
||||
str: Path to template or None if none of profiles match current
|
||||
context. (Existence of formatted path is not validated.)
|
||||
"""
|
||||
# Use `avalon.io` as Mongo connection
|
||||
from avalon import io
|
||||
|
||||
return get_custom_workfile_template_by_string_context(
|
||||
template_profiles,
|
||||
io.Session["AVALON_PROJECT"],
|
||||
io.Session["AVALON_ASSET"],
|
||||
io.Session["AVALON_TASK"],
|
||||
io
|
||||
)
|
||||
|
|
|
|||
|
|
@ -18,5 +18,9 @@
|
|||
"secondary_pool": "",
|
||||
"chunk_size": 1000000
|
||||
}
|
||||
},
|
||||
"workfile_builder": {
|
||||
"create_first_version": false,
|
||||
"custom_templates": []
|
||||
}
|
||||
}
|
||||
6
openpype/settings/defaults/project_settings/blender.json
Normal file
6
openpype/settings/defaults/project_settings/blender.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"workfile_builder": {
|
||||
"create_first_version": false,
|
||||
"custom_templates": []
|
||||
}
|
||||
}
|
||||
|
|
@ -6,9 +6,7 @@
|
|||
"load": "ctrl+alt+l",
|
||||
"manage": "ctrl+alt+m",
|
||||
"build_workfile": "ctrl+alt+b"
|
||||
},
|
||||
"open_workfile_at_start": false,
|
||||
"create_initial_workfile": true
|
||||
}
|
||||
},
|
||||
"create": {
|
||||
"CreateWriteRender": {
|
||||
|
|
@ -147,12 +145,13 @@
|
|||
"node_name_template": "{class_name}_{ext}"
|
||||
}
|
||||
},
|
||||
"workfile_build": {
|
||||
"workfile_builder": {
|
||||
"create_first_version": false,
|
||||
"custom_templates": [],
|
||||
"builder_on_start": false,
|
||||
"profiles": [
|
||||
{
|
||||
"tasks": [
|
||||
"compositing"
|
||||
],
|
||||
"tasks": [],
|
||||
"current_context": [
|
||||
{
|
||||
"subset_name_filters": [],
|
||||
|
|
@ -162,10 +161,12 @@
|
|||
],
|
||||
"repre_names": [
|
||||
"exr",
|
||||
"dpx"
|
||||
"dpx",
|
||||
"mov"
|
||||
],
|
||||
"loaders": [
|
||||
"LoadSequence"
|
||||
"LoadSequence",
|
||||
"LoadMov"
|
||||
]
|
||||
}
|
||||
],
|
||||
|
|
|
|||
|
|
@ -13,5 +13,9 @@
|
|||
"jpg"
|
||||
]
|
||||
}
|
||||
},
|
||||
"workfile_builder": {
|
||||
"create_first_version": false,
|
||||
"custom_templates": []
|
||||
}
|
||||
}
|
||||
|
|
@ -32,5 +32,9 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"workfile_builder": {
|
||||
"create_first_version": false,
|
||||
"custom_templates": []
|
||||
},
|
||||
"filters": {}
|
||||
}
|
||||
|
|
@ -78,6 +78,10 @@
|
|||
"type": "schema",
|
||||
"name": "schema_project_hiero"
|
||||
},
|
||||
{
|
||||
"type": "schema",
|
||||
"name": "schema_project_blender"
|
||||
},
|
||||
{
|
||||
"type": "schema",
|
||||
"name": "schema_project_aftereffects"
|
||||
|
|
|
|||
|
|
@ -85,6 +85,14 @@
|
|||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "schema_template",
|
||||
"name": "template_workfile_options",
|
||||
"skip_paths": [
|
||||
"workfile_builder/builder_on_start",
|
||||
"workfile_builder/profiles"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "blender",
|
||||
"label": "Blender",
|
||||
"is_file": true,
|
||||
"children": [
|
||||
{
|
||||
"type": "schema_template",
|
||||
"name": "template_workfile_options",
|
||||
"skip_paths": [
|
||||
"workfile_builder/builder_on_start",
|
||||
"workfile_builder/profiles"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -43,16 +43,6 @@
|
|||
"label": "Build Workfile"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "open_workfile_at_start",
|
||||
"label": "Open Workfile window at start of a Nuke session"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "create_initial_workfile",
|
||||
"label": "Create initial workfile version if none available"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -103,8 +93,8 @@
|
|||
"template_data": []
|
||||
},
|
||||
{
|
||||
"type": "schema",
|
||||
"name": "schema_workfile_build"
|
||||
"type": "schema_template",
|
||||
"name": "template_workfile_options"
|
||||
},
|
||||
{
|
||||
"type": "schema",
|
||||
|
|
|
|||
|
|
@ -52,6 +52,14 @@
|
|||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "schema_template",
|
||||
"name": "template_workfile_options",
|
||||
"skip_paths": [
|
||||
"workfile_builder/builder_on_start",
|
||||
"workfile_builder/profiles"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,6 +112,14 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "schema_template",
|
||||
"name": "template_workfile_options",
|
||||
"skip_paths": [
|
||||
"workfile_builder/builder_on_start",
|
||||
"workfile_builder/profiles"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "schema",
|
||||
"name": "schema_publish_gui_filter"
|
||||
|
|
|
|||
|
|
@ -94,4 +94,4 @@
|
|||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
[
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "workfile_builder",
|
||||
"label": "Workfile Builder",
|
||||
"children": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "create_first_version",
|
||||
"label": "Create first workfile",
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"type": "path",
|
||||
"key": "template_path",
|
||||
"label": "First workfile template",
|
||||
"multiplatform": true,
|
||||
"multipath": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
@ -0,0 +1,145 @@
|
|||
[{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "workfile_builder",
|
||||
"label": "Workfile Builder",
|
||||
"children": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "create_first_version",
|
||||
"label": "Create first workfile",
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"type": "list",
|
||||
"key": "custom_templates",
|
||||
"label": "Custom templates",
|
||||
"is_group": true,
|
||||
"use_label_wrap": true,
|
||||
"object_type": {
|
||||
"type": "dict",
|
||||
"children": [
|
||||
{
|
||||
"type": "task-types-enum",
|
||||
"key": "task_types",
|
||||
"label": "Task types"
|
||||
},
|
||||
{
|
||||
"type": "splitter"
|
||||
},
|
||||
{
|
||||
"type": "label",
|
||||
"label": "Absolute path to workfile template or OpenPype Anatomy text is accepted."
|
||||
},
|
||||
{
|
||||
"type": "path",
|
||||
"key": "path",
|
||||
"label": "Path",
|
||||
"multiplatform": true,
|
||||
"multipath": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "builder_on_start",
|
||||
"label": "Run Builder Profiles on first launch",
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"type": "list",
|
||||
"key": "profiles",
|
||||
"label": "Profiles",
|
||||
"use_label_wrap": true,
|
||||
"object_type": {
|
||||
"type": "dict",
|
||||
"children": [
|
||||
{
|
||||
"key": "tasks",
|
||||
"label": "Tasks",
|
||||
"type": "list",
|
||||
"object_type": "text"
|
||||
},
|
||||
{
|
||||
"type": "splitter"
|
||||
},
|
||||
{
|
||||
"key": "current_context",
|
||||
"label": "<b>Current Context</b>",
|
||||
"type": "list",
|
||||
"highlight_content": true,
|
||||
"object_type": {
|
||||
"type": "dict",
|
||||
"children": [
|
||||
{
|
||||
"key": "subset_name_filters",
|
||||
"label": "Subset name Filters",
|
||||
"type": "list",
|
||||
"object_type": "text"
|
||||
},
|
||||
{
|
||||
"key": "families",
|
||||
"label": "Families",
|
||||
"type": "list",
|
||||
"object_type": "text"
|
||||
},
|
||||
{
|
||||
"key": "repre_names",
|
||||
"label": "Repre Names",
|
||||
"type": "list",
|
||||
"object_type": "text"
|
||||
},
|
||||
{
|
||||
"key": "loaders",
|
||||
"label": "Loaders",
|
||||
"type": "list",
|
||||
"object_type": "text"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "separator"
|
||||
},
|
||||
{
|
||||
"key": "linked_assets",
|
||||
"label": "<b>Linked Assets/Shots</b>",
|
||||
"type": "list",
|
||||
"highlight_content": true,
|
||||
"object_type": {
|
||||
"type": "dict",
|
||||
"children": [
|
||||
{
|
||||
"key": "subset_name_filters",
|
||||
"label": "Subset name Filters",
|
||||
"type": "list",
|
||||
"object_type": "text"
|
||||
},
|
||||
{
|
||||
"key": "families",
|
||||
"label": "Families",
|
||||
"type": "list",
|
||||
"object_type": "text"
|
||||
},
|
||||
{
|
||||
"key": "repre_names",
|
||||
"label": "Repre Names",
|
||||
"type": "list",
|
||||
"object_type": "text"
|
||||
},
|
||||
{
|
||||
"key": "loaders",
|
||||
"label": "Loaders",
|
||||
"type": "list",
|
||||
"object_type": "text"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
@ -508,6 +508,8 @@ class PathWidget(BaseWidget):
|
|||
self.content_layout = QtWidgets.QGridLayout(self)
|
||||
self.content_layout.setContentsMargins(0, 0, 0, 0)
|
||||
self.content_layout.setSpacing(5)
|
||||
# Add stretch to second column
|
||||
self.content_layout.setColumnStretch(1, 1)
|
||||
self.body_widget = None
|
||||
|
||||
self.setAttribute(QtCore.Qt.WA_TranslucentBackground)
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit e9882d0ffff27fed03a03459f496c29da0310cd2
|
||||
Subproject commit 07756c7368e4447d28f27dc88b6a46c95692e035
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 9.4 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.5 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.8 KiB |
|
|
@ -17,10 +17,10 @@ Projects always use default project values unless they have [project override](.
|
|||
|
||||
Many of the settings are using a concept of **Profile filters**
|
||||
|
||||
You can define multiple profiles to choose from for different contexts. Each filter is evaluated and a
|
||||
profile with filters matching the current context the most, is used.
|
||||
You can define multiple profiles to choose from for different contexts. Each filter is evaluated and a
|
||||
profile with filters matching the current context the most, is used.
|
||||
|
||||
You can define profile without any filters and use it as **default**.
|
||||
You can define profile without any filters and use it as **default**.
|
||||
|
||||
Only **one or none** profile will be returned per context.
|
||||
|
||||
|
|
@ -129,7 +129,7 @@ Profile may generate multiple outputs from a single input. Each output must defi
|
|||
Saves information for all published subsets into DB, published assets are available for other hosts, tools and tasks after.
|
||||
#### Template name profiles
|
||||
|
||||
Allows to select [anatomy template](admin_settings_project_anatomy.md#templates) based on context of subset being published.
|
||||
Allows to select [anatomy template](admin_settings_project_anatomy.md#templates) based on context of subset being published.
|
||||
|
||||
For example for `render` profile you might want to publish and store assets in different location (based on anatomy setting) then for `publish` profile.
|
||||
[Profile filtering](#profile-filters) is used to select between appropriate template for each context of published subsets.
|
||||
|
|
@ -139,7 +139,7 @@ Applicable context filters:
|
|||
- **`tasks`** - Current task. `["modeling", "animation"]`
|
||||
|
||||

|
||||
|
||||
|
||||
(This image shows use case where `render` anatomy template is used for subsets of families ['review, 'render', 'prerender'], `publish` template is chosen for all other.)
|
||||
|
||||
#### Subset grouping profiles
|
||||
|
|
@ -154,5 +154,16 @@ Applicable context filters:
|
|||
- **`tasks`** - Current task. `["modeling", "animation"]`
|
||||
|
||||

|
||||
|
||||
(This image shows use case where only assets published from 'photoshop', for all families for all tasks should be marked as grouped with a capitalized name of Task where they are published from.)
|
||||
|
||||
(This image shows use case where only assets published from 'photoshop', for all families for all tasks should be marked as grouped with a capitalized name of Task where they are published from.)
|
||||
|
||||
## Tools
|
||||
Settings for OpenPype tools.
|
||||
|
||||
## Workfiles
|
||||
All settings related to Workfile tool.
|
||||
|
||||
### Open last workfile at launch
|
||||
This feature allows you to define a rule for each task/host or toggle the feature globally to all tasks as they are visible in the picture.
|
||||
|
||||

|
||||
63
website/docs/project_settings/settings_project_nuke.md
Normal file
63
website/docs/project_settings/settings_project_nuke.md
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
---
|
||||
id: settings_project_nuke
|
||||
title: Project Nuke Setting
|
||||
sidebar_label: Nuke
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
Project settings can have project specific values. Each new project is using studio values defined in **default** project but these values can be modified or overriden per project.
|
||||
|
||||
:::warning Default studio values
|
||||
Projects always use default project values unless they have [project override](../admin_settings#project-overrides) (orage colour). Any changes in default project may affect all existing projects.
|
||||
:::
|
||||
|
||||
## Workfile Builder
|
||||
|
||||
All Workfile Builder related settings can be found here. This is a list of available features:
|
||||
- Create first workfile
|
||||
- Custom Template path (task type filtered)
|
||||
- Run Builder profiles at first run
|
||||
- Define Builder Profiles With Filters
|
||||
|
||||

|
||||
|
||||
:::important Auto Load Last Version
|
||||
In case you want to set the auto load of the latest available version of workfiles, you can do it from [here](settings_project_global#open-last-workfile-at-launch).
|
||||
:::
|
||||
|
||||
### Create first workfile
|
||||
|
||||
By switchintg this feature on, OpenPype will generate initial workfile version. Following attributes are possible to configure:
|
||||
|
||||

|
||||
|
||||
#### Custom templates
|
||||
Custom templates are added into nuke's node graph as nodes. List of task types can be defined for templates filtering.
|
||||
|
||||
- Task types are sourced from project related Anatomy/Task Types
|
||||
|
||||

|
||||
|
||||
- multi platform path can be used in a variety of ways. Along the absolut path to a template file also an python formating could be used. At the moment these keys are supported (image example bellow):
|
||||
- `root[key]`: definitions from anatomy roots
|
||||
- `project[name, code]`: project in context
|
||||
- `asset`: name of asset/shot in context
|
||||
- `task[type, name, short_name]`: as they are defined on asset/shot and in **Anatomy/Task Type** on project context
|
||||
|
||||

|
||||
|
||||
#### Run Builder profiles on first launch
|
||||
Enabling this feature will look into available Builder's Prorfiles (look bellow for more informations about this feature) and load available versions into node graph.
|
||||
|
||||
### Profiles (Builder)
|
||||
Builder profiles are set of rules allowing artist Load any available versions for the context of the asset, which it is run from. Preset is having following attributes:
|
||||
|
||||
- **Filter**: Each profile could be defined with task filter. In case no filter is defined, a profile will be working for all.
|
||||
|
||||
- **Context section**: filtres for subset name (regex accepted), families, representation names and available Loader plugin.
|
||||
|
||||
- **Linked Assets/Shots**: filters for asset builds to be added
|
||||
|
||||

|
||||
|
|
@ -64,7 +64,8 @@ module.exports = {
|
|||
type: "category",
|
||||
label: "Project Settings",
|
||||
items: [
|
||||
"project_settings/settings_project_global"
|
||||
"project_settings/settings_project_global",
|
||||
"project_settings/settings_project_nuke"
|
||||
],
|
||||
},
|
||||
],
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue