Merge remote-tracking branch 'upstream/develop' into fusion-new-publisher

This commit is contained in:
Roy Nieterau 2023-03-07 20:43:43 +01:00
commit fca0ff591b
11 changed files with 159 additions and 53 deletions

View file

@ -1,16 +1,9 @@
## Brief description
First sentence is brief description.
## Description
Next paragraf is more elaborate text with more info. This will be displayed for example in collapsed form under the first sentence in a changelog.
## Changelog Description
Paragraphs contain detailed information on the changes made to the product or service, providing an in-depth description of the updates and enhancements. They can be used to explain the reasoning behind the changes, or to highlight the importance of the new features. Paragraphs can often include links to further information or support documentation.
## Additional info
The rest will be ignored in changelog and should contain any additional
technical information.
## Documentation (add _"type: documentation"_ label)
[feature_documentation](future_url_after_it_will_be_merged)
Paragraphs of text giving context of additional technical information or code examples.
## Testing notes:
1. start with this step
2. follow this step
2. follow this step

View file

@ -48,7 +48,6 @@ from openpype.pipeline.colorspace import (
get_imageio_config
)
from openpype.pipeline.workfile import BuildWorkfile
from . import gizmo_menu
from .constants import ASSIST
@ -2678,6 +2677,18 @@ def process_workfile_builder():
open_file(last_workfile_path)
def start_workfile_template_builder():
from .workfile_template_builder import (
build_workfile_template
)
# to avoid looping of the callback, remove it!
log.info("Starting workfile template builder...")
build_workfile_template(workfile_creation_enabled=True)
# remove callback since it would be duplicating the workfile
nuke.removeOnCreate(start_workfile_template_builder, nodeClass="Root")
@deprecated
def recreate_instance(origin_node, avalon_data=None):
"""Recreate input instance to different data

View file

@ -33,6 +33,7 @@ from .lib import (
add_publish_knob,
WorkfileSettings,
process_workfile_builder,
start_workfile_template_builder,
launch_workfiles_app,
check_inventory_versions,
set_avalon_knob_data,
@ -48,7 +49,6 @@ from .workfile_template_builder import (
NukePlaceholderLoadPlugin,
NukePlaceholderCreatePlugin,
build_workfile_template,
update_workfile_template,
create_placeholder,
update_placeholder,
)
@ -156,6 +156,7 @@ def add_nuke_callbacks():
nuke.addOnCreate(
workfile_settings.set_context_settings, nodeClass="Root")
nuke.addOnCreate(workfile_settings.set_favorites, nodeClass="Root")
nuke.addOnCreate(start_workfile_template_builder, nodeClass="Root")
nuke.addOnCreate(process_workfile_builder, nodeClass="Root")
# fix ffmpeg settings on script

View file

@ -1,7 +1,5 @@
import collections
import nuke
from openpype.pipeline import registered_host
from openpype.pipeline.workfile.workfile_template_builder import (
AbstractTemplateBuilder,
@ -14,7 +12,6 @@ from openpype.pipeline.workfile.workfile_template_builder import (
from openpype.tools.workfile_template_build import (
WorkfileBuildPlaceholderDialog,
)
from .lib import (
find_free_space_to_paste_nodes,
get_extreme_positions,
@ -45,7 +42,7 @@ class NukeTemplateBuilder(AbstractTemplateBuilder):
get_template_preset implementation)
Returns:
bool: Wether the template was succesfully imported or not
bool: Wether the template was successfully imported or not
"""
# TODO check if the template is already imported
@ -55,7 +52,6 @@ class NukeTemplateBuilder(AbstractTemplateBuilder):
return True
class NukePlaceholderPlugin(PlaceholderPlugin):
node_color = 4278190335
@ -947,9 +943,9 @@ class NukePlaceholderCreatePlugin(
siblings_input.setInput(0, copy_output)
def build_workfile_template(*args):
def build_workfile_template(*args, **kwargs):
builder = NukeTemplateBuilder(registered_host())
builder.build_template()
builder.build_template(*args, **kwargs)
def update_workfile_template(*args):

View file

@ -13,7 +13,7 @@ def has_unsaved_changes():
def save_file(filepath):
path = filepath.replace("\\", "/")
nuke.scriptSaveAs(path)
nuke.scriptSaveAs(path, overwrite=1)
nuke.Root()["name"].setValue(path)
nuke.Root()["project_directory"].setValue(os.path.dirname(path))
nuke.Root().setModified(False)

View file

@ -95,7 +95,8 @@ def update_op_assets(
op_asset = create_op_asset(item)
insert_result = dbcon.insert_one(op_asset)
item_doc = get_asset_by_id(
project_name, insert_result.inserted_id)
project_name, insert_result.inserted_id
)
# Update asset
item_data = deepcopy(item_doc["data"])
@ -133,39 +134,47 @@ def update_op_assets(
try:
fps = float(item_data.get("fps"))
except (TypeError, ValueError):
fps = float(gazu_project.get(
"fps", project_doc["data"].get("fps", 25)))
fps = float(
gazu_project.get("fps", project_doc["data"].get("fps", 25))
)
item_data["fps"] = fps
# Resolution, fall back to project default
match_res = re.match(
r"(\d+)x(\d+)",
item_data.get("resolution", gazu_project.get("resolution"))
item_data.get("resolution", gazu_project.get("resolution")),
)
if match_res:
item_data["resolutionWidth"] = int(match_res.group(1))
item_data["resolutionHeight"] = int(match_res.group(2))
else:
item_data["resolutionWidth"] = project_doc["data"].get(
"resolutionWidth")
"resolutionWidth"
)
item_data["resolutionHeight"] = project_doc["data"].get(
"resolutionHeight")
"resolutionHeight"
)
# Properties that doesn't fully exist in Kitsu.
# Guessing those property names below:
# Pixel Aspect Ratio
item_data["pixelAspect"] = item_data.get(
"pixel_aspect", project_doc["data"].get("pixelAspect"))
"pixel_aspect", project_doc["data"].get("pixelAspect")
)
# Handle Start
item_data["handleStart"] = item_data.get(
"handle_start", project_doc["data"].get("handleStart"))
"handle_start", project_doc["data"].get("handleStart")
)
# Handle End
item_data["handleEnd"] = item_data.get(
"handle_end", project_doc["data"].get("handleEnd"))
"handle_end", project_doc["data"].get("handleEnd")
)
# Clip In
item_data["clipIn"] = item_data.get(
"clip_in", project_doc["data"].get("clipIn"))
"clip_in", project_doc["data"].get("clipIn")
)
# Clip Out
item_data["clipOut"] = item_data.get(
"clip_out", project_doc["data"].get("clipOut"))
"clip_out", project_doc["data"].get("clipOut")
)
# Tasks
tasks_list = []
@ -175,11 +184,9 @@ def update_op_assets(
elif item_type == "Shot":
tasks_list = gazu.task.all_tasks_for_shot(item)
item_data["tasks"] = {
item_data["tasks"] = {
t["task_type_name"]: {
"type": t["task_type_name"],
"zou": gazu.task.get_task(t["id"]),
}
t["task_type_name"]: {
"type": t["task_type_name"],
"zou": gazu.task.get_task(t["id"]),
}
for t in tasks_list
}
@ -218,7 +225,9 @@ def update_op_assets(
if parent_zou_id_dict is not None:
visual_parent_doc_id = (
parent_zou_id_dict.get("_id")
if parent_zou_id_dict else None)
if parent_zou_id_dict
else None
)
if visual_parent_doc_id is None:
# Find root folder doc ("Assets" or "Shots")
@ -345,7 +354,8 @@ def write_project_to_op(project: dict, dbcon: AvalonMongoDB) -> UpdateOne:
def sync_all_projects(
login: str, password: str, ignore_projects: list = None):
login: str, password: str, ignore_projects: list = None
):
"""Update all OP projects in DB with Zou data.
Args:
@ -390,7 +400,7 @@ def sync_project_from_kitsu(dbcon: AvalonMongoDB, project: dict):
if not project:
project = gazu.project.get_project_by_name(project["name"])
log.info("Synchronizing {}...".format(project['name']))
log.info("Synchronizing {}...".format(project["name"]))
# Get all assets from zou
all_assets = gazu.asset.all_assets_for_project(project)
@ -473,8 +483,11 @@ def sync_project_from_kitsu(dbcon: AvalonMongoDB, project: dict):
[
UpdateOne({"_id": id}, update)
for id, update in update_op_assets(
dbcon, project, project_dict,
all_entities, zou_ids_and_asset_docs
dbcon,
project,
project_dict,
all_entities,
zou_ids_and_asset_docs,
)
]
)

View file

@ -28,6 +28,7 @@ from openpype.settings import (
get_project_settings,
get_system_settings,
)
from openpype.host import IWorkfileHost
from openpype.host import HostBase
from openpype.lib import (
Logger,
@ -440,7 +441,9 @@ class AbstractTemplateBuilder(object):
self,
template_path=None,
level_limit=None,
keep_placeholders=None
keep_placeholders=None,
create_first_version=None,
workfile_creation_enabled=False
):
"""Main callback for building workfile from template path.
@ -457,6 +460,11 @@ class AbstractTemplateBuilder(object):
keep_placeholders (bool): Add flag to placeholder data for
hosts to decide if they want to remove
placeholder after it is used.
create_first_version (bool): create first version of a workfile
workfile_creation_enabled (bool): If True, it might create
first version but ignore
process if version is created
"""
template_preset = self.get_template_preset()
@ -465,6 +473,30 @@ class AbstractTemplateBuilder(object):
if keep_placeholders is None:
keep_placeholders = template_preset["keep_placeholder"]
if create_first_version is None:
create_first_version = template_preset["create_first_version"]
# check if first version is created
created_version_workfile = self.create_first_workfile_version()
# if first version is created, import template
# and populate placeholders
if (
create_first_version
and workfile_creation_enabled
and created_version_workfile
):
self.import_template(template_path)
self.populate_scene_placeholders(
level_limit, keep_placeholders)
# save workfile after template is populated
self.save_workfile(created_version_workfile)
# ignore process if first workfile is enabled
# but a version is already created
if workfile_creation_enabled:
return
self.import_template(template_path)
self.populate_scene_placeholders(
@ -516,6 +548,39 @@ class AbstractTemplateBuilder(object):
pass
def create_first_workfile_version(self):
"""
Create first version of workfile.
Should load the content of template into scene so
'populate_scene_placeholders' can be started.
Args:
template_path (str): Fullpath for current task and
host's template file.
"""
last_workfile_path = os.environ.get("AVALON_LAST_WORKFILE")
self.log.info("__ last_workfile_path: {}".format(last_workfile_path))
if os.path.exists(last_workfile_path):
# ignore in case workfile existence
self.log.info("Workfile already exists, skipping creation.")
return False
# Create first version
self.log.info("Creating first version of workfile.")
self.save_workfile(last_workfile_path)
# Confirm creation of first version
return last_workfile_path
def save_workfile(self, workfile_path):
"""Save workfile in current host."""
# Save current scene, continue to open file
if isinstance(self.host, IWorkfileHost):
self.host.save_workfile(workfile_path)
else:
self.host.save_file(workfile_path)
def _prepare_placeholders(self, placeholders):
"""Run preparation part for placeholders on plugins.
@ -699,6 +764,8 @@ class AbstractTemplateBuilder(object):
# switch to remove placeholders after they are used
keep_placeholder = profile.get("keep_placeholder")
create_first_version = profile.get("create_first_version")
# backward compatibility, since default is True
if keep_placeholder is None:
keep_placeholder = True
@ -732,7 +799,8 @@ class AbstractTemplateBuilder(object):
self.log.info("Found template at: '{}'".format(path))
return {
"path": path,
"keep_placeholder": keep_placeholder
"keep_placeholder": keep_placeholder,
"create_first_version": create_first_version
}
solved_path = None
@ -761,7 +829,8 @@ class AbstractTemplateBuilder(object):
return {
"path": solved_path,
"keep_placeholder": keep_placeholder
"keep_placeholder": keep_placeholder,
"create_first_version": create_first_version
}

View file

@ -565,7 +565,17 @@
]
},
"templated_workfile_build": {
"profiles": []
"profiles": [
{
"task_types": [
"Compositing"
],
"task_names": [],
"path": "{project[name]}/templates/comp.nk",
"keep_placeholder": true,
"create_first_version": true
}
]
},
"filters": {}
}

