mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 12:54:40 +01:00
[Automated] Merged develop into main
This commit is contained in:
commit
30d52c5af0
30 changed files with 457 additions and 197 deletions
|
|
@ -72,7 +72,7 @@ class ExtractPlayblast(openpype.api.Extractor):
|
|||
|
||||
# Isolate view is requested by having objects in the set besides a
|
||||
# camera.
|
||||
if preset.pop("isolate_view", False) or instance.data.get("isolate"):
|
||||
if preset.pop("isolate_view", False) and instance.data.get("isolate"):
|
||||
preset["isolate"] = instance.data["setMembers"]
|
||||
|
||||
# Show/Hide image planes on request.
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ class ExtractThumbnail(openpype.api.Extractor):
|
|||
|
||||
# Isolate view is requested by having objects in the set besides a
|
||||
# camera.
|
||||
if preset.pop("isolate_view", False) or instance.data.get("isolate"):
|
||||
if preset.pop("isolate_view", False) and instance.data.get("isolate"):
|
||||
preset["isolate"] = instance.data["setMembers"]
|
||||
|
||||
with maintained_time():
|
||||
|
|
|
|||
|
|
@ -286,7 +286,8 @@ def add_button_write_to_read(node):
|
|||
node.addKnob(knob)
|
||||
|
||||
|
||||
def create_write_node(name, data, input=None, prenodes=None, review=True):
|
||||
def create_write_node(name, data, input=None, prenodes=None,
|
||||
review=True, linked_knobs=None):
|
||||
''' Creating write node which is group node
|
||||
|
||||
Arguments:
|
||||
|
|
@ -465,12 +466,16 @@ def create_write_node(name, data, input=None, prenodes=None, review=True):
|
|||
GN.addKnob(nuke.Text_Knob('', 'Rendering'))
|
||||
|
||||
# Add linked knobs.
|
||||
linked_knob_names = [
|
||||
"_grp-start_",
|
||||
"use_limit", "first", "last",
|
||||
"_grp-end_",
|
||||
"Render"
|
||||
]
|
||||
linked_knob_names = []
|
||||
|
||||
# add input linked knobs and create group only if any input
|
||||
if linked_knobs:
|
||||
linked_knob_names.append("_grp-start_")
|
||||
linked_knob_names.extend(linked_knobs)
|
||||
linked_knob_names.append("_grp-end_")
|
||||
|
||||
linked_knob_names.append("Render")
|
||||
|
||||
for name in linked_knob_names:
|
||||
if "_grp-start_" in name:
|
||||
knob = nuke.Tab_Knob(
|
||||
|
|
@ -481,13 +486,20 @@ def create_write_node(name, data, input=None, prenodes=None, review=True):
|
|||
"rnd_attr_end", "Rendering attributes", nuke.TABENDGROUP)
|
||||
GN.addKnob(knob)
|
||||
else:
|
||||
link = nuke.Link_Knob("")
|
||||
link.makeLink(write_node.name(), name)
|
||||
link.setName(name)
|
||||
if "Render" in name:
|
||||
link.setLabel("Render Local")
|
||||
link.setFlag(0x1000)
|
||||
GN.addKnob(link)
|
||||
if "___" in name:
|
||||
# add devider
|
||||
GN.addKnob(nuke.Text_Knob(""))
|
||||
else:
|
||||
# add linked knob by name
|
||||
link = nuke.Link_Knob("")
|
||||
link.makeLink(write_node.name(), name)
|
||||
link.setName(name)
|
||||
|
||||
# make render
|
||||
if "Render" in name:
|
||||
link.setLabel("Render Local")
|
||||
link.setFlag(0x1000)
|
||||
GN.addKnob(link)
|
||||
|
||||
# adding write to read button
|
||||
add_button_write_to_read(GN)
|
||||
|
|
|
|||
|
|
@ -103,7 +103,8 @@ class CreateWritePrerender(plugin.PypeCreator):
|
|||
write_data,
|
||||
input=selected_node,
|
||||
prenodes=[],
|
||||
review=False)
|
||||
review=False,
|
||||
linked_knobs=["channels", "___", "first", "last", "use_limit"])
|
||||
|
||||
# relinking to collected connections
|
||||
for i, input in enumerate(inputs):
|
||||
|
|
@ -122,19 +123,10 @@ class CreateWritePrerender(plugin.PypeCreator):
|
|||
w_node = n
|
||||
write_node.end()
|
||||
|
||||
# add inner write node Tab
|
||||
write_node.addKnob(nuke.Tab_Knob("WriteLinkedKnobs"))
|
||||
if self.presets.get("use_range_limit"):
|
||||
w_node["use_limit"].setValue(True)
|
||||
w_node["first"].setValue(nuke.root()["first_frame"].value())
|
||||
w_node["last"].setValue(nuke.root()["last_frame"].value())
|
||||
|
||||
# linking knobs to group property panel
|
||||
linking_knobs = ["channels", "___", "first", "last", "use_limit"]
|
||||
for k in linking_knobs:
|
||||
if "___" in k:
|
||||
write_node.addKnob(nuke.Text_Knob(''))
|
||||
else:
|
||||
lnk = nuke.Link_Knob(k)
|
||||
lnk.makeLink(w_node.name(), k)
|
||||
lnk.setName(k.replace('_', ' ').capitalize())
|
||||
lnk.clearFlag(nuke.STARTLINE)
|
||||
write_node.addKnob(lnk)
|
||||
|
||||
return write_node
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import os
|
||||
import re
|
||||
from pprint import pformat
|
||||
import nuke
|
||||
import pyblish.api
|
||||
import openpype.api as pype
|
||||
|
|
@ -17,6 +18,7 @@ class CollectNukeWrites(pyblish.api.InstancePlugin):
|
|||
|
||||
def process(self, instance):
|
||||
_families_test = [instance.data["family"]] + instance.data["families"]
|
||||
self.log.debug("_families_test: {}".format(_families_test))
|
||||
|
||||
node = None
|
||||
for x in instance:
|
||||
|
|
@ -133,22 +135,29 @@ class CollectNukeWrites(pyblish.api.InstancePlugin):
|
|||
"outputDir": output_dir,
|
||||
"ext": ext,
|
||||
"label": label,
|
||||
"handleStart": handle_start,
|
||||
"handleEnd": handle_end,
|
||||
"frameStart": first_frame + handle_start,
|
||||
"frameEnd": last_frame - handle_end,
|
||||
"frameStartHandle": first_frame,
|
||||
"frameEndHandle": last_frame,
|
||||
"outputType": output_type,
|
||||
"colorspace": colorspace,
|
||||
"deadlineChunkSize": deadlineChunkSize,
|
||||
"deadlinePriority": deadlinePriority
|
||||
})
|
||||
|
||||
if "prerender" in _families_test:
|
||||
if self.is_prerender(_families_test):
|
||||
instance.data.update({
|
||||
"family": "prerender",
|
||||
"families": []
|
||||
"handleStart": 0,
|
||||
"handleEnd": 0,
|
||||
"frameStart": first_frame,
|
||||
"frameEnd": last_frame,
|
||||
"frameStartHandle": first_frame,
|
||||
"frameEndHandle": last_frame,
|
||||
})
|
||||
else:
|
||||
instance.data.update({
|
||||
"handleStart": handle_start,
|
||||
"handleEnd": handle_end,
|
||||
"frameStart": first_frame + handle_start,
|
||||
"frameEnd": last_frame - handle_end,
|
||||
"frameStartHandle": first_frame,
|
||||
"frameEndHandle": last_frame,
|
||||
})
|
||||
|
||||
# * Add audio to instance if exists.
|
||||
|
|
@ -170,4 +179,7 @@ class CollectNukeWrites(pyblish.api.InstancePlugin):
|
|||
"filename": api.get_representation_path(repre_doc)
|
||||
}]
|
||||
|
||||
self.log.debug("instance.data: {}".format(instance.data))
|
||||
self.log.debug("instance.data: {}".format(pformat(instance.data)))
|
||||
|
||||
def is_prerender(self, families):
|
||||
return next((f for f in families if "prerender" in f), None)
|
||||
|
|
|
|||
|
|
@ -61,7 +61,6 @@ class ValidateRenderedFrames(pyblish.api.InstancePlugin):
|
|||
hosts = ["nuke", "nukestudio"]
|
||||
actions = [RepairCollectionActionToLocal, RepairCollectionActionToFarm]
|
||||
|
||||
|
||||
def process(self, instance):
|
||||
|
||||
for repre in instance.data["representations"]:
|
||||
|
|
@ -78,10 +77,10 @@ class ValidateRenderedFrames(pyblish.api.InstancePlugin):
|
|||
|
||||
collection = collections[0]
|
||||
|
||||
frame_length = int(
|
||||
instance.data["frameEndHandle"]
|
||||
- instance.data["frameStartHandle"] + 1
|
||||
)
|
||||
fstartH = instance.data["frameStartHandle"]
|
||||
fendH = instance.data["frameEndHandle"]
|
||||
|
||||
frame_length = int(fendH - fstartH + 1)
|
||||
|
||||
if frame_length != 1:
|
||||
if len(collections) != 1:
|
||||
|
|
@ -95,7 +94,16 @@ class ValidateRenderedFrames(pyblish.api.InstancePlugin):
|
|||
raise ValidationException(msg)
|
||||
|
||||
collected_frames_len = int(len(collection.indexes))
|
||||
coll_start = min(collection.indexes)
|
||||
coll_end = max(collection.indexes)
|
||||
|
||||
self.log.info("frame_length: {}".format(frame_length))
|
||||
self.log.info("collected_frames_len: {}".format(
|
||||
collected_frames_len))
|
||||
self.log.info("fstartH-fendH: {}-{}".format(fstartH, fendH))
|
||||
self.log.info(
|
||||
"coll_start-coll_end: {}-{}".format(coll_start, coll_end))
|
||||
|
||||
self.log.info(
|
||||
"len(collection.indexes): {}".format(collected_frames_len)
|
||||
)
|
||||
|
|
@ -103,8 +111,11 @@ class ValidateRenderedFrames(pyblish.api.InstancePlugin):
|
|||
if ("slate" in instance.data["families"]) \
|
||||
and (frame_length != collected_frames_len):
|
||||
collected_frames_len -= 1
|
||||
fstartH += 1
|
||||
|
||||
assert (collected_frames_len == frame_length), (
|
||||
assert ((collected_frames_len >= frame_length)
|
||||
and (coll_start <= fstartH)
|
||||
and (coll_end >= fendH)), (
|
||||
"{} missing frames. Use repair to render all frames"
|
||||
).format(__name__)
|
||||
|
||||
|
|
|
|||
|
|
@ -58,18 +58,14 @@ class CreateRenderlayer(plugin.Creator):
|
|||
# Get currently selected layers
|
||||
layers_data = lib.layers_data()
|
||||
|
||||
group_ids = set()
|
||||
for layer in layers_data:
|
||||
if layer["selected"]:
|
||||
group_ids.add(layer["group_id"])
|
||||
|
||||
selected_layers = [
|
||||
layer
|
||||
for layer in layers_data
|
||||
if layer["selected"]
|
||||
]
|
||||
# Return layer name if only one is selected
|
||||
if len(group_ids) == 1:
|
||||
group_id = list(group_ids)[0]
|
||||
groups_data = lib.groups_data()
|
||||
for group in groups_data:
|
||||
if group["group_id"] == group_id:
|
||||
return group["name"]
|
||||
if len(selected_layers) == 1:
|
||||
return selected_layers[0]["name"]
|
||||
|
||||
# Use defaults
|
||||
if cls.defaults:
|
||||
|
|
|
|||
|
|
@ -986,9 +986,21 @@ class ExtractReview(pyblish.api.InstancePlugin):
|
|||
output_width = output_def.get("width") or None
|
||||
output_height = output_def.get("height") or None
|
||||
|
||||
# Overscal color
|
||||
overscan_color_value = "black"
|
||||
overscan_color = output_def.get("overscan_color")
|
||||
if overscan_color:
|
||||
bg_red, bg_green, bg_blue, _ = overscan_color
|
||||
overscan_color_value = "#{0:0>2X}{1:0>2X}{2:0>2X}".format(
|
||||
bg_red, bg_green, bg_blue
|
||||
)
|
||||
self.log.debug("Overscan color: `{}`".format(overscan_color_value))
|
||||
|
||||
# Convert overscan value video filters
|
||||
overscan_crop = output_def.get("overscan_crop")
|
||||
overscan = OverscanCrop(input_width, input_height, overscan_crop)
|
||||
overscan = OverscanCrop(
|
||||
input_width, input_height, overscan_crop, overscan_color_value
|
||||
)
|
||||
overscan_crop_filters = overscan.video_filters()
|
||||
# Add overscan filters to filters if are any and modify input
|
||||
# resolution by it's values
|
||||
|
|
@ -1158,9 +1170,10 @@ class ExtractReview(pyblish.api.InstancePlugin):
|
|||
"scale={}x{}:flags=lanczos".format(
|
||||
width_scale, height_scale
|
||||
),
|
||||
"pad={}:{}:{}:{}:black".format(
|
||||
"pad={}:{}:{}:{}:{}".format(
|
||||
output_width, output_height,
|
||||
width_half_pad, height_half_pad
|
||||
width_half_pad, height_half_pad,
|
||||
overscan_color_value
|
||||
),
|
||||
"setsar=1"
|
||||
])
|
||||
|
|
@ -1707,12 +1720,15 @@ class OverscanCrop:
|
|||
item_regex = re.compile(r"([\+\-])?([0-9]+)(.+)?")
|
||||
relative_source_regex = re.compile(r"%([\+\-])")
|
||||
|
||||
def __init__(self, input_width, input_height, string_value):
|
||||
def __init__(
|
||||
self, input_width, input_height, string_value, overscal_color=None
|
||||
):
|
||||
# Make sure that is not None
|
||||
string_value = string_value or ""
|
||||
|
||||
self.input_width = input_width
|
||||
self.input_height = input_height
|
||||
self.overscal_color = overscal_color
|
||||
|
||||
width, height = self._convert_string_to_values(string_value)
|
||||
self._width_value = width
|
||||
|
|
@ -1767,16 +1783,22 @@ class OverscanCrop:
|
|||
|
||||
elif width >= self.input_width and height >= self.input_height:
|
||||
output.append(
|
||||
"pad={}:{}:(iw-ow)/2:(ih-oh)/2".format(width, height)
|
||||
"pad={}:{}:(iw-ow)/2:(ih-oh)/2:{}".format(
|
||||
width, height, self.overscal_color
|
||||
)
|
||||
)
|
||||
|
||||
elif width > self.input_width and height < self.input_height:
|
||||
output.append("crop=iw:{}".format(height))
|
||||
output.append("pad={}:ih:(iw-ow)/2:(ih-oh)/2".format(width))
|
||||
output.append("pad={}:ih:(iw-ow)/2:(ih-oh)/2:{}".format(
|
||||
width, self.overscal_color
|
||||
))
|
||||
|
||||
elif width < self.input_width and height > self.input_height:
|
||||
output.append("crop={}:ih".format(width))
|
||||
output.append("pad=iw:{}:(iw-ow)/2:(ih-oh)/2".format(height))
|
||||
output.append("pad=iw:{}:(iw-ow)/2:(ih-oh)/2:{}".format(
|
||||
height, self.overscal_color
|
||||
))
|
||||
|
||||
return output
|
||||
|
||||
|
|
|
|||
|
|
@ -78,6 +78,10 @@
|
|||
{
|
||||
"name": "colorspace",
|
||||
"value": "linear"
|
||||
},
|
||||
{
|
||||
"name": "create_directories",
|
||||
"value": "True"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -114,6 +118,10 @@
|
|||
{
|
||||
"name": "colorspace",
|
||||
"value": "linear"
|
||||
},
|
||||
{
|
||||
"name": "create_directories",
|
||||
"value": "True"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,6 +56,12 @@
|
|||
]
|
||||
},
|
||||
"overscan_crop": "",
|
||||
"overscan_color": [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
255
|
||||
],
|
||||
"width": 0,
|
||||
"height": 0,
|
||||
"bg_color": [
|
||||
|
|
@ -226,6 +232,17 @@
|
|||
],
|
||||
"tasks": [],
|
||||
"template": "{family}{Task}_{Render_layer}_{Render_pass}"
|
||||
},
|
||||
{
|
||||
"families": [
|
||||
"review",
|
||||
"workfile"
|
||||
],
|
||||
"hosts": [
|
||||
"tvpaint"
|
||||
],
|
||||
"tasks": [],
|
||||
"template": "{family}{Task}"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@
|
|||
"fpath_template": "{work}/renders/nuke/{subset}/{subset}.{frame}.{ext}"
|
||||
},
|
||||
"CreateWritePrerender": {
|
||||
"fpath_template": "{work}/prerenders/nuke/{subset}/{subset}.{frame}.{ext}"
|
||||
"fpath_template": "{work}/prerenders/nuke/{subset}/{subset}.{frame}.{ext}",
|
||||
"use_range_limit": true
|
||||
}
|
||||
},
|
||||
"publish": {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,17 @@ class ColorEntity(InputEntity):
|
|||
def _item_initalization(self):
|
||||
self.valid_value_types = (list, )
|
||||
self.value_on_not_set = [0, 0, 0, 255]
|
||||
self.use_alpha = self.schema_data.get("use_alpha", True)
|
||||
|
||||
def set_override_state(self, *args, **kwargs):
|
||||
super(ColorEntity, self).set_override_state(*args, **kwargs)
|
||||
value = self._current_value
|
||||
if (
|
||||
not self.use_alpha
|
||||
and isinstance(value, list)
|
||||
and len(value) == 4
|
||||
):
|
||||
value[3] = 255
|
||||
|
||||
def convert_to_valid_type(self, value):
|
||||
"""Conversion to valid type.
|
||||
|
|
@ -51,4 +62,8 @@ class ColorEntity(InputEntity):
|
|||
).format(value)
|
||||
raise BaseInvalidValueType(reason, self.path)
|
||||
new_value.append(item)
|
||||
|
||||
# Make sure
|
||||
if not self.use_alpha:
|
||||
new_value[3] = 255
|
||||
return new_value
|
||||
|
|
|
|||
|
|
@ -77,6 +77,11 @@
|
|||
"type": "text",
|
||||
"key": "fpath_template",
|
||||
"label": "Path template"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "use_range_limit",
|
||||
"label": "Use Frame range limit by default"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
"key": "imageio",
|
||||
"label": "Color Management and Output Formats",
|
||||
"is_file": true,
|
||||
"is_group": true,
|
||||
"children": [
|
||||
{
|
||||
"key": "hiero",
|
||||
|
|
@ -15,6 +14,7 @@
|
|||
"type": "dict",
|
||||
"label": "Workfile",
|
||||
"collapsible": false,
|
||||
"is_group": true,
|
||||
"children": [
|
||||
{
|
||||
"type": "form",
|
||||
|
|
@ -89,6 +89,7 @@
|
|||
"type": "dict",
|
||||
"label": "Colorspace on Inputs by regex detection",
|
||||
"collapsible": true,
|
||||
"is_group": true,
|
||||
"children": [
|
||||
{
|
||||
"type": "list",
|
||||
|
|
@ -123,6 +124,7 @@
|
|||
"type": "dict",
|
||||
"label": "Viewer",
|
||||
"collapsible": false,
|
||||
"is_group": true,
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
|
|
@ -136,6 +138,7 @@
|
|||
"type": "dict",
|
||||
"label": "Workfile",
|
||||
"collapsible": false,
|
||||
"is_group": true,
|
||||
"children": [
|
||||
{
|
||||
"type": "form",
|
||||
|
|
@ -233,6 +236,7 @@
|
|||
"type": "dict",
|
||||
"label": "Nodes",
|
||||
"collapsible": true,
|
||||
"is_group": true,
|
||||
"children": [
|
||||
{
|
||||
"key": "requiredNodes",
|
||||
|
|
@ -335,6 +339,7 @@
|
|||
"type": "dict",
|
||||
"label": "Colorspace on Inputs by regex detection",
|
||||
"collapsible": true,
|
||||
"is_group": true,
|
||||
"children": [
|
||||
{
|
||||
"type": "list",
|
||||
|
|
|
|||
|
|
@ -182,6 +182,16 @@
|
|||
"key": "overscan_crop",
|
||||
"label": "Overscan crop"
|
||||
},
|
||||
{
|
||||
"type": "label",
|
||||
"label": "Overscan color is used when input aspect ratio is not same as output aspect ratio."
|
||||
},
|
||||
{
|
||||
"type": "color",
|
||||
"label": "Overscan color",
|
||||
"key": "overscan_color",
|
||||
"use_alpha": false
|
||||
},
|
||||
{
|
||||
"type": "label",
|
||||
"label": "Width and Height must be both set to higher value than 0 else source resolution is used."
|
||||
|
|
|
|||
|
|
@ -325,19 +325,59 @@ class ProjectModel(QtGui.QStandardItemModel):
|
|||
|
||||
self.hide_invisible = False
|
||||
self.project_icon = qtawesome.icon("fa.map", color="white")
|
||||
self._project_names = set()
|
||||
|
||||
def refresh(self):
|
||||
self.clear()
|
||||
self.beginResetModel()
|
||||
|
||||
project_names = set()
|
||||
for project_doc in self.get_projects():
|
||||
item = QtGui.QStandardItem(self.project_icon, project_doc["name"])
|
||||
self.appendRow(item)
|
||||
project_names.add(project_doc["name"])
|
||||
|
||||
self.endResetModel()
|
||||
origin_project_names = set(self._project_names)
|
||||
self._project_names = project_names
|
||||
|
||||
project_names_to_remove = origin_project_names - project_names
|
||||
if project_names_to_remove:
|
||||
row_counts = {}
|
||||
continuous = None
|
||||
for row in range(self.rowCount()):
|
||||
index = self.index(row, 0)
|
||||
index_name = index.data(QtCore.Qt.DisplayRole)
|
||||
if index_name in project_names_to_remove:
|
||||
if continuous is None:
|
||||
continuous = row
|
||||
row_counts[continuous] = 0
|
||||
row_counts[continuous] += 1
|
||||
else:
|
||||
continuous = None
|
||||
|
||||
for row in reversed(sorted(row_counts.keys())):
|
||||
count = row_counts[row]
|
||||
self.removeRows(row, count)
|
||||
|
||||
continuous = None
|
||||
row_counts = {}
|
||||
for idx, project_name in enumerate(sorted(project_names)):
|
||||
if project_name in origin_project_names:
|
||||
continuous = None
|
||||
continue
|
||||
|
||||
if continuous is None:
|
||||
continuous = idx
|
||||
row_counts[continuous] = []
|
||||
|
||||
row_counts[continuous].append(project_name)
|
||||
|
||||
for row in reversed(sorted(row_counts.keys())):
|
||||
items = []
|
||||
for project_name in row_counts[row]:
|
||||
item = QtGui.QStandardItem(self.project_icon, project_name)
|
||||
items.append(item)
|
||||
|
||||
self.invisibleRootItem().insertRows(row, items)
|
||||
|
||||
def get_projects(self):
|
||||
project_docs = []
|
||||
|
||||
for project_doc in sorted(
|
||||
self.dbcon.projects(), key=lambda x: x["name"]
|
||||
):
|
||||
|
|
|
|||
|
|
@ -22,6 +22,9 @@ from .constants import (
|
|||
class ProjectBar(QtWidgets.QWidget):
|
||||
project_changed = QtCore.Signal(int)
|
||||
|
||||
# Project list will be refreshed each 10000 msecs
|
||||
refresh_interval = 10000
|
||||
|
||||
def __init__(self, dbcon, parent=None):
|
||||
super(ProjectBar, self).__init__(parent)
|
||||
|
||||
|
|
@ -47,14 +50,16 @@ class ProjectBar(QtWidgets.QWidget):
|
|||
QtWidgets.QSizePolicy.Maximum
|
||||
)
|
||||
|
||||
refresh_timer = QtCore.QTimer()
|
||||
refresh_timer.setInterval(self.refresh_interval)
|
||||
|
||||
self.model = model
|
||||
self.project_delegate = project_delegate
|
||||
self.project_combobox = project_combobox
|
||||
|
||||
# Initialize
|
||||
self.refresh()
|
||||
self.refresh_timer = refresh_timer
|
||||
|
||||
# Signals
|
||||
refresh_timer.timeout.connect(self._on_refresh_timeout)
|
||||
self.project_combobox.currentIndexChanged.connect(self.project_changed)
|
||||
|
||||
# Set current project by default if it's set.
|
||||
|
|
@ -62,6 +67,20 @@ class ProjectBar(QtWidgets.QWidget):
|
|||
if project_name:
|
||||
self.set_project(project_name)
|
||||
|
||||
def showEvent(self, event):
|
||||
if not self.refresh_timer.isActive():
|
||||
self.refresh_timer.start()
|
||||
super(ProjectBar, self).showEvent(event)
|
||||
|
||||
def _on_refresh_timeout(self):
|
||||
if not self.isVisible():
|
||||
# Stop timer if widget is not visible
|
||||
self.refresh_timer.stop()
|
||||
|
||||
elif self.isActiveWindow():
|
||||
# Refresh projects if window is active
|
||||
self.model.refresh()
|
||||
|
||||
def get_current_project(self):
|
||||
return self.project_combobox.currentText()
|
||||
|
||||
|
|
@ -69,27 +88,14 @@ class ProjectBar(QtWidgets.QWidget):
|
|||
index = self.project_combobox.findText(project_name)
|
||||
if index < 0:
|
||||
# Try refresh combobox model
|
||||
self.project_combobox.blockSignals(True)
|
||||
self.model.refresh()
|
||||
self.project_combobox.blockSignals(False)
|
||||
|
||||
self.refresh()
|
||||
index = self.project_combobox.findText(project_name)
|
||||
|
||||
if index >= 0:
|
||||
self.project_combobox.setCurrentIndex(index)
|
||||
|
||||
def refresh(self):
|
||||
prev_project_name = self.get_current_project()
|
||||
|
||||
# Refresh without signals
|
||||
self.project_combobox.blockSignals(True)
|
||||
|
||||
self.model.refresh()
|
||||
self.set_project(prev_project_name)
|
||||
|
||||
self.project_combobox.blockSignals(False)
|
||||
|
||||
self.project_changed.emit(self.project_combobox.currentIndex())
|
||||
|
||||
|
||||
class ActionBar(QtWidgets.QWidget):
|
||||
|
|
|
|||
|
|
@ -91,6 +91,8 @@ class ProjectsPanel(QtWidgets.QWidget):
|
|||
"""Projects Page"""
|
||||
|
||||
project_clicked = QtCore.Signal(str)
|
||||
# Refresh projects each 10000 msecs
|
||||
refresh_interval = 10000
|
||||
|
||||
def __init__(self, dbcon, parent=None):
|
||||
super(ProjectsPanel, self).__init__(parent=parent)
|
||||
|
|
@ -106,21 +108,40 @@ class ProjectsPanel(QtWidgets.QWidget):
|
|||
flick.activateOn(view)
|
||||
model = ProjectModel(self.dbcon)
|
||||
model.hide_invisible = True
|
||||
model.refresh()
|
||||
view.setModel(model)
|
||||
|
||||
layout.addWidget(view)
|
||||
|
||||
refresh_timer = QtCore.QTimer()
|
||||
refresh_timer.setInterval(self.refresh_interval)
|
||||
|
||||
refresh_timer.timeout.connect(self._on_refresh_timeout)
|
||||
view.clicked.connect(self.on_clicked)
|
||||
|
||||
self.model = model
|
||||
self.view = view
|
||||
self.refresh_timer = refresh_timer
|
||||
|
||||
def on_clicked(self, index):
|
||||
if index.isValid():
|
||||
project_name = index.data(QtCore.Qt.DisplayRole)
|
||||
self.project_clicked.emit(project_name)
|
||||
|
||||
def showEvent(self, event):
|
||||
self.model.refresh()
|
||||
if not self.refresh_timer.isActive():
|
||||
self.refresh_timer.start()
|
||||
super(ProjectsPanel, self).showEvent(event)
|
||||
|
||||
def _on_refresh_timeout(self):
|
||||
if not self.isVisible():
|
||||
# Stop timer if widget is not visible
|
||||
self.refresh_timer.stop()
|
||||
|
||||
elif self.isActiveWindow():
|
||||
# Refresh projects if window is active
|
||||
self.model.refresh()
|
||||
|
||||
|
||||
class AssetsPanel(QtWidgets.QWidget):
|
||||
"""Assets page"""
|
||||
|
|
@ -276,6 +297,8 @@ class AssetsPanel(QtWidgets.QWidget):
|
|||
|
||||
class LauncherWindow(QtWidgets.QDialog):
|
||||
"""Launcher interface"""
|
||||
# Refresh actions each 10000msecs
|
||||
actions_refresh_timeout = 10000
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super(LauncherWindow, self).__init__(parent)
|
||||
|
|
@ -344,6 +367,10 @@ class LauncherWindow(QtWidgets.QDialog):
|
|||
layout.setSpacing(0)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
actions_refresh_timer = QtCore.QTimer()
|
||||
actions_refresh_timer.setInterval(self.actions_refresh_timeout)
|
||||
|
||||
self.actions_refresh_timer = actions_refresh_timer
|
||||
self.message_label = message_label
|
||||
self.project_panel = project_panel
|
||||
self.asset_panel = asset_panel
|
||||
|
|
@ -353,6 +380,7 @@ class LauncherWindow(QtWidgets.QDialog):
|
|||
self._page = 0
|
||||
|
||||
# signals
|
||||
actions_refresh_timer.timeout.connect(self._on_action_timer)
|
||||
actions_bar.action_clicked.connect(self.on_action_clicked)
|
||||
action_history.trigger_history.connect(self.on_history_action)
|
||||
project_panel.project_clicked.connect(self.on_project_clicked)
|
||||
|
|
@ -367,9 +395,11 @@ class LauncherWindow(QtWidgets.QDialog):
|
|||
self.resize(520, 740)
|
||||
|
||||
def showEvent(self, event):
|
||||
super().showEvent(event)
|
||||
# TODO implement refresh/reset which will trigger updating
|
||||
self.discover_actions()
|
||||
if not self.actions_refresh_timer.isActive():
|
||||
self.actions_refresh_timer.start()
|
||||
self.discover_actions()
|
||||
|
||||
super(LauncherWindow, self).showEvent(event)
|
||||
|
||||
def set_page(self, page):
|
||||
current = self.page_slider.currentIndex()
|
||||
|
|
@ -402,6 +432,15 @@ class LauncherWindow(QtWidgets.QDialog):
|
|||
def filter_actions(self):
|
||||
self.actions_bar.filter_actions()
|
||||
|
||||
def _on_action_timer(self):
|
||||
if not self.isVisible():
|
||||
# Stop timer if widget is not visible
|
||||
self.actions_refresh_timer.stop()
|
||||
|
||||
elif self.isActiveWindow():
|
||||
# Refresh projects if window is active
|
||||
self.discover_actions()
|
||||
|
||||
def on_project_clicked(self, project_name):
|
||||
self.dbcon.Session["AVALON_PROJECT"] = project_name
|
||||
# Refresh projects
|
||||
|
|
@ -412,7 +451,6 @@ class LauncherWindow(QtWidgets.QDialog):
|
|||
def on_back_clicked(self):
|
||||
self.dbcon.Session["AVALON_PROJECT"] = None
|
||||
self.set_page(0)
|
||||
self.project_panel.model.refresh() # Refresh projects
|
||||
self.discover_actions()
|
||||
|
||||
def on_action_clicked(self, action):
|
||||
|
|
|
|||
|
|
@ -25,7 +25,9 @@ class ColorWidget(InputWidget):
|
|||
self._dialog.open()
|
||||
return
|
||||
|
||||
dialog = ColorDialog(self.input_field.color(), self)
|
||||
dialog = ColorDialog(
|
||||
self.input_field.color(), self.entity.use_alpha, self
|
||||
)
|
||||
self._dialog = dialog
|
||||
|
||||
dialog.open()
|
||||
|
|
@ -120,12 +122,12 @@ class ColorViewer(QtWidgets.QWidget):
|
|||
|
||||
|
||||
class ColorDialog(QtWidgets.QDialog):
|
||||
def __init__(self, color=None, parent=None):
|
||||
def __init__(self, color=None, use_alpha=True, parent=None):
|
||||
super(ColorDialog, self).__init__(parent)
|
||||
|
||||
self.setWindowTitle("Color picker dialog")
|
||||
|
||||
picker_widget = ColorPickerWidget(color, self)
|
||||
picker_widget = ColorPickerWidget(color, use_alpha, self)
|
||||
|
||||
footer_widget = QtWidgets.QWidget(self)
|
||||
|
||||
|
|
|
|||
|
|
@ -4,35 +4,6 @@ from Qt import QtWidgets, QtCore, QtGui
|
|||
from .color_view import draw_checkerboard_tile
|
||||
|
||||
|
||||
slide_style = """
|
||||
QSlider::groove:horizontal {
|
||||
background: qlineargradient(
|
||||
x1: 0, y1: 0, x2: 1, y2: 0, stop: 0 #000, stop: 1 #fff
|
||||
);
|
||||
height: 8px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
QSlider::handle:horizontal {
|
||||
background: qlineargradient(
|
||||
x1:0, y1:0, x2:1, y2:1, stop:0 #ddd, stop:1 #bbb
|
||||
);
|
||||
border: 1px solid #777;
|
||||
width: 8px;
|
||||
margin-top: -1px;
|
||||
margin-bottom: -1px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
QSlider::handle:horizontal:hover {
|
||||
background: qlineargradient(
|
||||
x1:0, y1:0, x2:1, y2:1, stop:0 #eee, stop:1 #ddd
|
||||
);
|
||||
border: 1px solid #444;ff
|
||||
border-radius: 4px;
|
||||
}"""
|
||||
|
||||
|
||||
class AlphaSlider(QtWidgets.QSlider):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(AlphaSlider, self).__init__(*args, **kwargs)
|
||||
|
|
@ -80,7 +51,7 @@ class AlphaSlider(QtWidgets.QSlider):
|
|||
|
||||
painter.fillRect(event.rect(), QtCore.Qt.transparent)
|
||||
|
||||
painter.setRenderHint(QtGui.QPainter.SmoothPixmapTransform)
|
||||
painter.setRenderHint(QtGui.QPainter.HighQualityAntialiasing)
|
||||
rect = self.style().subControlRect(
|
||||
QtWidgets.QStyle.CC_Slider,
|
||||
opt,
|
||||
|
|
@ -135,19 +106,8 @@ class AlphaSlider(QtWidgets.QSlider):
|
|||
|
||||
painter.save()
|
||||
|
||||
gradient = QtGui.QRadialGradient()
|
||||
radius = handle_rect.height() / 2
|
||||
center_x = handle_rect.width() / 2 + handle_rect.x()
|
||||
center_y = handle_rect.height()
|
||||
gradient.setCenter(center_x, center_y)
|
||||
gradient.setCenterRadius(radius)
|
||||
gradient.setFocalPoint(center_x, center_y)
|
||||
|
||||
gradient.setColorAt(0.9, QtGui.QColor(127, 127, 127))
|
||||
gradient.setColorAt(1, QtCore.Qt.transparent)
|
||||
|
||||
painter.setPen(QtCore.Qt.NoPen)
|
||||
painter.setBrush(gradient)
|
||||
painter.setBrush(QtGui.QColor(127, 127, 127))
|
||||
painter.drawEllipse(handle_rect)
|
||||
|
||||
painter.restore()
|
||||
|
|
|
|||
|
|
@ -17,19 +17,12 @@ from .color_inputs import (
|
|||
class ColorPickerWidget(QtWidgets.QWidget):
|
||||
color_changed = QtCore.Signal(QtGui.QColor)
|
||||
|
||||
def __init__(self, color=None, parent=None):
|
||||
def __init__(self, color=None, use_alpha=True, parent=None):
|
||||
super(ColorPickerWidget, self).__init__(parent)
|
||||
|
||||
# Color triangle
|
||||
color_triangle = QtColorTriangle(self)
|
||||
|
||||
alpha_slider_proxy = QtWidgets.QWidget(self)
|
||||
alpha_slider = AlphaSlider(QtCore.Qt.Horizontal, alpha_slider_proxy)
|
||||
|
||||
alpha_slider_layout = QtWidgets.QHBoxLayout(alpha_slider_proxy)
|
||||
alpha_slider_layout.setContentsMargins(5, 5, 5, 5)
|
||||
alpha_slider_layout.addWidget(alpha_slider, 1)
|
||||
|
||||
# Eye picked widget
|
||||
pick_widget = PickScreenColorWidget()
|
||||
pick_widget.setMaximumHeight(50)
|
||||
|
|
@ -47,8 +40,6 @@ class ColorPickerWidget(QtWidgets.QWidget):
|
|||
color_view = ColorViewer(self)
|
||||
color_view.setMaximumHeight(50)
|
||||
|
||||
alpha_inputs = AlphaInputs(self)
|
||||
|
||||
color_inputs_color = QtGui.QColor()
|
||||
col_inputs_by_label = [
|
||||
("HEX", HEXInputs(color_inputs_color, self)),
|
||||
|
|
@ -58,6 +49,7 @@ class ColorPickerWidget(QtWidgets.QWidget):
|
|||
]
|
||||
|
||||
layout = QtWidgets.QGridLayout(self)
|
||||
|
||||
empty_col = 1
|
||||
label_col = empty_col + 1
|
||||
input_col = label_col + 1
|
||||
|
|
@ -65,6 +57,9 @@ class ColorPickerWidget(QtWidgets.QWidget):
|
|||
empty_widget.setFixedWidth(10)
|
||||
layout.addWidget(empty_widget, 0, empty_col)
|
||||
|
||||
layout.setColumnStretch(0, 1)
|
||||
layout.setColumnStretch(input_col, 1)
|
||||
|
||||
row = 0
|
||||
layout.addWidget(btn_pick_color, row, label_col)
|
||||
layout.addWidget(color_view, row, input_col)
|
||||
|
|
@ -84,20 +79,41 @@ class ColorPickerWidget(QtWidgets.QWidget):
|
|||
layout.setRowStretch(row, 1)
|
||||
row += 1
|
||||
|
||||
layout.addWidget(alpha_slider_proxy, row, 0)
|
||||
alpha_label = None
|
||||
alpha_slider_proxy = None
|
||||
alpha_slider = None
|
||||
alpha_inputs = None
|
||||
if not use_alpha:
|
||||
color.setAlpha(255)
|
||||
else:
|
||||
alpha_inputs = AlphaInputs(self)
|
||||
alpha_label = QtWidgets.QLabel("Alpha", self)
|
||||
alpha_slider_proxy = QtWidgets.QWidget(self)
|
||||
alpha_slider = AlphaSlider(
|
||||
QtCore.Qt.Horizontal, alpha_slider_proxy
|
||||
)
|
||||
|
||||
alpha_slider_layout = QtWidgets.QHBoxLayout(alpha_slider_proxy)
|
||||
alpha_slider_layout.setContentsMargins(5, 5, 5, 5)
|
||||
alpha_slider_layout.addWidget(alpha_slider, 1)
|
||||
|
||||
layout.addWidget(alpha_slider_proxy, row, 0)
|
||||
|
||||
layout.addWidget(alpha_label, row, label_col)
|
||||
layout.addWidget(alpha_inputs, row, input_col)
|
||||
|
||||
row += 1
|
||||
|
||||
layout.addWidget(QtWidgets.QLabel("Alpha", self), row, label_col)
|
||||
layout.addWidget(alpha_inputs, row, input_col)
|
||||
row += 1
|
||||
layout.setRowStretch(row, 1)
|
||||
|
||||
color_view.set_color(color_triangle.cur_color)
|
||||
|
||||
color_triangle.color_changed.connect(self.triangle_color_changed)
|
||||
alpha_slider.valueChanged.connect(self._on_alpha_slider_change)
|
||||
pick_widget.color_selected.connect(self.on_color_change)
|
||||
alpha_inputs.alpha_changed.connect(self._on_alpha_inputs_changed)
|
||||
btn_pick_color.released.connect(self.pick_color)
|
||||
if alpha_slider:
|
||||
alpha_slider.valueChanged.connect(self._on_alpha_slider_change)
|
||||
alpha_inputs.alpha_changed.connect(self._on_alpha_inputs_changed)
|
||||
|
||||
self.color_input_fields = color_input_fields
|
||||
self.color_inputs_color = color_inputs_color
|
||||
|
|
@ -131,7 +147,8 @@ class ColorPickerWidget(QtWidgets.QWidget):
|
|||
return self.color_view.color()
|
||||
|
||||
def set_color(self, color):
|
||||
self.alpha_inputs.set_alpha(color.alpha())
|
||||
if self.alpha_inputs:
|
||||
self.alpha_inputs.set_alpha(color.alpha())
|
||||
self.on_color_change(color)
|
||||
|
||||
def pick_color(self):
|
||||
|
|
@ -163,10 +180,10 @@ class ColorPickerWidget(QtWidgets.QWidget):
|
|||
|
||||
def alpha_changed(self, value):
|
||||
self.color_view.set_alpha(value)
|
||||
if self.alpha_slider.value() != value:
|
||||
if self.alpha_slider and self.alpha_slider.value() != value:
|
||||
self.alpha_slider.setValue(value)
|
||||
|
||||
if self.alpha_inputs.alpha_value != value:
|
||||
if self.alpha_inputs and self.alpha_inputs.alpha_value != value:
|
||||
self.alpha_inputs.set_alpha(value)
|
||||
|
||||
def _on_alpha_inputs_changed(self, value):
|
||||
|
|
|
|||
|
|
@ -241,7 +241,11 @@ class QtColorTriangle(QtWidgets.QWidget):
|
|||
|
||||
# Blit the static generated background with the hue gradient onto
|
||||
# the double buffer.
|
||||
buf = QtGui.QImage(self.bg_image.copy())
|
||||
buf = QtGui.QImage(
|
||||
self.bg_image.width(),
|
||||
self.bg_image.height(),
|
||||
QtGui.QImage.Format_RGB32
|
||||
)
|
||||
|
||||
# Draw the trigon
|
||||
# Find the color with only the hue, and max value and saturation
|
||||
|
|
@ -254,9 +258,21 @@ class QtColorTriangle(QtWidgets.QWidget):
|
|||
)
|
||||
|
||||
# Slow step: convert the image to a pixmap
|
||||
pix = QtGui.QPixmap.fromImage(buf)
|
||||
pix = self.bg_image.copy()
|
||||
pix_painter = QtGui.QPainter(pix)
|
||||
pix_painter.setRenderHint(QtGui.QPainter.Antialiasing)
|
||||
|
||||
pix_painter.setRenderHint(QtGui.QPainter.HighQualityAntialiasing)
|
||||
|
||||
trigon_path = QtGui.QPainterPath()
|
||||
trigon_path.moveTo(self.point_a)
|
||||
trigon_path.lineTo(self.point_b)
|
||||
trigon_path.lineTo(self.point_c)
|
||||
trigon_path.closeSubpath()
|
||||
pix_painter.setClipPath(trigon_path)
|
||||
|
||||
pix_painter.drawImage(0, 0, buf)
|
||||
|
||||
pix_painter.setClipping(False)
|
||||
|
||||
# Draw an outline of the triangle
|
||||
pix_painter.setPen(self._triangle_outline_pen)
|
||||
|
|
@ -724,27 +740,37 @@ class QtColorTriangle(QtWidgets.QWidget):
|
|||
lx = leftX[y]
|
||||
rx = rightX[y]
|
||||
|
||||
# if the xdist is 0, don't draw anything.
|
||||
xdist = rx - lx
|
||||
if xdist == 0.0:
|
||||
continue
|
||||
|
||||
lxi = int(floor(lx))
|
||||
rxi = int(floor(rx))
|
||||
rc = rightColors[y]
|
||||
lc = leftColors[y]
|
||||
|
||||
# if the xdist is 0, don't draw anything.
|
||||
xdist = rx - lx
|
||||
if xdist != 0.0:
|
||||
r = lc.r
|
||||
g = lc.g
|
||||
b = lc.b
|
||||
rdelta = (rc.r - r) / xdist
|
||||
gdelta = (rc.g - g) / xdist
|
||||
bdelta = (rc.b - b) / xdist
|
||||
r = lc.r
|
||||
g = lc.g
|
||||
b = lc.b
|
||||
rdelta = (rc.r - r) / xdist
|
||||
gdelta = (rc.g - g) / xdist
|
||||
bdelta = (rc.b - b) / xdist
|
||||
|
||||
# Inner loop 2. Draws the line from left to right.
|
||||
for x in range(lxi, rxi + 1):
|
||||
buf.setPixel(x, y, QtGui.qRgb(int(r), int(g), int(b)))
|
||||
r += rdelta
|
||||
g += gdelta
|
||||
b += bdelta
|
||||
# Draw 2 more pixels on left side for smoothing
|
||||
for x in range(lxi - 2, lxi):
|
||||
buf.setPixel(x, y, QtGui.qRgb(int(r), int(g), int(b)))
|
||||
|
||||
# Inner loop 2. Draws the line from left to right.
|
||||
for x in range(lxi, rxi):
|
||||
buf.setPixel(x, y, QtGui.qRgb(int(r), int(g), int(b)))
|
||||
r += rdelta
|
||||
g += gdelta
|
||||
b += bdelta
|
||||
|
||||
# Draw 2 more pixels on right side for smoothing
|
||||
for x in range(rxi, rxi + 3):
|
||||
buf.setPixel(x, y, QtGui.qRgb(int(r), int(g), int(b)))
|
||||
|
||||
def _radius_at(self, pos, rect):
|
||||
mousexdist = pos.x() - float(rect.center().x())
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ def draw_checkerboard_tile(piece_size=None, color_1=None, color_2=None):
|
|||
if piece_size is None:
|
||||
piece_size = 7
|
||||
|
||||
# Make sure piece size is not float
|
||||
piece_size = int(piece_size)
|
||||
if color_1 is None:
|
||||
color_1 = QtGui.QColor(188, 188, 188)
|
||||
|
||||
|
|
|
|||
30
website/docs/admin_hosts_tvpaint.md
Normal file
30
website/docs/admin_hosts_tvpaint.md
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
---
|
||||
id: admin_hosts_tvpaint
|
||||
title: TVPaint
|
||||
sidebar_label: TVPaint
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
## Subset name templates
|
||||
Definition of possibile subset name templates in TVPaint integration.
|
||||
|
||||
### [Render Layer](artist_hosts_tvpaint#render-layer)
|
||||
Render layer has additional keys for subset name template. It is possible to use **render_layer** and **render_pass**.
|
||||
|
||||
- Key **render_layer** is alias for variant (user's input).
|
||||
- For key **render_pass** is used predefined value `"Beauty"` (ATM value can't be changed).
|
||||
|
||||
### [Render pass](artist_hosts_tvpaint#render-pass)
|
||||
Render pass has additional keys for subset name template. It is possible to use **render_layer** and **render_pass**.
|
||||
- Key **render_layer** is filled with value of **render_pass** from `renderLayer` group.
|
||||
- Key **render_pass** is alias for variant (user's input).
|
||||
|
||||
:::important Render Layer/Pass templates
|
||||
It is recommended to use same subset name template for both **renderLayer** and **renderPass** families.
|
||||
- Example template: `"{family}{Task}_{Render_layer}_{Render_pass}"`
|
||||
:::
|
||||
|
||||
### [Review](artist_hosts_tvpaint#review) and [Workfile](artist_hosts_tvpaint#workfile)
|
||||
Families **review** and **workfile** are not manually created but are automatically generated during publishing. That's why it is recommended to not use **variant** key in their subset name template.
|
||||
|
|
@ -45,7 +45,7 @@ In TVPaint you can find the Tools in OpenPype menu extension. The OpenPype Tools
|
|||
|
||||
|
||||
## Create
|
||||
In TVPaint you can create and publish **[Reviews](#review)**, **[Render Passes](#render-pass)**, and **[Render Layers](#render-layer)**.
|
||||
In TVPaint you can create and publish **[Reviews](#review)**, **[Workfile](#workfile)**, **[Render Passes](#render-pass)** and **[Render Layers](#render-layer)**.
|
||||
|
||||
You have the possibility to organize your layers by using `Color group`.
|
||||
|
||||
|
|
@ -67,26 +67,13 @@ OpenPype specifically never tries to guess what you want to publish from the sce
|
|||
|
||||
When you want to publish `review` or `render layer` or `render pass`, open the `Creator` through the Tools menu `Create` button.
|
||||
|
||||
### Review
|
||||
### Review
|
||||
`Review` renders the whole file as is and sends the resulting QuickTime to Ftrack.
|
||||
- Is automatically created during publishing.
|
||||
|
||||
<div class="row markdown">
|
||||
<div class="col col--6 markdown">
|
||||
|
||||
`Review` renders the whole file as is and sends the resulting QuickTime to Ftrack.
|
||||
|
||||
To create reviewable quicktime of your animation:
|
||||
|
||||
- select `Review` in the `Creator`
|
||||
- press `Create`
|
||||
- When you run [publish](#publish), file will be rendered and converted to quicktime.`
|
||||
|
||||
</div>
|
||||
<div class="col col--6 markdown">
|
||||
|
||||

|
||||
|
||||
</div>
|
||||
</div>
|
||||
### Workfile
|
||||
`Workfile` stores the source workfile as is during publishing (e.g. for backup).
|
||||
- Is automatically created during publishing.
|
||||
|
||||
### Render Layer
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 30 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 26 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
|
|
@ -112,6 +112,10 @@ Profile may generate multiple outputs from a single input. Each output must defi
|
|||
| "-10% -200px" | 1800px 800px |
|
||||
| "-10% -0px" | 1800px 1000px |
|
||||
|
||||
- **`Overscan color`**
|
||||
- Color of empty area caused by different aspect ratio of input and output.
|
||||
- By default is set to black color.
|
||||
|
||||
- **`Letter Box`**
|
||||
- **Enabled** - Enable letter boxes
|
||||
- **Ratio** - Ratio of letter boxes
|
||||
|
|
@ -124,6 +128,14 @@ Profile may generate multiple outputs from a single input. Each output must defi
|
|||

|
||||

|
||||
|
||||
- **`Background color`**
|
||||
- Background color can be used for inputs with possible transparency (e.g. png sequence).
|
||||
- Input's without possible alpha channel are ignored all the time (e.g. mov).
|
||||
- Background color slows down rendering process.
|
||||
- set alpha to `0` to not use this option at all (in most of cases background stays black)
|
||||
- other than `0` alpha will draw color as background
|
||||
|
||||
|
||||
### IntegrateAssetNew
|
||||
|
||||
Saves information for all published subsets into DB, published assets are available for other hosts, tools and tasks after.
|
||||
|
|
@ -160,6 +172,39 @@ Applicable context filters:
|
|||
## Tools
|
||||
Settings for OpenPype tools.
|
||||
|
||||
## Creator
|
||||
Settings related to [Creator tool](artist_tools.md#details).
|
||||
|
||||
### Subset name profiles
|
||||

|
||||
|
||||
Subset name helps to identify published content. More specific name helps with organization and avoid mixing of published content. Subset name is defined using one of templates defined in **Subset name profiles settings**. The template is filled with context information at the time of creation.
|
||||
|
||||
Usage of template is defined by profile filtering using creator's family, host and task name. Profile without filters is used as default template and it is recommend to set default template. If default template is not available `"{family}{Task}"` is used.
|
||||
|
||||
**Formatting keys**
|
||||
|
||||
All templates can contain text and formatting keys **family**, **task** and **variant** e.g. `"MyStudio_{family}_{task}"` (example - not recommended in production).
|
||||
|
||||
|Key|Description|
|
||||
|---|---|
|
||||
|family|Creators family|
|
||||
|task|Task under which is creation triggered|
|
||||
|variant|User input in creator tool|
|
||||
|
||||
**Formatting keys have 3 variants with different letter capitalization.**
|
||||
|
||||
|Task|Key variant|Description|Result|
|
||||
|---|---|---|---|
|
||||
|`bgAnim`|`{task}`|Keep original value as is.|`bgAnim`|
|
||||
|`bgAnim`|`{Task}`|Capitalize first letter of value.|`BgAnim`|
|
||||
|`bgAnim`|`{TASK}`|Each letter which be capitalized.|`BGANIM`|
|
||||
|
||||
Template may look like `"{family}{Task}{Variant}"`.
|
||||
|
||||
Some creators may have other keys as their context may require more information or more specific values. Make sure you've read documentation of host you're using.
|
||||
|
||||
|
||||
## Workfiles
|
||||
All settings related to Workfile tool.
|
||||
|
||||
|
|
|
|||
|
|
@ -90,7 +90,8 @@ module.exports = {
|
|||
"admin_hosts_maya",
|
||||
"admin_hosts_resolve",
|
||||
"admin_hosts_harmony",
|
||||
"admin_hosts_aftereffects"
|
||||
"admin_hosts_aftereffects",
|
||||
"admin_hosts_tvpaint"
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue