Merge branch 'develop' into enhancement/OP-6191_3dsmax-switching-versions-of-maxScene-maintain-parentagelinks

This commit is contained in:
Kayla Man 2023-09-13 18:19:13 +08:00
commit 199d099a59
28 changed files with 560 additions and 180 deletions

View file

@ -35,6 +35,8 @@ body:
label: Version
description: What version are you running? Look to OpenPype Tray
options:
- 3.16.7-nightly.1
- 3.16.6
- 3.16.6-nightly.1
- 3.16.5
- 3.16.5-nightly.5
@ -133,8 +135,6 @@ body:
- 3.14.9
- 3.14.9-nightly.5
- 3.14.9-nightly.4
- 3.14.9-nightly.3
- 3.14.9-nightly.2
validations:
required: true
- type: dropdown

View file

@ -1,6 +1,243 @@
# Changelog
## [3.16.6](https://github.com/ynput/OpenPype/tree/3.16.6)
[Full Changelog](https://github.com/ynput/OpenPype/compare/3.16.5...3.16.6)
### **🆕 New features**
<details>
<summary>Workfiles tool: Refactor workfiles tool (for AYON) <a href="https://github.com/ynput/OpenPype/pull/5550">#5550</a></summary>
Refactored workfiles tool to new tool. Separated backend and frontend logic. Refactored logic is AYON-centric and is used only in AYON mode, so it does not affect OpenPype.
___
</details>
<details>
<summary>AfterEffects: added validator for missing files in FootageItems <a href="https://github.com/ynput/OpenPype/pull/5590">#5590</a></summary>
Published composition in AE could contain multiple FootageItems as a layers. If FootageItem contains imported file and it doesn't exist, render triggered by Publish process will silently fail and no output is generated. This could cause failure later in the process with unclear reason. (In `ExtractReview`).This PR adds validation to protect from this.
___
</details>
### **🚀 Enhancements**
<details>
<summary>Maya: Yeti Cache Include viewport preview settings from source <a href="https://github.com/ynput/OpenPype/pull/5561">#5561</a></summary>
When publishing and loading yeti caches persist the display output and preview colors + settings to ensure consistency in the view
___
</details>
<details>
<summary>Houdini: validate colorspace in review rop <a href="https://github.com/ynput/OpenPype/pull/5322">#5322</a></summary>
Adding a validator that checks if 'OCIO Colorspace' parameter on review rop was set to a valid value.It is a step towards managing colorspace in review ropvalid values are the ones in the dropdown menuthis validator also provides some helper actions This PR is related to #4836 and #4833
___
</details>
<details>
<summary>Colorspace: adding abstraction of publishing related functions <a href="https://github.com/ynput/OpenPype/pull/5497">#5497</a></summary>
The functionality of Colorspace has been abstracted for greater usability.
___
</details>
<details>
<summary>Nuke: removing redundant workfile colorspace attributes <a href="https://github.com/ynput/OpenPype/pull/5580">#5580</a></summary>
Nuke root workfile colorspace data type knobs are long time configured automatically via config roles or the default values are also working well. Therefore there is no need for pipeline managed knobs.
___
</details>
<details>
<summary>Ftrack: Less verbose logs for Ftrack integration in artist facing logs <a href="https://github.com/ynput/OpenPype/pull/5596">#5596</a></summary>
- Reduce artist-facing logs for component integration for Ftrack
- Avoid "Comment is not set" log in artist facing report for Kitsu and Ftrack
- Remove info log about `ffprobe` inspecting a file (changed to debug log)
- interesting to see however that it ffprobes the same jpeg twice - but maybe once for thumbnail?
___
</details>
### **🐛 Bug fixes**
<details>
<summary>Maya: Fix rig validators for new out_SET and controls_SET names <a href="https://github.com/ynput/OpenPype/pull/5595">#5595</a></summary>
Fix usage of `out_SET` and `controls_SET` since #5310 because they can now be prefixed by the subset name.
___
</details>
<details>
<summary>TrayPublisher: set default frame values to sequential data <a href="https://github.com/ynput/OpenPype/pull/5530">#5530</a></summary>
We are inheriting default frame handles and fps data either from project or setting them to 0. This is just for case a production will decide not to injest the sequential representations with asset based metadata.
___
</details>
<details>
<summary>Publisher: Screenshot opacity value fix <a href="https://github.com/ynput/OpenPype/pull/5576">#5576</a></summary>
Fix opacity value.
___
</details>
<details>
<summary>AfterEffects: fix imports of image sequences <a href="https://github.com/ynput/OpenPype/pull/5581">#5581</a></summary>
#4602 broke imports of image sequences.
___
</details>
<details>
<summary>AYON: Fix representation context conversion <a href="https://github.com/ynput/OpenPype/pull/5591">#5591</a></summary>
Do not fix `"folder"` key in representation context until it is needed.
___
</details>
<details>
<summary>ayon-nuke: default factory to lists <a href="https://github.com/ynput/OpenPype/pull/5594">#5594</a></summary>
Default factory were missing in settings schemas for complicated objects like lists and it was causing settings to be failing saving.
___
</details>
<details>
<summary>Maya: Fix look assigner showing no asset if 'not found' representations are present <a href="https://github.com/ynput/OpenPype/pull/5597">#5597</a></summary>
Fix Maya Look assigner failing to show any content if it finds an invalid container for which it can't find the asset in the current project. (This can happen when e.g. loading something from a library project).There was logic already to avoid this but there was a bug where it used variable `_id` which did not exist and likely had to be `asset_id`.I've fixed that and improved the logged message a bit, e.g.:
```
// Warning: openpype.hosts.maya.tools.mayalookassigner.commands : Id found on 22 nodes for which no asset is found database, skipping '641d78ec85c3c5b102e836b0'
```
Example not found representation in Loader:The issue isn't necessarily related to NOT FOUND representations but in essence boils down to finding nodes with asset ids that do not exist in the current project which could very well just be local meshes in your scene.**Note:**I've excluded logging the nodes themselves because that tends to be a very long list of nodes. Only downside to removing that is that it's unclear which nodes are related to that `id`. If there are any ideas on how to still provide a concise informational message about that that'd be great so I could add it. Things I had considered:
- Report the containers, issue here is that it's about asset ids on nodes which don't HAVE to be in containers - it could be local geometry
- Report the namespaces, issue here is that it could be nodes without namespaces (plus potentially not about ALL nodes in a namespace)
- Report the short names of the nodes; it's shorter and readable but still likely a lot of nodes.@tokejepsen @LiborBatek any other ideas?
___
</details>
<details>
<summary>Photoshop: fixed blank Flatten image <a href="https://github.com/ynput/OpenPype/pull/5600">#5600</a></summary>
Flatten image is simplified publishing approach where all visible layers are "flatten" and published together. This image could be used as a reference etc.This is implemented by auto creator which wasn't updated after first publish. This would result in missing newly created layers after `auto_image` instance was created.
___
</details>
<details>
<summary>Blender: Remove Hardcoded Subset Name for Reviews <a href="https://github.com/ynput/OpenPype/pull/5603">#5603</a></summary>
Fixes hardcoded subset name for Reviews in Blender.
___
</details>
<details>
<summary>TVPaint: Fix tool callbacks <a href="https://github.com/ynput/OpenPype/pull/5608">#5608</a></summary>
Do not wait for callback to finish.
___
</details>
### **🔀 Refactored code**
<details>
<summary>Chore: Remove unused variables and cleanup <a href="https://github.com/ynput/OpenPype/pull/5588">#5588</a></summary>
Removing some unused variables. In some cases the unused variables _seemed like they should've been used - maybe?_ so please **double check the code whether it doesn't hint to an already existing bug**.Also tweaked some other small bugs in code + tweaked logging levels.
___
</details>
### **Merged pull requests**
<details>
<summary>Chore: Loader log deprecation warning for 'fname' attribute <a href="https://github.com/ynput/OpenPype/pull/5587">#5587</a></summary>
Since https://github.com/ynput/OpenPype/pull/4602 the `fname` attribute on the `LoaderPlugin` should've been deprecated and set for removal over time. However, no deprecation warning was logged whatsoever and thus one usage appears to have sneaked in (fixed with this PR) and a new one tried to sneak in with a recent PR
___
</details>
## [3.16.5](https://github.com/ynput/OpenPype/tree/3.16.5)

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ExtensionManifest Version="8.0" ExtensionBundleId="com.openpype.AE.panel" ExtensionBundleVersion="1.0.26"
<ExtensionManifest Version="8.0" ExtensionBundleId="com.openpype.AE.panel" ExtensionBundleVersion="1.0.27"
ExtensionBundleName="com.openpype.AE.panel" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ExtensionList>
<Extension Id="com.openpype.AE.panel" Version="1.0" />
@ -10,22 +10,22 @@
<!-- Photoshop -->
<!--<Host Name="PHXS" Version="[14.0,19.0]" /> -->
<!-- <Host Name="PHSP" Version="[14.0,19.0]" /> -->
<!-- Illustrator -->
<!-- <Host Name="ILST" Version="[18.0,22.0]" /> -->
<!-- InDesign -->
<!-- <Host Name="IDSN" Version="[10.0,13.0]" /> -->
<!-- <Host Name="IDSN" Version="[10.0,13.0]" /> -->
<!-- Premiere -->
<!-- <Host Name="PPRO" Version="[8.0,12.0]" /> -->
<!-- AfterEffects -->
<Host Name="AEFT" Version="[13.0,99.0]" />
<!-- PRELUDE -->
<!-- PRELUDE -->
<!-- <Host Name="PRLD" Version="[3.0,7.0]" /> -->
<!-- FLASH Pro -->
<!-- <Host Name="FLPR" Version="[14.0,18.0]" /> -->
@ -63,7 +63,7 @@
<Height>550</Height>
<Width>400</Width>
</MaxSize>-->
</Geometry>
<Icons>
<Icon Type="Normal">./icons/iconNormal.png</Icon>
@ -71,9 +71,9 @@
<Icon Type="Disabled">./icons/iconDisabled.png</Icon>
<Icon Type="DarkNormal">./icons/iconDarkNormal.png</Icon>
<Icon Type="DarkRollOver">./icons/iconDarkRollover.png</Icon>
</Icons>
</Icons>
</UI>
</DispatchInfo>
</Extension>
</DispatchInfoList>
</ExtensionManifest>
</ExtensionManifest>

View file

@ -215,6 +215,8 @@ function _getItem(item, comps, folders, footages){
* Refactor
*/
var item_type = '';
var path = '';
var containing_comps = [];
if (item instanceof FolderItem){
item_type = 'folder';
if (!folders){
@ -222,10 +224,18 @@ function _getItem(item, comps, folders, footages){
}
}
if (item instanceof FootageItem){
item_type = 'footage';
if (!footages){
return "{}";
}
item_type = 'footage';
if (item.file){
path = item.file.fsName;
}
if (item.usedIn){
for (j = 0; j < item.usedIn.length; ++j){
containing_comps.push(item.usedIn[j].id);
}
}
}
if (item instanceof CompItem){
item_type = 'comp';
@ -236,7 +246,9 @@ function _getItem(item, comps, folders, footages){
var item = {"name": item.name,
"id": item.id,
"type": item_type};
"type": item_type,
"path": path,
"containing_comps": containing_comps};
return JSON.stringify(item);
}

View file

@ -37,6 +37,9 @@ class AEItem(object):
height = attr.ib(default=None)
is_placeholder = attr.ib(default=False)
uuid = attr.ib(default=False)
path = attr.ib(default=False) # path to FootageItem to validate
# list of composition Footage is in
containing_comps = attr.ib(factory=list)
class AfterEffectsServerStub():
@ -704,7 +707,10 @@ class AfterEffectsServerStub():
d.get("instance_id"),
d.get("width"),
d.get("height"),
d.get("is_placeholder"))
d.get("is_placeholder"),
d.get("uuid"),
d.get("path"),
d.get("containing_comps"),)
ret.append(item)
return ret

View file

@ -31,13 +31,8 @@ class FileLoader(api.AfterEffectsLoader):
path = self.filepath_from_context(context)
repr_cont = context["representation"]["context"]
if "#" not in path:
frame = repr_cont.get("frame")
if frame:
padding = len(frame)
path = path.replace(frame, "#" * padding)
import_options['sequence'] = True
if len(context["representation"]["files"]) > 1:
import_options['sequence'] = True
if not path:
repr_id = context["representation"]["_id"]

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<root>
<error id="main">
<title>Footage item missing</title>
<description>
## Footage item missing
FootageItem `{name}` contains missing `{path}`. Render will not produce any frames and AE will stop react to any integration
### How to repair?
Remove `{name}` or provide missing file.
</description>
</error>
</root>

View file

@ -0,0 +1,49 @@
# -*- coding: utf-8 -*-
"""Validate presence of footage items in composition
Requires:
"""
import os
import pyblish.api
from openpype.pipeline import (
PublishXmlValidationError
)
from openpype.hosts.aftereffects.api import get_stub
class ValidateFootageItems(pyblish.api.InstancePlugin):
"""
Validates if FootageItems contained in composition exist.
AE fails silently and doesn't render anything if footage item file is
missing. This will result in nonresponsiveness of AE UI as it expects
reaction from user, but it will not provide dialog.
This validator tries to check existence of the files.
It will not protect from missing frame in multiframes though
(as AE api doesn't provide this information and it cannot be told how many
frames should be there easily). Missing frame is replaced by placeholder.
"""
order = pyblish.api.ValidatorOrder
label = "Validate Footage Items"
families = ["render.farm", "render.local", "render"]
hosts = ["aftereffects"]
optional = True
def process(self, instance):
"""Plugin entry point."""
comp_id = instance.data["comp_id"]
for footage_item in get_stub().get_items(comps=False, folders=False,
footages=True):
self.log.info(footage_item)
if comp_id not in footage_item.containing_comps:
continue
path = footage_item.path
if path and not os.path.exists(path):
msg = f"File {path} not found."
formatting = {"name": footage_item.name, "path": path}
raise PublishXmlValidationError(self, msg,
formatting_data=formatting)

View file

@ -39,15 +39,11 @@ class CollectReview(pyblish.api.InstancePlugin):
]
if not instance.data.get("remove"):
task = instance.context.data["task"]
# Store focal length in `burninDataMembers`
burninData = instance.data.setdefault("burninDataMembers", {})
burninData["focalLength"] = focal_length
instance.data.update({
"subset": f"{task}Review",
"review_camera": camera,
"frameStart": instance.context.data["frameStart"],
"frameEnd": instance.context.data["frameEnd"],

View file

@ -4,6 +4,7 @@ from openpype.lib import BoolDef
import openpype.hosts.photoshop.api as api
from openpype.hosts.photoshop.lib import PSAutoCreator
from openpype.pipeline.create import get_subset_name
from openpype.lib import prepare_template_data
from openpype.client import get_asset_by_name
@ -37,19 +38,14 @@ class AutoImageCreator(PSAutoCreator):
asset_doc = get_asset_by_name(project_name, asset_name)
if existing_instance is None:
subset_name = get_subset_name(
self.family, self.default_variant, task_name, asset_doc,
subset_name = self.get_subset_name(
self.default_variant, task_name, asset_doc,
project_name, host_name
)
publishable_ids = [layer.id for layer in api.stub().get_layers()
if layer.visible]
data = {
"asset": asset_name,
"task": task_name,
# ids are "virtual" layers, won't get grouped as 'members' do
# same difference in color coded layers in WP
"ids": publishable_ids
}
if not self.active_on_create:
@ -69,8 +65,8 @@ class AutoImageCreator(PSAutoCreator):
existing_instance["asset"] != asset_name
or existing_instance["task"] != task_name
):
subset_name = get_subset_name(
self.family, self.default_variant, task_name, asset_doc,
subset_name = self.get_subset_name(
self.default_variant, task_name, asset_doc,
project_name, host_name
)
@ -118,3 +114,29 @@ class AutoImageCreator(PSAutoCreator):
Artist might disable this instance from publishing or from creating
review for it though.
"""
def get_subset_name(
self,
variant,
task_name,
asset_doc,
project_name,
host_name=None,
instance=None
):
dynamic_data = prepare_template_data({"layer": "{layer}"})
subset_name = get_subset_name(
self.family, variant, task_name, asset_doc,
project_name, host_name, dynamic_data=dynamic_data
)
return self._clean_subset_name(subset_name)
def _clean_subset_name(self, subset_name):
"""Clean all variants leftover {layer} from subset name."""
dynamic_data = prepare_template_data({"layer": "{layer}"})
for value in dynamic_data.values():
if value in subset_name:
return (subset_name.replace(value, "")
.replace("__", "_")
.replace("..", "."))
return subset_name

View file

@ -94,12 +94,17 @@ class ImageCreator(Creator):
name = self._clean_highlights(stub, directory)
layer_names_in_hierarchy.append(name)
data.update({"subset": subset_name})
data.update({"members": [str(group.id)]})
data.update({"layer_name": layer_name})
data.update({"long_name": "_".join(layer_names_in_hierarchy)})
data_update = {
"subset": subset_name,
"members": [str(group.id)],
"layer_name": layer_name,
"long_name": "_".join(layer_names_in_hierarchy)
}
data.update(data_update)
creator_attributes = {"mark_for_review": self.mark_for_review}
mark_for_review = (pre_create_data.get("mark_for_review") or
self.mark_for_review)
creator_attributes = {"mark_for_review": mark_for_review}
data.update({"creator_attributes": creator_attributes})
if not self.active_on_create:
@ -124,8 +129,6 @@ class ImageCreator(Creator):
if creator_id == self.identifier:
instance_data = self._handle_legacy(instance_data)
layer = api.stub().get_layer(instance_data["members"][0])
instance_data["layer"] = layer
instance = CreatedInstance.from_existing(
instance_data, self
)

View file

@ -0,0 +1,24 @@
import pyblish.api
from openpype.hosts.photoshop import api as photoshop
class CollectAutoImageRefresh(pyblish.api.ContextPlugin):
"""Refreshes auto_image instance with currently visible layers..
"""
label = "Collect Auto Image Refresh"
order = pyblish.api.CollectorOrder
hosts = ["photoshop"]
order = pyblish.api.CollectorOrder + 0.2
def process(self, context):
for instance in context:
creator_identifier = instance.data.get("creator_identifier")
if creator_identifier and creator_identifier == "auto_image":
self.log.debug("Auto image instance found, won't create new")
# refresh existing auto image instance with current visible
publishable_ids = [layer.id for layer in photoshop.stub().get_layers() # noqa
if layer.visible]
instance.data["ids"] = publishable_ids
return

View file

@ -0,0 +1,20 @@
import pyblish.api
from openpype.hosts.photoshop import api
class CollectImage(pyblish.api.InstancePlugin):
"""Collect layer metadata into a instance.
Used later in validation
"""
order = pyblish.api.CollectorOrder + 0.200
label = 'Collect Image'
hosts = ["photoshop"]
families = ["image"]
def process(self, instance):
if instance.data.get("members"):
layer = api.stub().get_layer(instance.data["members"][0])
instance.data["layer"] = layer

View file

@ -45,9 +45,11 @@ class ExtractImage(pyblish.api.ContextPlugin):
# Perform extraction
files = {}
ids = set()
layer = instance.data.get("layer")
if layer:
ids.add(layer.id)
# real layers and groups
members = instance.data("members")
if members:
ids.update(set([int(member) for member in members]))
# virtual groups collected by color coding or auto_image
add_ids = instance.data.pop("ids", None)
if add_ids:
ids.update(set(add_ids))

View file

@ -1,4 +1,5 @@
import os
import shutil
from PIL import Image
from openpype.lib import (
@ -55,6 +56,7 @@ class ExtractReview(publish.Extractor):
}
if instance.data["family"] != "review":
self.log.debug("Existing extracted file from image family used.")
# enable creation of review, without this jpg review would clash
# with jpg of the image family
output_name = repre_name
@ -62,8 +64,15 @@ class ExtractReview(publish.Extractor):
repre_skeleton.update({"name": repre_name,
"outputName": output_name})
if self.make_image_sequence and len(layers) > 1:
self.log.info("Extract layers to image sequence.")
img_file = self.output_seq_filename % 0
self._prepare_file_for_image_family(img_file, instance,
staging_dir)
repre_skeleton.update({
"files": img_file,
})
processed_img_names = [img_file]
elif self.make_image_sequence and len(layers) > 1:
self.log.debug("Extract layers to image sequence.")
img_list = self._save_sequence_images(staging_dir, layers)
repre_skeleton.update({
@ -72,17 +81,17 @@ class ExtractReview(publish.Extractor):
"fps": fps,
"files": img_list,
})
instance.data["representations"].append(repre_skeleton)
processed_img_names = img_list
else:
self.log.info("Extract layers to flatten image.")
img_list = self._save_flatten_image(staging_dir, layers)
self.log.debug("Extract layers to flatten image.")
img_file = self._save_flatten_image(staging_dir, layers)
repre_skeleton.update({
"files": img_list,
"files": img_file,
})
instance.data["representations"].append(repre_skeleton)
processed_img_names = [img_list]
processed_img_names = [img_file]
instance.data["representations"].append(repre_skeleton)
ffmpeg_args = get_ffmpeg_tool_args("ffmpeg")
@ -111,6 +120,35 @@ class ExtractReview(publish.Extractor):
self.log.info(f"Extracted {instance} to {staging_dir}")
def _prepare_file_for_image_family(self, img_file, instance, staging_dir):
"""Converts existing file for image family to .jpg
Image instance could have its own separate review (instance per layer
for example). This uses extracted file instead of extracting again.
Args:
img_file (str): name of output file (with 0000 value for ffmpeg
later)
instance:
staging_dir (str): temporary folder where extracted file is located
"""
repre_file = instance.data["representations"][0]
source_file_path = os.path.join(repre_file["stagingDir"],
repre_file["files"])
if not os.path.exists(source_file_path):
raise RuntimeError(f"{source_file_path} doesn't exist for "
"review to create from")
_, ext = os.path.splitext(repre_file["files"])
if ext != ".jpg":
im = Image.open(source_file_path)
# without this it produces messy low quality jpg
rgb_im = Image.new("RGBA", (im.width, im.height), "#ffffff")
rgb_im.alpha_composite(im)
rgb_im.convert("RGB").save(os.path.join(staging_dir, img_file))
else:
# handles already .jpg
shutil.copy(source_file_path,
os.path.join(staging_dir, img_file))
def _generate_mov(self, ffmpeg_path, instance, fps, no_of_frames,
source_files_pattern, staging_dir):
"""Generates .mov to upload to Ftrack.
@ -218,6 +256,11 @@ class ExtractReview(publish.Extractor):
(list) of PSItem
"""
layers = []
# creating review for existing 'image' instance
if instance.data["family"] == "image" and instance.data.get("layer"):
layers.append(instance.data["layer"])
return layers
for image_instance in instance.context:
if image_instance.data["family"] != "image":
continue

View file

@ -11,7 +11,7 @@ import filecmp
import tempfile
import threading
import shutil
from queue import Queue
from contextlib import closing
from aiohttp import web
@ -319,19 +319,19 @@ class QtTVPaintRpc(BaseTVPaintRpc):
async def workfiles_tool(self):
log.info("Triggering Workfile tool")
item = MainThreadItem(self.tools_helper.show_workfiles)
self._execute_in_main_thread(item)
self._execute_in_main_thread(item, wait=False)
return
async def loader_tool(self):
log.info("Triggering Loader tool")
item = MainThreadItem(self.tools_helper.show_loader)
self._execute_in_main_thread(item)
self._execute_in_main_thread(item, wait=False)
return
async def publish_tool(self):
log.info("Triggering Publish tool")
item = MainThreadItem(self.tools_helper.show_publisher_tool)
self._execute_in_main_thread(item)
self._execute_in_main_thread(item, wait=False)
return
async def scene_inventory_tool(self):
@ -350,13 +350,13 @@ class QtTVPaintRpc(BaseTVPaintRpc):
async def library_loader_tool(self):
log.info("Triggering Library loader tool")
item = MainThreadItem(self.tools_helper.show_library_loader)
self._execute_in_main_thread(item)
self._execute_in_main_thread(item, wait=False)
return
async def experimental_tools(self):
log.info("Triggering Library loader tool")
item = MainThreadItem(self.tools_helper.show_experimental_tools_dialog)
self._execute_in_main_thread(item)
self._execute_in_main_thread(item, wait=False)
return
async def _async_execute_in_main_thread(self, item, **kwargs):
@ -867,7 +867,7 @@ class QtCommunicator(BaseCommunicator):
def __init__(self, qt_app):
super().__init__()
self.callback_queue = Queue()
self.callback_queue = collections.deque()
self.qt_app = qt_app
def _create_routes(self):
@ -880,14 +880,14 @@ class QtCommunicator(BaseCommunicator):
def execute_in_main_thread(self, main_thread_item, wait=True):
"""Add `MainThreadItem` to callback queue and wait for result."""
self.callback_queue.put(main_thread_item)
self.callback_queue.append(main_thread_item)
if wait:
return main_thread_item.wait()
return
async def async_execute_in_main_thread(self, main_thread_item, wait=True):
"""Add `MainThreadItem` to callback queue and wait for result."""
self.callback_queue.put(main_thread_item)
self.callback_queue.append(main_thread_item)
if wait:
return await main_thread_item.async_wait()
@ -904,9 +904,9 @@ class QtCommunicator(BaseCommunicator):
self._exit()
return None
if self.callback_queue.empty():
return None
return self.callback_queue.get()
if self.callback_queue:
return self.callback_queue.popleft()
return None
def _on_client_connect(self):
super()._on_client_connect()

View file

@ -573,56 +573,6 @@ void FAR PASCAL PI_Close( PIFilter* iFilter )
}
/**************************************************************************************/
// we have something to do !
int FAR PASCAL PI_Parameters( PIFilter* iFilter, char* iArg )
{
if( !iArg )
{
// If the requester is not open, we open it.
if( Data.mReq == 0)
{
// Create empty requester because menu items are defined with
// `define_menu` callback
DWORD req = TVOpenFilterReqEx(
iFilter,
185,
20,
NULL,
NULL,
PIRF_STANDARD_REQ | PIRF_COLLAPSABLE_REQ,
FILTERREQ_NO_TBAR
);
if( req == 0 )
{
TVWarning( iFilter, TXT_REQUESTER_ERROR );
return 0;
}
Data.mReq = req;
// This is a very simple requester, so we create it's content right here instead
// of waiting for the PICBREQ_OPEN message...
// Not recommended for more complex requesters. (see the other examples)
// Sets the title of the requester.
TVSetReqTitle( iFilter, Data.mReq, TXT_REQUESTER );
// Request to listen to ticks
TVGrabTicks(iFilter, req, PITICKS_FLAG_ON);
}
else
{
// If it is already open, we just put it on front of all other requesters.
TVReqToFront( iFilter, Data.mReq );
}
}
return 1;
}
int newMenuItemsProcess(PIFilter* iFilter) {
// Menu items defined with `define_menu` should be propagated.
@ -702,6 +652,62 @@ int newMenuItemsProcess(PIFilter* iFilter) {
return 1;
}
/**************************************************************************************/
// we have something to do !
int FAR PASCAL PI_Parameters( PIFilter* iFilter, char* iArg )
{
if( !iArg )
{
// If the requester is not open, we open it.
if( Data.mReq == 0)
{
// Create empty requester because menu items are defined with
// `define_menu` callback
DWORD req = TVOpenFilterReqEx(
iFilter,
185,
20,
NULL,
NULL,
PIRF_STANDARD_REQ | PIRF_COLLAPSABLE_REQ,
FILTERREQ_NO_TBAR
);
if( req == 0 )
{
TVWarning( iFilter, TXT_REQUESTER_ERROR );
return 0;
}
Data.mReq = req;
// This is a very simple requester, so we create it's content right here instead
// of waiting for the PICBREQ_OPEN message...
// Not recommended for more complex requesters. (see the other examples)
// Sets the title of the requester.
TVSetReqTitle( iFilter, Data.mReq, TXT_REQUESTER );
// Request to listen to ticks
TVGrabTicks(iFilter, req, PITICKS_FLAG_ON);
if ( Data.firstParams == true ) {
Data.firstParams = false;
} else {
newMenuItemsProcess(iFilter);
}
}
else
{
// If it is already open, we just put it on front of all other requesters.
TVReqToFront( iFilter, Data.mReq );
}
}
return 1;
}
/**************************************************************************************/
// something happened that needs our attention.
// Global variable where current button up data are stored

View file

@ -28,11 +28,7 @@
"colorManagement": "Nuke",
"OCIO_config": "nuke-default",
"workingSpaceLUT": "linear",
"monitorLut": "sRGB",
"int8Lut": "sRGB",
"int16Lut": "sRGB",
"logLut": "Cineon",
"floatLut": "linear"
"monitorLut": "sRGB"
},
"nodes": {
"requiredNodes": [

View file

@ -106,26 +106,6 @@
"type": "text",
"key": "monitorLut",
"label": "monitor"
},
{
"type": "text",
"key": "int8Lut",
"label": "8-bit files"
},
{
"type": "text",
"key": "int16Lut",
"label": "16-bit files"
},
{
"type": "text",
"key": "logLut",
"label": "log files"
},
{
"type": "text",
"key": "floatLut",
"label": "float files"
}
]
}

View file

@ -1,3 +1,3 @@
# -*- coding: utf-8 -*-
"""Package declaring Pype version."""
__version__ = "3.16.6-nightly.1"
__version__ = "3.16.7-nightly.1"

View file

@ -1,6 +1,6 @@
[tool.poetry]
name = "OpenPype"
version = "3.16.5" # OpenPype
version = "3.16.6" # OpenPype
description = "Open VFX and Animation pipeline with support."
authors = ["OpenPype Team <info@openpype.io>"]
license = "MIT License"

View file

@ -61,22 +61,6 @@ def ocio_configs_switcher_enum():
class WorkfileColorspaceSettings(BaseSettingsModel):
"""Nuke workfile colorspace preset. """
"""# TODO: enhance settings with host api:
we need to add mapping to resolve properly keys.
Nuke is excpecting camel case key names,
but for better code consistency we need to
be using snake_case:
color_management = colorManagement
ocio_config = OCIO_config
working_space_name = workingSpaceLUT
monitor_name = monitorLut
monitor_out_name = monitorOutLut
int_8_name = int8Lut
int_16_name = int16Lut
log_name = logLut
float_name = floatLut
"""
colorManagement: Literal["Nuke", "OCIO"] = Field(
title="Color Management"
@ -95,18 +79,6 @@ class WorkfileColorspaceSettings(BaseSettingsModel):
monitorLut: str = Field(
title="Monitor"
)
int8Lut: str = Field(
title="8-bit files"
)
int16Lut: str = Field(
title="16-bit files"
)
logLut: str = Field(
title="Log files"
)
floatLut: str = Field(
title="Float files"
)
class ReadColorspaceRulesItems(BaseSettingsModel):
@ -233,10 +205,6 @@ DEFAULT_IMAGEIO_SETTINGS = {
"OCIO_config": "nuke-default",
"workingSpaceLUT": "linear",
"monitorLut": "sRGB",
"int8Lut": "sRGB",
"int16Lut": "sRGB",
"logLut": "Cineon",
"floatLut": "linear"
},
"nodes": {
"requiredNodes": [

View file

@ -18,6 +18,10 @@ Location: Settings > Project > AfterEffects
## Publish plugins
### Collect Review
Enable/disable creation of auto instance of review.
### Validate Scene Settings
#### Skip Resolution Check for Tasks
@ -28,6 +32,10 @@ Set regex pattern(s) to look for in a Task name to skip resolution check against
Set regex pattern(s) to look for in a Task name to skip `frameStart`, `frameEnd` check against values from DB.
### ValidateContainers
By default this validator will look loaded items with lower version than latest. This validator is context wide so it must be disabled in Context button.
### AfterEffects Submit to Deadline
* `Use Published scene` - Set to True (green) when Deadline should take published scene as a source instead of uploaded local one.

View file

@ -33,7 +33,6 @@ Provides list of [variants](artist_concepts.md#variant) that will be shown to an
Provides simplified publishing process. It will create single `image` instance for artist automatically. This instance will
produce flatten image from all visible layers in a workfile.
- Subset template for flatten image - provide template for subset name for this instance (example `imageBeauty`)
- Review - should be separate review created for this instance
### Create Review
@ -111,11 +110,11 @@ Set Byte limit for review file. Applicable if gigantic `image` instances are pro
#### Extract jpg Options
Handles tags for produced `.jpg` representation. `Create review` and `Add review to Ftrack` are defaults.
Handles tags for produced `.jpg` representation. `Create review` and `Add review to Ftrack` are defaults.
#### Extract mov Options
Handles tags for produced `.mov` representation. `Create review` and `Add review to Ftrack` are defaults.
Handles tags for produced `.mov` representation. `Create review` and `Add review to Ftrack` are defaults.
### Workfile Builder
@ -124,4 +123,4 @@ Allows to open prepared workfile for an artist when no workfile exists. Useful t
Could be configured per `Task type`, eg. `composition` task type could use different `.psd` template file than `art` task.
Workfile template must be accessible for all artists.
(Currently not handled by [SiteSync](module_site_sync.md))
(Currently not handled by [SiteSync](module_site_sync.md))

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Before After
Before After