View file

@ -34,6 +34,12 @@
"label": "Keep placeholders",
"type": "boolean",
"default": true
},
{
"key": "create_first_version",
"label": "Create first version",
"type": "boolean",
"default": true
}
]
}

View file

@ -186,7 +186,7 @@ class AttributeDefinitionsWidget(QtWidgets.QWidget):
class _BaseAttrDefWidget(QtWidgets.QWidget):
# Type 'object' may not work with older PySide versions
value_changed = QtCore.Signal(object, uuid.UUID)
value_changed = QtCore.Signal(object, str)
def __init__(self, attr_def, parent):
super(_BaseAttrDefWidget, self).__init__(parent)

View file

@ -98,15 +98,22 @@ class Popup(QtWidgets.QDialog):
height = window.height()
height = max(height, window.sizeHint().height())
desktop_geometry = QtWidgets.QDesktopWidget().availableGeometry()
screen_geometry = window.geometry()
try:
screen = window.screen()
desktop_geometry = screen.availableGeometry()
except AttributeError:
# Backwards compatibility for older Qt versions
# PySide6 removed QDesktopWidget
desktop_geometry = QtWidgets.QDesktopWidget().availableGeometry()
screen_width = screen_geometry.width()
screen_height = screen_geometry.height()
window_geometry = window.geometry()
screen_width = window_geometry.width()
screen_height = window_geometry.height()
# Calculate width and height of system tray
systray_width = screen_geometry.width() - desktop_geometry.width()
systray_height = screen_geometry.height() - desktop_geometry.height()
systray_width = window_geometry.width() - desktop_geometry.width()
systray_height = window_geometry.height() - desktop_geometry.height()
padding = 10