From 6bf3c9ca71c9160033794d1e117a114f273ffa49 Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Mon, 5 Aug 2019 14:02:20 +0100 Subject: [PATCH 1/7] Fix and simplify reset_resolution - Format was not being set. - Existing code logic was overtly complicated. --- pype/nuke/lib.py | 130 ++++++++++++++++++++--------------------------- 1 file changed, 54 insertions(+), 76 deletions(-) diff --git a/pype/nuke/lib.py b/pype/nuke/lib.py index b0842d4ab1..7bc41540c0 100644 --- a/pype/nuke/lib.py +++ b/pype/nuke/lib.py @@ -315,13 +315,12 @@ def create_write_node(name, data, prenodes=None): tile_color = _data.get("tile_color", "0xff0000ff") GN["tile_color"].setValue(tile_color) - # add render button lnk = nuke.Link_Knob("Render") lnk.makeLink(write_node.name(), "Render") lnk.setName("Render") GN.addKnob(lnk) - + return GN @@ -511,14 +510,21 @@ def reset_resolution(): asset = api.Session["AVALON_ASSET"] asset = io.find_one({"name": asset, "type": "asset"}) - width = asset.get('data', {}).get("resolutionWidth") - height = asset.get('data', {}).get("resolutionHeight") - pixel_aspect = asset.get('data', {}).get("pixelAspect") + data = { + "width": int(asset.get('data', {}).get('resolution_width')), + "height": int(asset.get('data', {}).get('resolution_height')), + "pixel_aspect": asset.get('data', {}).get('pixel_aspect', 1), + "name": project["name"] + } - log.info("pixel_aspect: {}".format(pixel_aspect)) - if any(not x for x in [width, height, pixel_aspect]): - log.error("Missing set shot attributes in DB. \nContact your supervisor!. \n\nWidth: `{0}` \nHeight: `{1}` \nPixel Asspect: `{2}`".format( - width, height, pixel_aspect)) + if any(x for x in data.values() if x is None): + log.error( + "Missing set shot attributes in DB." + "\nContact your supervisor!." + "\n\nWidth: `{width}`" + "\nHeight: `{height}`" + "\nPixel Asspect: `{pixel_aspect}`".format(**data) + ) return bbox = asset.get('data', {}).get('crop') @@ -526,72 +532,49 @@ def reset_resolution(): if bbox: try: x, y, r, t = bbox.split(".") + data.update( + { + "x": int(x), + "y": int(y), + "r": int(r), + "t": int(t), + } + ) except Exception as e: bbox = None - log.error("{}: {} \nFormat:Crop need to be set with dots, example: " - "0.0.1920.1080, /nSetting to default".format(__name__, e)) + log.error( + "{}: {} \nFormat:Crop need to be set with dots, example: " + "0.0.1920.1080, /nSetting to default".format(__name__, e) + ) - used_formats = list() - for f in nuke.formats(): - if project["name"] in str(f.name()): - used_formats.append(f) - else: - format_name = project["name"] + "_1" + existing_format = None + for format in nuke.formats(): + if data["name"] == format.name(): + existing_format = format + break + + if existing_format: + # Enforce existing format to be correct. + existing_format.setWidth(data["width"]) + existing_format.setHeight(data["height"]) + existing_format.setPixelAspect(data["pixel_aspect"]) - crnt_fmt_str = "" - if used_formats: - check_format = used_formats[-1] - format_name = "{}_{}".format( - project["name"], - int(used_formats[-1].name()[-1]) + 1 - ) - log.info( - "Format exists: {}. " - "Will create new: {}...".format( - used_formats[-1].name(), - format_name) - ) - crnt_fmt_kargs = { - "width": (check_format.width()), - "height": (check_format.height()), - "pixelAspect": float(check_format.pixelAspect()) - } if bbox: - crnt_fmt_kargs.update({ - "x": int(check_format.x()), - "y": int(check_format.y()), - "r": int(check_format.r()), - "t": int(check_format.t()), - }) - crnt_fmt_str = make_format_string(**crnt_fmt_kargs) - log.info("crnt_fmt_str: {}".format(crnt_fmt_str)) + existing_format.setX(data["x"]) + existing_format.setY(data["y"]) + existing_format.setR(data["r"]) + existing_format.setT(data["t"]) + else: + format_string = make_format_string(**data) + log.info("Creating new format: {}".format(format_string)) + nuke.addFormat(format_string) - new_fmt_kargs = { - "width": int(width), - "height": int(height), - "pixelAspect": float(pixel_aspect), - "project_name": format_name - } - if bbox: - new_fmt_kargs.update({ - "x": int(x), - "y": int(y), - "r": int(r), - "t": int(t), - }) - - new_fmt_str = make_format_string(**new_fmt_kargs) - log.info("new_fmt_str: {}".format(new_fmt_str)) - - if new_fmt_str not in crnt_fmt_str: - make_format(frm_str=new_fmt_str, - project_name=new_fmt_kargs["project_name"]) - - log.info("Format is set") + nuke.root()["format"].setValue(data["name"]) + log.info("Format is set.") -def make_format_string(**args): - if args.get("r"): +def make_format_string(**kwargs): + if kwargs.get("r"): return ( "{width} " "{height} " @@ -599,23 +582,18 @@ def make_format_string(**args): "{y} " "{r} " "{t} " - "{pixelAspect:.2f}".format(**args) + "{pixel_aspect:.2f} " + "{name}".format(**kwargs) ) else: return ( "{width} " "{height} " - "{pixelAspect:.2f}".format(**args) + "{pixel_aspect:.2f} " + "{name}".format(**kwargs) ) -def make_format(**args): - log.info("Format does't exist, will create: \n{}".format(args)) - nuke.addFormat("{frm_str} " - "{project_name}".format(**args)) - nuke.root()["format"].setValue("{project_name}".format(**args)) - - def set_context_settings(): # replace reset resolution from avalon core to pype's reset_resolution() From 0ddc260455922fd57676eca2261cc9d98d925a0b Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Wed, 21 Aug 2019 14:04:31 +0100 Subject: [PATCH 2/7] The version pattern needs to find at least 1 digit. Previous behaviour with "*" meant any "v" would return, so couldn't have any "v" in the path. --- pype/lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/lib.py b/pype/lib.py index 6eee38f6d8..e5bcdda69f 100644 --- a/pype/lib.py +++ b/pype/lib.py @@ -421,7 +421,7 @@ def get_version_from_path(file): v: version number in string ('001') """ - pattern = re.compile(r"[\._]v([0-9]*)") + pattern = re.compile(r"[\._]v([0-9]+)") try: return pattern.findall(file)[0] except IndexError: From 661501547332f5a7c852912c6940b856f043e8f3 Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Wed, 21 Aug 2019 14:16:06 +0100 Subject: [PATCH 3/7] Code cleanup and has_unsaved_changes. --- pype/nukestudio/__init__.py | 1 - pype/nukestudio/workio.py | 37 ++++++++++--------------------------- 2 files changed, 10 insertions(+), 28 deletions(-) diff --git a/pype/nukestudio/__init__.py b/pype/nukestudio/__init__.py index c900848a93..9283e732af 100644 --- a/pype/nukestudio/__init__.py +++ b/pype/nukestudio/__init__.py @@ -1,7 +1,6 @@ import os from pypeapp import Logger import hiero -from avalon.tools import workfiles from avalon import api as avalon from pyblish import api as pyblish diff --git a/pype/nukestudio/workio.py b/pype/nukestudio/workio.py index 7fbd85a708..eadd8322cd 100644 --- a/pype/nukestudio/workio.py +++ b/pype/nukestudio/workio.py @@ -1,19 +1,22 @@ -"""Host API required Work Files tool""" import os + import hiero +from avalon import api + def file_extensions(): return [".hrox"] def has_unsaved_changes(): - return hiero.core.projects()[-1] + # There are no methods for querying unsaved changes to a project, so + # enforcing to always save. + return True def save(filepath): project = hiero.core.projects()[-1] - if project: project.saveAs(filepath) else: @@ -22,40 +25,20 @@ def save(filepath): def open(filepath): - try: - hiero.core.openProject(filepath) - return True - except Exception as e: - try: - from PySide.QtGui import * - from PySide.QtCore import * - except: - from PySide2.QtGui import * - from PySide2.QtWidgets import * - from PySide2.QtCore import * - - prompt = "Cannot open the selected file: `{}`".format(e) - hiero.core.log.error(prompt) - dialog = QMessageBox.critical( - hiero.ui.mainWindow(), "Error", unicode(prompt)) + hiero.core.openProject(filepath) + return True def current_file(): - import os - import hiero - current_file = hiero.core.projects()[-1].path() normalised = os.path.normpath(current_file) # Unsaved current file - if normalised is '': - return "NOT SAVED" + if normalised == "": + return None return normalised - def work_root(): - from avalon import api - return os.path.normpath(api.Session["AVALON_WORKDIR"]).replace("\\", "/") From 62bee8eafaccb897efa04d5beb7cba8f3d628c3b Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Wed, 21 Aug 2019 15:04:57 +0100 Subject: [PATCH 4/7] Create/Validate Deadline tab with ChunkSize. --- pype/nuke/lib.py | 8 ++++ pype/plugins/nuke/create/create_write.py | 9 ++-- pype/plugins/nuke/publish/collect_writes.py | 6 +++ .../publish/validate_write_deadline_tab.py | 42 +++++++++++++++++++ 4 files changed, 61 insertions(+), 4 deletions(-) create mode 100644 pype/plugins/nuke/publish/validate_write_deadline_tab.py diff --git a/pype/nuke/lib.py b/pype/nuke/lib.py index 82244afdb5..9fddee5490 100644 --- a/pype/nuke/lib.py +++ b/pype/nuke/lib.py @@ -378,6 +378,14 @@ def add_rendering_knobs(node): return node +def add_deadline_tab(node): + node.addKnob(nuke.Tab_Knob("Deadline")) + + knob = nuke.Int_Knob("deadlineChunkSize", "Chunk Size") + knob.setValue(1) + node.addKnob(knob) + + def set_viewers_colorspace(viewer): ''' Adds correct colorspace to viewer diff --git a/pype/plugins/nuke/create/create_write.py b/pype/plugins/nuke/create/create_write.py index 03107238b5..51ffa9e986 100644 --- a/pype/plugins/nuke/create/create_write.py +++ b/pype/plugins/nuke/create/create_write.py @@ -1,7 +1,7 @@ from collections import OrderedDict import avalon.api import avalon.nuke -from pype.nuke.lib import create_write_node +from pype.nuke.lib import create_write_node, add_deadline_tab from pype import api as pype from pypeapp import config @@ -51,7 +51,7 @@ class CreateWriteRender(avalon.nuke.Creator): node = 'write' instance = nuke.toNode(self.data["subset"]) - + node = None if not instance: write_data = { "class": node, @@ -69,9 +69,10 @@ class CreateWriteRender(avalon.nuke.Creator): write_data.update({ "fpath_template": "{work}/renders/nuke/{subset}/{subset}.{frame}.{ext}"}) - create_write_node(self.data["subset"], write_data) + node = create_write_node(self.data["subset"], write_data) - return + # Deadline tab. + add_deadline_tab(node) class CreateWritePrerender(avalon.nuke.Creator): diff --git a/pype/plugins/nuke/publish/collect_writes.py b/pype/plugins/nuke/publish/collect_writes.py index 7104e3bd05..29ae6cb929 100644 --- a/pype/plugins/nuke/publish/collect_writes.py +++ b/pype/plugins/nuke/publish/collect_writes.py @@ -101,6 +101,11 @@ class CollectNukeWrites(pyblish.api.InstancePlugin): "fps": instance.context.data["fps"] } + group_node = [x for x in instance if x.Class() == "Group"][0] + deadlineChunkSize = 1 + if "deadlineChunkSize" in group_node.knobs(): + deadlineChunkSize = group_node["deadlineChunkSize"].value() + instance.data.update({ "versionData": version_data, "path": path, @@ -112,6 +117,7 @@ class CollectNukeWrites(pyblish.api.InstancePlugin): "frameEnd": last_frame, "outputType": output_type, "colorspace": node["colorspace"].value(), + "deadlineChunkSize": deadlineChunkSize }) self.log.debug("instance.data: {}".format(instance.data)) diff --git a/pype/plugins/nuke/publish/validate_write_deadline_tab.py b/pype/plugins/nuke/publish/validate_write_deadline_tab.py new file mode 100644 index 0000000000..0c222a164a --- /dev/null +++ b/pype/plugins/nuke/publish/validate_write_deadline_tab.py @@ -0,0 +1,42 @@ +import pyblish.api +import pype.nuke.lib + + +class RepairNukeWriteDeadlineTab(pyblish.api.Action): + + label = "Repair" + icon = "wrench" + on = "failed" + + def process(self, context, plugin): + + # Get the errored instances + failed = [] + for result in context.data["results"]: + if (result["error"] is not None and result["instance"] is not None + and result["instance"] not in failed): + failed.append(result["instance"]) + + # Apply pyblish.logic to get the instances for the plug-in + instances = pyblish.api.instances_by_plugin(failed, plugin) + + for instance in instances: + group_node = [x for x in instance if x.Class() == "Group"][0] + pype.nuke.lib.add_deadline_tab(group_node) + + +class ValidateNukeWriteDeadlineTab(pyblish.api.InstancePlugin): + """Ensure Deadline tab is present and current.""" + + order = pyblish.api.ValidatorOrder + label = "Deadline Tab" + hosts = ["nuke"] + optional = True + families = ["write"] + actions = [RepairNukeWriteDeadlineTab] + + def process(self, instance): + group_node = [x for x in instance if x.Class() == "Group"][0] + + msg = "Deadline tab missing on \"{}\"".format(group_node.name()) + assert "Deadline" in group_node.knobs(), msg From 5f87708ef84a68eb2e6d6cc87d28e1bb70b3c2d9 Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Wed, 21 Aug 2019 15:20:24 +0100 Subject: [PATCH 5/7] Submit to Deadline as well. --- pype/plugins/nuke/publish/submit_nuke_deadline.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pype/plugins/nuke/publish/submit_nuke_deadline.py b/pype/plugins/nuke/publish/submit_nuke_deadline.py index 0017de3ec4..ef971f3a37 100644 --- a/pype/plugins/nuke/publish/submit_nuke_deadline.py +++ b/pype/plugins/nuke/publish/submit_nuke_deadline.py @@ -84,6 +84,7 @@ class NukeSubmitDeadline(pyblish.api.InstancePlugin): start=int(instance.data["frameStart"]), end=int(instance.data["frameEnd"]) ), + "ChunkSize": instance.data["deadlineChunkSize"], "Comment": comment, From bb0035ecdd8e0bc090af8da4124e452787b9c44a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Mon, 26 Aug 2019 14:55:32 +0000 Subject: [PATCH 6/7] validate workspace giving false negatives because of drive letter case --- pype/plugins/maya/publish/validate_scene_set_workspace.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/plugins/maya/publish/validate_scene_set_workspace.py b/pype/plugins/maya/publish/validate_scene_set_workspace.py index 778c7eae86..bda397cf2a 100644 --- a/pype/plugins/maya/publish/validate_scene_set_workspace.py +++ b/pype/plugins/maya/publish/validate_scene_set_workspace.py @@ -12,7 +12,7 @@ def is_subdir(path, root_dir): root_dir = os.path.realpath(root_dir) # If not on same drive - if os.path.splitdrive(path)[0] != os.path.splitdrive(root_dir)[0]: + if os.path.splitdrive(path)[0].lower() != os.path.splitdrive(root_dir)[0].lower(): # noqa: E501 return False # Get 'relative path' (can contain ../ which means going up) From fb09c554afa5ad0449ebaf3e059f08b1f8f8c60a Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 29 Aug 2019 12:20:07 +0200 Subject: [PATCH 7/7] fixing to new way of attributes name convention --- pype/nuke/lib.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/pype/nuke/lib.py b/pype/nuke/lib.py index 7bc41540c0..ec6d9514f4 100644 --- a/pype/nuke/lib.py +++ b/pype/nuke/lib.py @@ -509,11 +509,18 @@ def reset_resolution(): project = io.find_one({"type": "project"}) asset = api.Session["AVALON_ASSET"] asset = io.find_one({"name": asset, "type": "asset"}) + asset_data = asset.get('data', {}) data = { - "width": int(asset.get('data', {}).get('resolution_width')), - "height": int(asset.get('data', {}).get('resolution_height')), - "pixel_aspect": asset.get('data', {}).get('pixel_aspect', 1), + "width": int(asset_data.get( + 'resolutionWidth', + asset_data.get('resolution_width'))), + "height": int(asset_data.get( + 'resolutionHeight', + asset_data.get('resolution_height'))), + "pixel_aspect": asset_data.get( + 'pixelAspect', + asset_data.get('pixel_aspect', 1)), "name": project["name"] }