diff --git a/pype/lib.py b/pype/lib.py index 12ec1f5584..c097ad1f10 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: diff --git a/pype/nuke/lib.py b/pype/nuke/lib.py index b2df6580de..14fa255c9b 100644 --- a/pype/nuke/lib.py +++ b/pype/nuke/lib.py @@ -379,6 +379,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 create_backdrop(label="", color=None, layer=0, nodes=None): """ @@ -665,87 +673,80 @@ class WorkfileSettings(object): def reset_resolution(self): """Set resolution to project resolution.""" log.info("Reseting 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', {}) - width = self._asset_entity.get('data', {}).get("resolutionWidth") - height = self._asset_entity.get('data', {}).get("resolutionHeight") - pixel_aspect = self._asset_entity.get('data', {}).get("pixelAspect") + data = { + "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"] + } - 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)) - return + 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) + ) bbox = self._asset_entity.get('data', {}).get('crop') 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 self._project["name"] in str(f.name()): - used_formats.append(f) - else: - format_name = self._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( - self._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 = self.make_format_string(**crnt_fmt_kargs) + existing_format.setX(data["x"]) + existing_format.setY(data["y"]) + existing_format.setR(data["r"]) + existing_format.setT(data["t"]) + else: + format_string = self.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), - }) + nuke.root()["format"].setValue(data["name"]) + log.info("Format is set.") - new_fmt_str = self.make_format_string(**new_fmt_kargs) - - if new_fmt_str not in crnt_fmt_str: - self.make_format(frm_str=new_fmt_str, - project_name=new_fmt_kargs["project_name"]) - - log.info("Format is set") - - def make_format_string(self, **args): - if args.get("r"): + def make_format_string(self, **kwargs): + if kwargs.get("r"): return ( "{width} " "{height} " @@ -753,21 +754,17 @@ class WorkfileSettings(object): "{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(self, **args): - log.info("Format does't exist, will create: \n{}".format(args)) - nuke.addFormat("{frm_str} " - "{project_name}".format(**args)) - self._root_node["format"].setValue("{project_name}".format(**args)) - def set_context_settings(self): # replace reset resolution from avalon core to pype's self.reset_resolution() @@ -1108,7 +1105,7 @@ class BuildWorkfile(WorkfileSettings): """ assert isinstance(nodes, list), "`nodes` should be a list of nodes" layer = self.pos_layer + layer - + create_backdrop(label=label, color=color, layer=layer, nodes=nodes) def position_reset(self, xpos=0, ypos=0): 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("\\", "/") 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) diff --git a/pype/plugins/nuke/create/create_write.py b/pype/plugins/nuke/create/create_write.py index c3da555259..a129367f52 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,6 +69,9 @@ class CreateWriteRender(avalon.nuke.Creator): write_data.update({ "fpath_template": "{work}/renders/nuke/{subset}/{subset}.{frame}.{ext}"}) + # Deadline tab. + add_deadline_tab(node) + return create_write_node(self.data["subset"], write_data) 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/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, 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