diff --git a/app.py b/app.py index 25c4fd8..02d645c 100644 --- a/app.py +++ b/app.py @@ -148,7 +148,9 @@ def get_node_render_template(self, node): """ Return the render template for the specified node """ - return self.__write_node_handler.get_render_template(node) + write_type = self.__write_node_handler.get_node_write_type_name(node) + + return self.__write_node_handler.get_render_template(node, write_type) def get_node_publish_template(self, node): """ @@ -221,21 +223,17 @@ def __add_write_node_commands(self, context=None): Creates write node menu entries for all write node configurations """ context = context or self.context - + write_type = "Version" write_node_icon = os.path.join(self.disk_location, "resources", "tk2_write.png") - for profile_name in self.__write_node_handler.profile_names: # add to toolbar menu - cb_fn = lambda pn=profile_name: self.__write_node_handler.create_new_node(pn) + cb_fn = lambda pn=profile_name, wt=write_type: self.__write_node_handler.create_new_node(pn,wt) self.engine.register_command( - "%s [Shotgun]" % profile_name, + "%s" % profile_name, cb_fn, dict( type="node", icon=write_node_icon, context=context, ) - ) - - - + ) \ No newline at end of file diff --git a/gizmos/WriteTank.gizmo b/gizmos/WriteTank.gizmo index 73f3eb8..5c10cc2 100644 --- a/gizmos/WriteTank.gizmo +++ b/gizmos/WriteTank.gizmo @@ -21,9 +21,9 @@ version 6.3 # # Additional knob flags # --------------------- -# These are set/removed by doing +/-FLAG_NAME, e.g. +STARTLINE to ensure the knob starts a -# new line in the property editor. A full list of flags can be found here: -# http://docs.thefoundry.co.uk/nuke/63/ndkdevguide/knobs-and-handles/knobflags.html +# These are set/removed by doing +/-FLAG_NAME, e.g. +STARTLINE to ensure the knob starts a +# new line in the property editor.A full list of flags can be found here: +# http://docs.thefoundry.co.uk/nuke/63/ndkdevguide/knobs-and-handles/knobflags.html # ########################################################################################## ########################################################################################## @@ -36,23 +36,40 @@ Gizmo { l "Shotgun Write" } addUserKnob { - 4 tk_profile_list - l "Profile" - M {} - t "Select the Profile to use for this node" - +DO_NOT_WRITE + 1 precomp_render_template + l "Sgtk Precomp Template" + t "The Sgtk precomp template associated with this node" + +INVISIBLE } addUserKnob { - 1 tank_channel - l "Output" - t "Choose an output name for this Write Node to help identify it when you have more than one output in your scene." - +STARTLINE + 1 element_render_template + l "Sgtk Elements Template" + t "The Sgtk elements template associated with this node" + +INVISIBLE } addUserKnob { - 6 tk_use_name_as_channel - l "Use node name" - t "Use the node name in place of the output name" - -STARTLINE + 1 denoise_render_template + l "Sgtk Denoise Template" + t "The Sgtk denoise template associated with this node" + +INVISIBLE + } + addUserKnob { + 1 cleanup_render_template + l "Sgtk Cleanup Template" + t "The Sgtk cleanup template associated with this node" + +INVISIBLE + } + addUserKnob { + 1 final_render_template + l "Sgtk Final Template" + t "The Sgtk Final template associated with this node" + +INVISIBLE + } + addUserKnob { + 1 test_render_template + l "Sgtk Test Template" + t "The Sgtk test template associated with this node" + +INVISIBLE } addUserKnob { 6 tk_is_fully_constructed @@ -61,7 +78,7 @@ Gizmo { +DO_NOT_WRITE +STARTLINE +INVISIBLE - } + } addUserKnob { 1 profile_name l "Profile Name" @@ -103,32 +120,31 @@ Gizmo { l "Sgtk Cached Proxy Path" t "The path for this write node when rendering in proxy mode" +INVISIBLE - } + } addUserKnob { 1 tk_last_known_script l "Last Known Script" t "The last known script this Write node was saved in - used to determine if the script is being saved as a new file or not" +INVISIBLE - } + } addUserKnob { 1 tk_file_type l "File Type" t "The file type to be used for the write node output - this cached value is used if the profile can't be determined" +INVISIBLE - } + } addUserKnob { 1 tk_file_type_settings l "File Type Settings" t "A dictionary of file type settings to be applied to the Write node - this cached value is used if the profile can't be determined" +INVISIBLE - } + } addUserKnob { 1 tk_write_node_settings l "Write Node Settings" t "A string of tcl containing a record of settings for the internal write node at save time." +INVISIBLE - } - + } addUserKnob { 26 "" l "" @@ -158,7 +174,6 @@ Gizmo { T " " t "A preview of the file location generated by the node." } - addUserKnob { 26 path_warning l " " @@ -167,16 +182,16 @@ Gizmo { +STARTLINE } addUserKnob { - 22 reset_path - l "Reset Path" - T "import nuke\nif hasattr(nuke, \"_shotgun_write_node_handler\"):\n nuke._shotgun_write_node_handler.on_reset_render_path_gizmo_callback()" + 26 files_warning + l " " + T " " + t "" +STARTLINE } - addUserKnob { - 26 "" - l "" - T " " + 22 reset_path + l "Reset Path" + T "import nuke\nif hasattr(nuke, \"_shotgun_write_node_handler\"):\n nuke._shotgun_write_node_handler.on_reset_render_path_gizmo_callback()" +STARTLINE } addUserKnob { @@ -188,17 +203,121 @@ Gizmo { } addUserKnob { 22 tk_copy_path - l "Copy Path to Clipboard" - T "import nuke\nif hasattr(nuke, \"_shotgun_write_node_handler\"):\n nuke._shotgun_write_node_handler.on_copy_path_to_clipboard_gizmo_callback()" + l "Copy Path" + T "import nuke\nif hasattr(nuke, \"_shotgun_write_node_handler\"):\n nuke._shotgun_write_node_handler.on_copy_path_to_clipboard_gizmo_callback(False)" + t "Copies the current render/proxy path to the clipboard" + -STARTLINE + } + addUserKnob { + 22 tk_copy_path_win + l "Copy Path - Windows" + T "import nuke\nif hasattr(nuke, \"_shotgun_write_node_handler\"):\n nuke._shotgun_write_node_handler.on_copy_path_to_clipboard_gizmo_callback(True)" t "Copies the current render/proxy path to the clipboard" -STARTLINE } - addUserKnob { 26 "" l "" +STARTLINE } + addUserKnob { + 4 write_type + l "SG Write Type" + t "Select the type of node you require this to be." + M {Version Precomp Element Denoise Cleanup Final Test} + } + addUserKnob { + 22 write_type_info + l "Info" + T " " + } + addUserKnob { + 1 write_type_cache + l "SG Write Type Cache" + t "The cached Write Type for this node" + +INVISIBLE + } + addUserKnob { + 25 "" + - STARTLINE + } + addUserKnob { + 4 tk_profile_list + l "Profile" + M {} + t "Select the Profile to use for this node" + -STARTLINE + +DO_NOT_WRITE + } + addUserKnob { + 1 tank_channel + l "Detail" + t "Choose an output name for this Write Node to help identify it when you have more than one output in your scene." + +STARTLINE + -INVISIBLE + } + addUserKnob { + 1 tank_channel_cache + l "" + t "Cache location for user entered text to change the output naming for write nodes" + +STARTLINE + +INVISIBLE + } + addUserKnob { + 6 tk_use_name_as_channel + l "Use node name" + t "Use the node name in place of the output name" + -STARTLINE + +INVISIBLE + } + addUserKnob { + 26 version_divider + l "" + +STARTLINE + -INVISIBLE + } + addUserKnob { + 26 latest_version + l "Latest Version:" + t "Shows current task context of the script" + T " " + } + addUserKnob { + 26 version_date + l "Version Date:" + t "Shows current task context of the script" + T " " + +STARTLINE + } + addUserKnob { + 26 version_description + l "Description:" + t "Shows description of the latest version" + T " " + +STARTLINE + } + addUserKnob { + 32 sync_from_sg + l "Sync Global frames from SG" + T " " + +STARTLINE + } + addUserKnob { + 25 "" + - STARTLINE + } + addUserKnob { + 32 refresh_version_info + l "Refresh Info" + T " " + -STARTLINE + } + addUserKnob { + 26 "" + l "" + +STARTLINE + } + addUserKnob { 41 channels T Write1.channels @@ -233,7 +352,6 @@ Gizmo { 41 views T Write1.views } - addUserKnob { 26 "" l "" @@ -253,6 +371,7 @@ Gizmo { } addUserKnob { 41 Render + l "Local Render" T Write1.Render -STARTLINE } @@ -273,7 +392,6 @@ Gizmo { T Write1.use_limit -STARTLINE } - addUserKnob { 41 reading l "read file" @@ -296,7 +414,6 @@ Gizmo { T Write1.reload -STARTLINE } - addUserKnob { 26 "" l "" @@ -411,7 +528,7 @@ Gizmo { addUserKnob {41 afterFrameRender l "after each frame" T Write1.afterFrameRender} addUserKnob {1 tk_after_render l "after render"} addUserKnob {41 renderProgress l "render progress" T Write1.renderProgress} - + knobChanged "import nuke\nif hasattr(nuke, \"_shotgun_write_node_handler\"):\n nuke._shotgun_write_node_handler.on_knob_changed_gizmo_callback()" onCreate "import nuke\nif hasattr(nuke, \"_shotgun_write_node_handler\"):\n nuke._shotgun_write_node_handler.on_node_created_gizmo_callback()" } @@ -421,6 +538,13 @@ Gizmo { xpos 195 ypos -74 } + AddTimeCode { + startcode 01:00:00:01 + metafps False + fps 23.98 + useFrame True + frame 1 + } Write { file "\[python __import__('nuke')._shotgun_write_node_handler.on_compute_path_gizmo_callback() if hasattr(__import__('nuke'), '_shotgun_write_node_handler') else nuke.thisParent().knob('cached_path').value()]" proxy "\[python __import__('nuke')._shotgun_write_node_handler.on_compute_proxy_path_gizmo_callback() if hasattr(__import__('nuke'), '_shotgun_write_node_handler') else nuke.thisParent().knob('tk_cached_proxy_path').value()]" diff --git a/info.yml b/info.yml index c4802f5..ea1d81a 100644 --- a/info.yml +++ b/info.yml @@ -64,6 +64,33 @@ configuration: fields: context, version, SEQ, [channel], [output], [name], [width], [height], [eye], [TankType], * allows_empty: True default_value: null + precomp_render_template: + type: template + fields: context, version, SEQ, [channel], [output], [name], [width], [height], [eye], * + allows_empty: True + default_value: null + element_render_template: + type: template + fields: context, version, SEQ, [channel], [output], [name], [width], [height], [eye], * + allows_empty: True + default_value: null + denoise_render_template: + type: template + fields: context, version, SEQ, [channel], [output], [name], [width], [height], [eye], * + allows_empty: True + cleanup_render_template: + type: template + fields: context, version, SEQ, [channel], [output], [name], [width], [height], [eye], * + allows_empty: True + final_render_template: + type: template + fields: context, version, SEQ, [channel], [output], [name], [width], [height], [eye], * + allows_empty: True + test_render_template: + type: template + fields: context, version, SEQ, [channel], [output], [name], [width], [height], [eye], * + allows_empty: True + default_value: null tile_color: type: list values: diff --git a/python/tk_nuke_writenode/handler.py b/python/tk_nuke_writenode/handler.py index a40313b..5e67aee 100644 --- a/python/tk_nuke_writenode/handler.py +++ b/python/tk_nuke_writenode/handler.py @@ -15,6 +15,10 @@ import datetime import base64 import re +import subprocess +import re +import traceback +import webbrowser import nuke import nukescripts @@ -22,19 +26,26 @@ import tank from tank import TankError from tank.platform import constants +from tank_vendor import shotgun_api3 as sgapi + +try: + import ConfigParser +except: + print( "Could not load ConfigParser module, sticky settings will not be loaded/saved" ) # Special exception raised when the work file cannot be resolved. class TkComputePathError(TankError): pass class TankWriteNodeHandler(object): - """ - Handles requests and processing from a tank write node. - """ + + # Handles requests and processing from a tank write node. + SG_WRITE_NODE_CLASS = "WriteTank" - SG_WRITE_DEFAULT_NAME = "ShotgunWrite" + SG_WRITE_DEFAULT_NAME = "SGWrite" WRITE_NODE_NAME = "Write1" + EMBED_TIME_CODE = "AddTimeCode1" OUTPUT_KNOB_NAME = "tank_channel" USE_NAME_AS_OUTPUT_KNOB_NAME = "tk_use_name_as_channel" @@ -47,8 +58,10 @@ def __init__(self, app): Construction """ self._app = app + self._curr_entity_type = self._app.context.entity['type'] self._script_template = self._app.get_template("template_script_work") - + self._project = self._app.context.project['name'] + # cache the profiles: self._promoted_knobs = {} self._profile_names = [] @@ -62,10 +75,13 @@ def __init__(self, app): self.__is_updating_proxy_path = False self.populate_profiles_from_settings() - + + # call and cache the version info + self._version_info = {} + # self.get_sg_info() + ################################################################################################ - # Properties - + # Properties @property def profile_names(self): """ @@ -130,11 +146,11 @@ def get_node_tank_type(self, node): if settings: return settings["tank_type"] - def get_render_template(self, node): + def get_render_template(self, node, write_type): """ helper function. Returns the associated render template obj for a node """ - return self.__get_render_template(node) + return self.__get_render_template(node, write_type) def get_publish_template(self, node): """ @@ -142,12 +158,12 @@ def get_publish_template(self, node): """ return self.__get_publish_template(node) - def get_proxy_render_template(self, node): + def get_proxy_render_template(self, node, write_type): """ helper function. Returns the associated render proxy template obj for a node. If this hasn't been defined then it falls back to the regular render template. """ - return self.__get_render_template(node, is_proxy=True, fallback_to_render=True) + return self.__get_render_template(node, write_type ,is_proxy=True, fallback_to_render=True) def get_proxy_publish_template(self, node): """ @@ -210,7 +226,20 @@ def reset_render_path(self, node): self.__update_render_path(node, force_reset=True, is_proxy=is_proxy) self.__update_render_path(node, force_reset=True, is_proxy=(not is_proxy)) - def create_new_node(self, profile_name): + def test_nodes_for_name (self, write_type): + # Cycles through script nodes to find clashing node names + existing_node_names = [n.name() for n in nuke.allNodes()] + # rename to our new default name: + postfix = 1 + while True: + new_name = "%s%d-%s" % (TankWriteNodeHandler.SG_WRITE_DEFAULT_NAME, postfix, write_type) + if new_name not in existing_node_names: + break + else: + postfix += 1 + return new_name + + def create_new_node(self, profile_name, write_type): """ Creates a new write node @@ -244,7 +273,7 @@ def create_new_node(self, profile_name): self._app.log_debug("Created Shotgun Write Node %s" % node.name()) # set the profile: - self.__set_profile(node, profile_name, reset_all_settings=True) + self.__set_profile(node, profile_name, write_type, reset_all_settings=True) return node @@ -276,9 +305,11 @@ def process_placeholder_nodes(self): n.dependencies()[0].setSelected(True) except: pass + # set the write type for creation of correct output + write_type = self.get_node_write_type_name(node) # create the node: - new_node = self.create_new_node(profile_name) + new_node = self.create_new_node(profile_name, write_type) # set the output: self.__set_output(new_node, output_name) @@ -408,8 +439,7 @@ def convert_sg_to_nuke_write_nodes(self): # copy across any knob values from the internal write node. for knob_name, knob in int_wn.knobs().iteritems(): # skip knobs we don't want to copy: - if knob_name in ["file_type", "file", "proxy", "beforeRender", "afterRender", - "name", "xpos", "ypos"]: + if knob_name in ["file_type", "file", "proxy", "beforeRender", "afterRender", "name", "xpos", "ypos"]: continue if knob_name in new_wn.knobs(): @@ -435,12 +465,17 @@ def convert_sg_to_nuke_write_nodes(self): knob = nuke.String_Knob("tk_output") knob.setValue(sg_wn[TankWriteNodeHandler.OUTPUT_KNOB_NAME].value()) new_wn.addKnob(knob) - + # use node name for output knob = nuke.Boolean_Knob(TankWriteNodeHandler.USE_NAME_AS_OUTPUT_KNOB_NAME) knob.setValue(sg_wn[TankWriteNodeHandler.USE_NAME_AS_OUTPUT_KNOB_NAME].value()) new_wn.addKnob(knob) - + + # channels + knob = nuke.String_Knob("tk_channels") + knob.setValue(sg_wn["channels"].value()) + new_wn.addKnob(knob) + # templates knob = nuke.String_Knob("tk_render_template") knob.setValue(sg_wn["render_template"].value()) @@ -457,6 +492,16 @@ def convert_sg_to_nuke_write_nodes(self): knob = nuke.String_Knob("tk_proxy_publish_template") knob.setValue(sg_wn["proxy_publish_template"].value()) new_wn.addKnob(knob) + + # Store tank_channel + knob = nuke.String_Knob("tk_tank_channel") + knob.setValue(sg_wn["tank_channel"].value()) + new_wn.addKnob(knob) + + #write type + knob = nuke.String_Knob("tk_write_type") + knob.setValue(sg_wn["write_type_cache"].value()) + new_wn.addKnob(knob) # delete original node: nuke.delete(sg_wn) @@ -489,16 +534,21 @@ def convert_nuke_to_sg_write_nodes(self): # look for additional toolkit knobs: profile_knob = wn.knob("tk_profile_name") + write_knob = wn.knob("tk_write_type") output_knob = wn.knob("tk_output") use_name_as_output_knob = wn.knob(TankWriteNodeHandler.USE_NAME_AS_OUTPUT_KNOB_NAME) + channels_knob = wn.knob("channels") render_template_knob = wn.knob("tk_render_template") publish_template_knob = wn.knob("tk_publish_template") proxy_render_template_knob = wn.knob("tk_proxy_render_template") proxy_publish_template_knob = wn.knob("tk_proxy_publish_template") + tk_tank_channel = wn.knob("tk_tank_channel") if (not profile_knob or not output_knob or not use_name_as_output_knob + or not write_knob + or not channels_knob or not render_template_knob or not publish_template_knob or not proxy_render_template_knob @@ -514,15 +564,16 @@ def convert_nuke_to_sg_write_nodes(self): # create new Shotgun Write node: new_sg_wn = nuke.createNode(TankWriteNodeHandler.SG_WRITE_NODE_CLASS) new_sg_wn.setSelected(False) - # copy across file & proxy knobs as well as all cached templates: new_sg_wn["cached_path"].setValue(wn["file"].value()) new_sg_wn["tk_cached_proxy_path"].setValue(wn["proxy"].value()) + new_sg_wn["channels"].setValue(channels_knob.value()) new_sg_wn["render_template"].setValue(render_template_knob.value()) new_sg_wn["publish_template"].setValue(publish_template_knob.value()) new_sg_wn["proxy_render_template"].setValue(proxy_render_template_knob.value()) new_sg_wn["proxy_publish_template"].setValue(proxy_publish_template_knob.value()) - + new_sg_wn["tank_channel_cache"].setValue(tk_tank_channel.value()) + # set the profile & output - this will cause the paths to be reset: # Note, we don't call the method __set_profile() as we don't want to # run all the normal logic that runs as part of switching the profile. @@ -541,9 +592,7 @@ def convert_nuke_to_sg_write_nodes(self): # copy across and knob values from the internal write node. for knob_name, knob in wn.knobs().iteritems(): # skip knobs we don't want to copy: - if knob_name in ["file_type", "file", "proxy", "beforeRender", "afterRender", - "name", "xpos", "ypos", "disable", "tile_color", "postage_stamp", - "label"]: + if knob_name in ["file_type", "file", "proxy", "beforeRender", "afterRender", "name", "xpos", "ypos", "disable", "tile_color", "postage_stamp", "label"]: continue if knob_name in int_wn.knobs(): @@ -556,7 +605,15 @@ def convert_nuke_to_sg_write_nodes(self): # explicitly copy some settings to the new Shotgun Write Node instead: for knob_name in ["disable", "tile_color", "postage_stamp"]: new_sg_wn[knob_name].setValue(wn[knob_name].value()) + + # set SG write type + write_name = write_knob.value() + new_sg_wn["write_type"].setValue(write_name) + #Set tank channel + tank_channel_text = tk_tank_channel.value() + new_sg_wn["tank_channel"].setValue(tank_channel_text) + # delete original node: nuke.delete(wn) @@ -565,6 +622,75 @@ def convert_nuke_to_sg_write_nodes(self): new_sg_wn.setXpos(node_pos[0]) new_sg_wn.setYpos(node_pos[1]) + def get_sg_info(self): + """ + Retrieves secific version info from SG and caches + """ + self._version_info = {} + + ctx_info = self._app.context + eng = tank.platform.current_engine() + sg = eng.shotgun + + filters = [ + ['entity', 'is', {'type': 'Shot', 'id': ctx_info.entity['id']}], + ['sg_task.Task.step.Step.id', 'is', ctx_info.step['id']] + ] + fields = ['id', 'code', 'sg_asset_type', 'created_at' , 'sg_version_number', 'description'] + additional_filter_presets = [ + { + "preset_name": "LATEST", + "latest_by": "ENTITIES_CREATED_AT" + }] + + self._version_info = sg.find_one("Version",filters,fields,additional_filter_presets = additional_filter_presets,include_archived_projects=False) + sg.close() + + def get_node_write_type_name(self, node): + """ + Return the name of the profile the specified node is using + """ + return node.knob("write_type").value() + + def sync_frames_from_SG(self): + eng = tank.platform.current_engine() + try: + app = eng.apps["tk-multi-setframerange"] + app.run_app() + except: + self._app.log_debug("Failed to sync frames") + raise + + def test_folder_for_renders(self, path): + """ + Tests a given folder location - mainly the writenode's file path for containing files + + :returns: True/False + """ + + path_dir = os.path.dirname(path) + if os.path.isdir(path_dir): + # nuke.tprint("Path exists...") + file_output_ext = [] + path_items_ext = [] + path_file_output_ext = os.path.split(path) + path_file_output_ext = os.path.splitext(path_file_output_ext[-1])[-1] + file_output_ext.append(path_file_output_ext) + path_items = os.listdir(path_dir) + if path_items: + for file in path_items: + path_item_ext = os.path.splitext(file)[1] + if path_item_ext not in path_items_ext: + path_items_ext.append(path_item_ext) + + ext_match = list(set(path_items_ext).intersection(set(file_output_ext))) + + return ext_match + + else: + return False + + ################################################################################################ # Public methods called from gizmo - although these are public, they should @@ -690,7 +816,7 @@ def on_reset_render_path_gizmo_callback(self): self.reset_render_path(node) - def on_copy_path_to_clipboard_gizmo_callback(self): + def on_copy_path_to_clipboard_gizmo_callback(self, win_safe): """ Callback from the gizmo whenever the 'Copy path to clipboard' button is pressed. @@ -700,11 +826,14 @@ def on_copy_path_to_clipboard_gizmo_callback(self): # get the path depending if in full or proxy mode: is_proxy = node.proxy() render_path = self.__get_render_path(node, is_proxy) - + if win_safe: + system = sys.platform + if system == "win32": + render_path = os.path.normpath(render_path) # use Qt to copy the path to the clipboard: from sgtk.platform.qt import QtGui QtGui.QApplication.clipboard().setText(render_path) - + def on_before_render_gizmo_callback(self): """ Callback from nuke whenever a tank write node is about to be rendered. @@ -755,6 +884,7 @@ def on_before_render_gizmo_callback(self): self._app.log_error("The Write node's beforeRender setting failed " "to execute!") raise + def on_after_render_gizmo_callback(self): """ Callback from nuke whenever a tank write node has finished being rendered @@ -809,10 +939,10 @@ def __get_template(self, node, name): # the profile probably doesn't exist any more so # try to use the cached version template_name = node.knob(name).value() - + return self._app.get_template_by_name(template_name) - def __get_render_template(self, node, is_proxy=False, fallback_to_render=False): + def __get_render_template(self, node, write_type, is_proxy=False, fallback_to_render=False): """ Get a specific render template for the current profile @@ -822,10 +952,34 @@ def __get_render_template(self, node, is_proxy=False, fallback_to_render=False): """ if is_proxy: template = self.__get_template(node, "proxy_render_template") + if template or not fallback_to_render: + return template + if write_type == "Precomp": + template = self.__get_template(node, "precomp_render_template") + if template or not fallback_to_render: + return template + elif write_type == "Element": + template = self.__get_template(node, "element_render_template") + if template or not fallback_to_render: + return template + elif write_type == "Denoise": + template = self.__get_template(node, "denoise_render_template") + if template or not fallback_to_render: + return template + elif write_type == "Cleanup": + template = self.__get_template(node, "cleanup_render_template") + if template or not fallback_to_render: + return template + elif write_type == "Final": + template = self.__get_template(node, "final_render_template") + if template or not fallback_to_render: + return template + elif write_type == "Test": + template = self.__get_template(node, "test_render_template") if template or not fallback_to_render: return template - - return self.__get_template(node, "render_template") + else: + return self.__get_template(node, "render_template") def __get_publish_template(self, node, is_proxy=False): """ @@ -841,8 +995,9 @@ def __is_output_used(self, node): Determine if output key is used in either the render or the proxy render templates """ - render_template = self.__get_render_template(node, is_proxy=False) - proxy_render_template = self.__get_render_template(node, is_proxy=True) + write_type = self.get_node_write_type_name(node) + render_template = self.__get_render_template(node, write_type, is_proxy=False) + proxy_render_template = self.__get_render_template(node, write_type, is_proxy=True) for template in [render_template, proxy_render_template]: if not template: @@ -852,7 +1007,7 @@ def __is_output_used(self, node): return True return False - + def __update_knob_value(self, node, name, new_value): """ Update the value for the specified knob on the specified node @@ -870,13 +1025,13 @@ def __update_output_knobs(self, node): """ output_knob = node.knob(TankWriteNodeHandler.OUTPUT_KNOB_NAME) name_as_output_knob = node.knob(TankWriteNodeHandler.USE_NAME_AS_OUTPUT_KNOB_NAME) - + output_is_used = self.__is_output_used(node) name_as_output = name_as_output_knob.value() - - output_knob.setEnabled(output_is_used and not name_as_output) - output_knob.setVisible(output_is_used) - name_as_output_knob.setVisible(output_is_used) + + # output_knob.setEnabled(output_is_used and not name_as_output) + # output_knob.setVisible(output_is_used) + # name_as_output_knob.setVisible(output_is_used) def __update_path_preview(self, node, is_proxy): """ @@ -886,12 +1041,15 @@ def __update_path_preview(self, node, is_proxy): # this will be displayed on the node in the graph # useful to tell what type of node it is pn = node.knob("profile_name").value() - label = "Shotgun Write %s" % pn + + # set the write type for creation of correct output + write_type = self.get_node_write_type_name(node) + + label = "%s - %s" % (write_type, pn) self.__update_knob_value(node, "label", label) # get the render path: path = self.__get_render_path(node, is_proxy) - # calculate the parts: context_path = local_path = file_name = "" @@ -910,12 +1068,65 @@ def __update_path_preview(self, node, is_proxy): file_name = os.path.basename(norm_path) render_dir = os.path.dirname(norm_path) - # now get the context path - context_path = None - for x in self._app.context.entity_locations: - if render_dir.startswith(x): - context_path = x - + # retrieve the correct context based on the dynamic + # file structure we have to separate out test + # renders from primary ones + # get the current script path: + script_path = self.__get_current_script_path() + work_template = self._app.tank.template_from_path(script_path) + curr_fields = work_template.get_fields(script_path) + + if self._curr_entity_type == 'Shot': + fields ={ + 'Shot': curr_fields['Shot'], + 'Step': curr_fields['Step'], + 'name': '', + 'output': '', + 'version': curr_fields['version'] + } + if write_type == "Test": + context_info = self._app.tank.templates['shot_render_test_global'] + elif write_type == "Precomp": + context_info = self._app.tank.templates['shot_render_global'] + elif write_type == "Element": + context_info = self._app.tank.templates['shot_render_global'] + elif write_type == "Denoise": + context_info = self._app.tank.templates['shot_render_global'] + elif write_type == "Cleanup": + context_info = self._app.tank.templates['shot_render_global'] + elif write_type == "Final": + context_info = self._app.tank.templates['shot_render_global'] + else: + context_info = self._app.tank.templates['shot_render_global'] + + context_path = context_info.apply_fields(fields) + + elif self._curr_entity_type == 'Asset': + fields ={ + 'Asset': curr_fields['Asset'], + 'Step': curr_fields['Step'], + 'sg_asset_type': curr_fields['sg_asset_type'], + 'name': '', + 'output': '', + 'version': curr_fields['version'] + } + if write_type == "Test": + context_info = self._app.tank.templates['asset_render_test_global'] + elif write_type == "Precomp": + context_info = self._app.tank.templates['asset_render_global'] + elif write_type == "Element": + context_info = self._app.tank.templates['asset_render_global'] + elif write_type == "Denoise": + context_info = self._app.tank.templates['asset_render_global'] + elif write_type == "Cleanup": + context_info = self._app.tank.templates['asset_render_global'] + elif write_type == "Final": + context_info = self._app.tank.templates['asset_render_global'] + else: + context_info = self._app.tank.templates['asset_render_global'] + + context_path = context_info.apply_fields(fields) + if context_path: # found a context path! # chop off this bit from the normalized path @@ -951,8 +1162,13 @@ def set_path_knob(name, value): set_path_knob("path_context", context_path) set_path_knob("path_local", local_path) - set_path_knob("path_filename", file_name) + set_path_knob("path_filename", file_name) + # get the path depending if in full or proxy mode: + is_proxy = node.proxy() + render_path = self.__get_render_path(node, is_proxy) + + def __apply_cached_file_format_settings(self, node): """ @@ -978,8 +1194,7 @@ def __apply_cached_file_format_settings(self, node): # update the node: self.__populate_format_settings(node, file_type, file_settings) - - def __set_profile(self, node, profile_name, reset_all_settings=False): + def __set_profile(self, node, profile_name, write_type, reset_all_settings=False): """ Set the current profile for the specified node. @@ -998,6 +1213,7 @@ def __set_profile(self, node, profile_name, reset_all_settings=False): # get the profile details: profile = self._profiles.get(profile_name) + ctx_info = self._app.context if not profile: # this shouldn't really every happen! self._app.log_warning("Failed to find a write node profile called '%s' for node '%s'!" @@ -1006,21 +1222,42 @@ def __set_profile(self, node, profile_name, reset_all_settings=False): self.__apply_cached_file_format_settings(node) return - self._app.log_debug("Changing the profile for node '%s' to: %s" % (node.name(), profile_name)) + self._app.log_debug("Changing the profile for node '%s' to: %s" % (node.name(), profile_name)) - # keep track of the old profile name: - old_profile_name = node.knob("profile_name").value() - # pull settings from profile: render_template = self._app.get_template_by_name(profile["render_template"]) publish_template = self._app.get_template_by_name(profile["publish_template"]) proxy_render_template = self._app.get_template_by_name(profile["proxy_render_template"]) proxy_publish_template = self._app.get_template_by_name(profile["proxy_publish_template"]) + precomp_render_template = self._app.get_template_by_name(profile["precomp_render_template"]) + element_render_template = self._app.get_template_by_name(profile["element_render_template"]) + denoise_render_template = self._app.get_template_by_name(profile["denoise_render_template"]) + cleanup_render_template = self._app.get_template_by_name(profile["cleanup_render_template"]) + final_render_template = self._app.get_template_by_name(profile["final_render_template"]) + test_render_template = self._app.get_template_by_name(profile["test_render_template"]) + file_type = profile["file_type"] file_settings = profile["settings"] tile_color = profile["tile_color"] promote_write_knobs = profile.get("promote_write_knobs", []) + if file_type == "exr" and write_type == "Version" or write_type == "Test": + if ctx_info.step['name'] == "Roto": + nuke.tprint("Task context is " + ctx_info.step['name']+". Applying RLE compression to "+ write_type +" output.") + file_settings.update({'compression' : 'Zip (1 scanline)'}) + file_settings.update({'datatype' : '16 bit half'}) + else: + nuke.tprint("Task context is " + ctx_info.step['name']+". Removing compression from "+ write_type +" output.") + file_settings.update({'compression' : 'none'}) + file_settings.update({'datatype' : '16 bit half'}) + elif (file_type == "exr" and write_type == "Element" or write_type == "Precomp" + or write_type == "Cleanup" or write_type == "Denoise"): + # nuke.tprint("Adding compression to "+ write_type +" output.") + file_settings.update({'compression' : 'Zip (1 scanline)'}) + file_settings.update({'datatype' : '16 bit half'}) + elif (file_type == "dpx" and write_type == "Cleanup"): + file_settings.update({'datatype' : '16 bit'}) + # Make sure any invalid entries are removed from the profile list: list_profiles = node.knob("tk_profile_list").values() if list_profiles != self._profile_names: @@ -1028,6 +1265,7 @@ def __set_profile(self, node, profile_name, reset_all_settings=False): # update both the list and the cached value for profile name: self.__update_knob_value(node, "profile_name", profile_name) + self.__update_knob_value(node, "write_type_cache", write_type) self.__update_knob_value(node, "tk_profile_list", profile_name) # set the format @@ -1038,7 +1276,17 @@ def __set_profile(self, node, profile_name, reset_all_settings=False): reset_all_settings, promote_write_knobs, ) - + + # set the channel info based on the profile type + # if profile_name == "Dpx": + # self.__update_knob_value(node, "channels", "rgb") + # elif profile_name == "Exr 16 bit": + # self.__update_knob_value(node, "channels", "rgba") + # elif profile_name == "Jpeg": + # self.__update_knob_value(node, "channels", "rgb") + # else: + # nuke.tprint("No profile with the name:", profile_name) + # cache the type and settings on the root node so that # they get serialized with the script: self.__update_knob_value(node, "tk_file_type", file_type) @@ -1095,6 +1343,12 @@ def __set_profile(self, node, profile_name, reset_all_settings=False): proxy_render_template.name if proxy_render_template else "") self.__update_knob_value(node, "proxy_publish_template", proxy_publish_template.name if proxy_publish_template else "") + self.__update_knob_value(node, "precomp_render_template", precomp_render_template.name) + self.__update_knob_value(node, "element_render_template", element_render_template.name) + self.__update_knob_value(node, "denoise_render_template", denoise_render_template.name) + self.__update_knob_value(node, "cleanup_render_template", cleanup_render_template.name) + self.__update_knob_value(node, "final_render_template", final_render_template.name) + self.__update_knob_value(node, "test_render_template", test_render_template.name) # If a node's tile_color was defined in the profile then set it: if not tile_color or len(tile_color) != 3: @@ -1104,7 +1358,26 @@ def __set_profile(self, node, profile_name, reset_all_settings=False): "setting will be ignored!") % profile_name) # reset tile_color knob value back to default: - default_value = int(node["tile_color"].defaultValue()) + if write_type == "Precomp": + default_value = 4121611007 + + elif write_type == "Element": + default_value = 1095751564801 + + elif write_type == "Denoise": + default_value = 309868287 + + elif write_type == "Cleanup": + default_value = 4287911423 + + elif write_type == "Final": + default_value = 16711935 + + elif write_type == "Test": + default_value = 4278190081 + + else: + default_value = int(node["tile_color"].defaultValue()) self.__update_knob_value(node, "tile_color", default_value) else: # build packed RGB @@ -1115,11 +1388,90 @@ def __set_profile(self, node, profile_name, reset_all_settings=False): self.__update_knob_value(node, "tile_color", packed_rgb) + # set the channel info based on the profile type + profile_channel = "rgba" + if profile_name == "Dpx": + if self._project == "Jack Ryan S1": + node.knob("colorspace").setValue("Cineon") + elif (self._project == "GOT8" or + self._project == "SSVFX_PIPELINE"): + node.node("Write1").knob("datatype").setValue("10 bit") + profile_channel = "rgb" + elif profile_name == "Exr 16 bit": + profile_channel = "rgb" + if (write_type == "Precomp" or + write_type == "Element"): + profile_channel = "rgba" + if ctx_info.step['name'] == "Roto": + profile_channel = "rgba" + + elif profile_name == "Jpeg": + profile_channel = "rgb" + else: + nuke.tprint("No profile with that name") + + # Update embeded time code + + time_code = node.node(TankWriteNodeHandler.EMBED_TIME_CODE) + eng = tank.platform.current_engine() + app = eng.apps["tk-multi-setframerange"] + frame_range = app.get_frame_range_from_shotgun() + if self._curr_entity_type == 'Shot': + proj_fps = 23.98 + timecode = "01:00:00:01" + if not frame_range[0]: + print "No frame range values found on SG" + shot_frame_range_start = 1 + use_start_frame = False + pass + else: + shot_frame_range_start = frame_range[0] + use_start_frame = True + + if self._project == "": + print "Leaving tc blank for non-standard values" + # node.node("AddTimeCode1").knob("metafps").setValue(False) + elif self._project == "The Favourite": + proj_fps = 24 + timecode = "01:00:00:01" + print "Project is The Favourite. Setting TC FPS to 24" + elif self._project == "The Little Stranger": + proj_fps = 24 + timecode = "01:00:00:01" + print "Project is The Little Stranger. Setting TC FPS to 24" + elif self._project == "SSVFX_PIPELINE": + proj_fps = 24 + timecode = "01:00:00:01" + print "Project is SSVFX_PIPELINE. Setting TC FPS to 24" + elif self._project == "DeAging": + proj_fps = 25 + timecode = "01:00:00:01" + print "Project is DeAging. Setting TC FPS to 25" + elif self._project == "GOT8": + proj_fps = 23.976 + timecode = "01:00:00:01" + print "Project is GOT8. Setting TC FPS to 23.976" + else: + proj_fps = 23.98 + timecode = "01:00:00:01" + + time_code.knobs()["startcode"].setValue(timecode) + time_code.knobs()["fps"].setValue(proj_fps) + time_code.knobs()["useFrame"].setValue(use_start_frame) + time_code.knobs()["frame"].setValue(shot_frame_range_start) + # nuke.tprint("Timecode values: ", timecode, proj_fps, "Use start frame", use_start_frame, "First frame from SG" ,shot_frame_range_start) + + self.__update_knob_value(node, "channels", profile_channel) + + # Version info visibility + self.__update_version_preview(node, write_type) + + # Reset the render path but only if the named profile has changed - this will only # be the case if the user has changed the profile through the UI so this will avoid # the node automatically updating without the user's knowledge. - if profile_name != old_profile_name: - self.reset_render_path(node) + + self.reset_render_path(node) def __populate_initial_output_name(self, template, node): """ @@ -1143,40 +1495,44 @@ def __populate_initial_output_name(self, template, node): if output_default is None: output_default = key.default if output_is_optional: - output_is_optional = template.is_optional(key_name) + output_is_optional = template.is_optional(key_name) + if not have_output_key: # Nothing to do! return - - if output_default is None: - # no default name - use hard coded built in - output_default = "output" - - # get the output names for all other nodes that are using the same profile - used_output_names = set() - node_profile = self.get_node_profile_name(node) - for n in self.get_nodes(): - if n != node and self.get_node_profile_name(n) == node_profile: - used_output_names.add(n.knob(TankWriteNodeHandler.OUTPUT_KNOB_NAME).value()) - - # handle if output is optional: - if output_is_optional and "" not in used_output_names: - # default should be an empty string: - output_default = "" - - # now ensure output name is unique: - postfix = 1 - output_name = output_default - while output_name in used_output_names: - output_name = "%s%d" % (output_default, postfix) - postfix += 1 - - # finally, set the output name on the knob: - node.knob(TankWriteNodeHandler.OUTPUT_KNOB_NAME).setValue(output_name) - - def __populate_format_settings( - self, node, file_type, file_settings, reset_all_settings=False, promoted_write_knobs=None - ): + + # if output_default is None: + # # no default name - use hard coded built in + # step_name = self._app.context.step['name']#.lower() + # if step_name: + # output_default = step_name + # else: + # output_default = "output" + + # # get the output names for all other nodes that are using the same profile + # used_output_names = set() + # node_profile = self.get_node_profile_name(node) + # for n in self.get_nodes(): + # if n != node and self.get_node_profile_name(n) == node_profile: + # used_output_names.add(n.knob(TankWriteNodeHandler.OUTPUT_KNOB_NAME).value()) + + # # handle if output is optional: + # if output_is_optional and "" not in used_output_names: + # # default should be an empty string: + # output_default = "" + + # # now ensure output name is unique: + # postfix = 1 + # output_name = output_default + # while output_name in used_output_names: + # output_name = "%s%d" % (output_default, postfix) + # postfix += 1 + + + # # finally, set the output name on the knob: + # node.knob(TankWriteNodeHandler.OUTPUT_KNOB_NAME).setValue(output_name) + + def __populate_format_settings(self, node, file_type, file_settings, reset_all_settings=False, promoted_write_knobs=None): """ Controls the file format of the write node @@ -1343,6 +1699,7 @@ def __update_render_path(self, node, force_reset=False, is_proxy=False): reset_path_button_visible = False path_warning = "" + files_warning = "" render_path = None cache_entry = None try: @@ -1368,6 +1725,24 @@ def __update_render_path(self, node, force_reset=False, is_proxy=False): else: # compute the render path: render_path = self.__compute_render_path_from(node, render_template, width, height, output_name) + if self.test_folder_for_renders(render_path): + ext_match = self.test_folder_for_renders(render_path) + ext_match_string = "" + if ext_match >1: + for i in ext_match: + ext_match_string += " " +i+ " " + else: + ext_match_string = ext_match[0] + files_warning += "" + ext_match_string + " files already in this location." + files_warning += "
   
" + files_warning += "
Test SG write type available for test renders.
" + self.__update_knob_value(node, "files_warning", + "Careful Now!
%s
" + % "
".join(self.__wrap_text(files_warning, 60))) + node.knob("files_warning").setVisible(True) + else: + self.__update_knob_value(node, "files_warning", "") + node.knob("files_warning").setVisible(False) except TkComputePathError, e: # update cache: @@ -1505,7 +1880,9 @@ def __get_files_on_disk(self, node, is_proxy=False): Returns the files on disk associated with this node """ file_name = self.__get_render_path(node, is_proxy) - template = self.__get_render_template(node, is_proxy, fallback_to_render=True) + # set the write type for creation of correct output + write_type = self.get_node_write_type_name(node) + template = self.__get_render_template(node, write_type, is_proxy, fallback_to_render=True) if not template.validate(file_name): raise Exception("Could not resolve the files on disk for node %s." @@ -1569,7 +1946,6 @@ def __calculate_proxy_dimensions(self, node): #print ("sx:", scale_x, "sy:", scale_y, "tx:", offset_x, "ty:", offset_y, # "w:", scaled_format.width(), "h:", scaled_format.height()) return (scaled_format.width(), scaled_format.height()) - def __gather_render_settings(self, node, is_proxy=False): """ @@ -1580,7 +1956,10 @@ def __gather_render_settings(self, node, is_proxy=False): :param is_proxy: If True then compute the proxy path, otherwise compute the standard render path :returns: Tuple containing (render template, width, height, output name) """ - render_template = self.__get_render_template(node, is_proxy) + # set the write type for creation of correct output + write_type = self.get_node_write_type_name(node) + + render_template = self.__get_render_template(node, write_type, is_proxy) width = height = 0 output_name = "" @@ -1607,7 +1986,6 @@ def __gather_render_settings(self, node, is_proxy=False): return (render_template, width, height, output_name) - def __compute_render_path(self, node, is_proxy=False): """ Computes the render path for a node. @@ -1673,7 +2051,7 @@ def __compute_render_path_from(self, node, render_template, width, height, outpu if key_name in fields: del(fields[key_name]) - if key_name in render_template.keys: + if key_name in render_template.keys: if not output_name: if not render_template.is_optional(key_name): raise TkComputePathError("A valid output name is required by this profile for the '%s' field!" @@ -1682,10 +2060,9 @@ def __compute_render_path_from(self, node, render_template, width, height, outpu if not render_template.keys[key_name].validate(output_name): raise TkComputePathError("The output name '%s' contains illegal characters!" % output_name) fields[key_name] = output_name - + # update with additional fields from the context: fields.update(self._app.context.as_template_fields(render_template)) - # generate the render path: path = "" try: @@ -1707,8 +2084,11 @@ def __is_render_path_locked(self, node, render_path, cached_path, is_proxy=False The path is locked if a new path generated with the previous template fields would be different to the cached path ignoring the width & height fields. """ + # set the write type for creation of correct output + write_type = self.get_node_write_type_name(node) + # get the render template: - render_template = self.__get_render_template(node, is_proxy, fallback_to_render=True) + render_template = self.__get_render_template(node, write_type, is_proxy, fallback_to_render=True) if not render_template: return True @@ -1763,7 +2143,7 @@ def __setup_new_node(self, node): return self._app.log_debug("Setting up new node...") - + # populate the profiles list as this isn't stored with the file and is # dynamic based on the user's configuration profile_names = list(self._profile_names) @@ -1786,10 +2166,14 @@ def __setup_new_node(self, node): # sure we reset all settings reset_all_profile_settings = True + # set the write type for creation of correct output + write_type = self.get_node_write_type_name(node) + # ensure that the correct entry is selected from the list: self.__update_knob_value(node, "tk_profile_list", current_profile_name) + # and make sure the node is up-to-date with the profile: - self.__set_profile(node, current_profile_name, reset_all_settings=reset_all_profile_settings) + self.__set_profile(node, current_profile_name, write_type, reset_all_settings=reset_all_profile_settings) # ensure that the disable value properly propogates to the internal write node: write_node = node.node(TankWriteNodeHandler.WRITE_NODE_NAME) @@ -1802,11 +2186,20 @@ def __setup_new_node(self, node): # pasted the node will get a new name to avoid a collision # and we need to make sure we update the output name to # match that new name. - if node.knob(TankWriteNodeHandler.USE_NAME_AS_OUTPUT_KNOB_NAME).value(): - # force output name to be the node name: - new_output_name = node.knob("name").value() - self.__set_output(node, new_output_name) - + # if node.knob(TankWriteNodeHandler.USE_NAME_AS_OUTPUT_KNOB_NAME).value(): + # # force output name to be the node name: + # new_output_name = node.knob("name").value() + # self.__set_output(node, new_output_name) + + if self._curr_entity_type == 'Shot': + if write_type == "Version": + node.knob(TankWriteNodeHandler.OUTPUT_KNOB_NAME).setEnabled(False) + if self._curr_entity_type == 'Asset': + if write_type == "Version": + node.knob(TankWriteNodeHandler.OUTPUT_KNOB_NAME).setEnabled(True) + # set the version info if it exists + self.__update_version_preview(node, write_type) + # now that the node is constructed, we can process knob changes # correctly. node.knob("tk_is_fully_constructed").setValue(True) @@ -1833,7 +2226,8 @@ def __on_knob_changed(self): """ node = nuke.thisNode() knob = nuke.thisKnob() - grp = nuke.thisGroup() + grp = nuke.thisGroup() + write_type = self.get_node_write_type_name(node) if not self.__is_node_fully_constructed(node): # knobChanged will be called during script load for all knobs with non-default @@ -1846,30 +2240,261 @@ def __on_knob_changed(self): if knob.name() == "tk_profile_list": # change the profile for the specified node: new_profile_name = knob.value() - self.__set_profile(node, new_profile_name, reset_all_settings=True) - + # set the write type for creation of correct output + self.__set_profile(node, new_profile_name, write_type, reset_all_settings=True) elif knob.name() == TankWriteNodeHandler.OUTPUT_KNOB_NAME: # internal cached output has been changed! - new_output_name = knob.value() + new_output_name = knob.value() if node.knob(TankWriteNodeHandler.USE_NAME_AS_OUTPUT_KNOB_NAME).value(): # force output name to be the node name: new_output_name = node.knob("name").value() - self.__set_output(node, new_output_name) - + self.__set_output(node, new_output_name) elif knob.name() == "name": # node name has changed: if node.knob(TankWriteNodeHandler.USE_NAME_AS_OUTPUT_KNOB_NAME).value(): # set the output to the node name: - self.__set_output(node, knob.value()) - + self.__set_output(node, knob.value()) elif knob.name() == TankWriteNodeHandler.USE_NAME_AS_OUTPUT_KNOB_NAME: # checkbox controlling if the name should be used as the output has been toggled name_as_output = knob.value() - node.knob(TankWriteNodeHandler.OUTPUT_KNOB_NAME).setEnabled(not name_as_output) - if name_as_output: - # update output to reflect the node name: - self.__set_output(node, node.knob("name").value()) - + if node.knob(TankWriteNodeHandler.USE_NAME_AS_OUTPUT_KNOB_NAME).value(): + node.knob(TankWriteNodeHandler.OUTPUT_KNOB_NAME).setEnabled(not name_as_output) + if name_as_output: + # update output to reflect the node name: + self.__set_output(node, node.knob("name").value()) + else: + step_name = self._app.context.step['name'] + if step_name: + new_output_name = step_name + else: + new_output_name = "output" + self.__set_output(node, new_output_name) + elif knob.name() == "write_type": + # set the write type for creation of correct output + if self._curr_entity_type == 'Shot': + if self._project == "Lost In Space S1": + write_type_profile = "Exr 16 bit" + if write_type== "Version": + profile_channels = "rgb" + self.__update_knob_value(node, TankWriteNodeHandler.OUTPUT_KNOB_NAME, "") + node.knob(TankWriteNodeHandler.OUTPUT_KNOB_NAME).setEnabled(False) + elif write_type == "Precomp": + self.__update_knob_value(node, TankWriteNodeHandler.OUTPUT_KNOB_NAME, "") + node.knob(TankWriteNodeHandler.OUTPUT_KNOB_NAME).setEnabled(True) + elif write_type == "Element": + self.__update_knob_value(node, TankWriteNodeHandler.OUTPUT_KNOB_NAME, "") + node.knob(TankWriteNodeHandler.OUTPUT_KNOB_NAME).setEnabled(True) + elif write_type == "Denoise": + self.__update_knob_value(node, TankWriteNodeHandler.OUTPUT_KNOB_NAME, "") + node.knob(TankWriteNodeHandler.OUTPUT_KNOB_NAME).setEnabled(True) + elif write_type == "Cleanup": + self.__update_knob_value(node, TankWriteNodeHandler.OUTPUT_KNOB_NAME, "") + node.knob(TankWriteNodeHandler.OUTPUT_KNOB_NAME).setEnabled(True) + elif write_type == "Final": + self.__update_knob_value(node, TankWriteNodeHandler.OUTPUT_KNOB_NAME, "") + node.knob(TankWriteNodeHandler.OUTPUT_KNOB_NAME).setEnabled(False) + elif write_type == "Test": + self.__update_knob_value(node, TankWriteNodeHandler.OUTPUT_KNOB_NAME, "") + node.knob(TankWriteNodeHandler.OUTPUT_KNOB_NAME).setEnabled(True) + # Pop warning that the renders saved to the Test location + user_name = self._app.context.user['name'].split() + nuke.message( + "Hi %s" % str(user_name[0]) + "\n" + "\n" + "Please be aware that this is a temporary location.\n" + "Renders saved here will be removed at the end of the week.\n") + # Updates the predefined profile based on the write type + self.__update_knob_value(node, "tk_profile_list", write_type_profile) + # reset profile + self.__set_profile(node, write_type_profile, write_type, reset_all_settings=True) + elif self._project == "Jack Ryan S1": + write_type = self.get_node_write_type_name(node) + write_type_profile = "Dpx" + if write_type== "Version": + self.__update_knob_value(node, TankWriteNodeHandler.OUTPUT_KNOB_NAME, "") + node.node(TankWriteNodeHandler.WRITE_NODE_NAME)["fill"].setValue(False) + node.knob(TankWriteNodeHandler.OUTPUT_KNOB_NAME).setEnabled(False) + node.knob("_promoted_1").setValue(False) + elif write_type == "Precomp": + self.__update_knob_value(node, TankWriteNodeHandler.OUTPUT_KNOB_NAME, "") + node.node(TankWriteNodeHandler.WRITE_NODE_NAME)["fill"].setValue(False) + node.knob(TankWriteNodeHandler.OUTPUT_KNOB_NAME).setEnabled(True) + node.knob("_promoted_1").setValue(False) + elif write_type == "Element": + self.__update_knob_value(node, TankWriteNodeHandler.OUTPUT_KNOB_NAME, "") + node.node(TankWriteNodeHandler.WRITE_NODE_NAME)["fill"].setValue(False) + node.knob(TankWriteNodeHandler.OUTPUT_KNOB_NAME).setEnabled(True) + node.knob("_promoted_1").setValue(False) + elif write_type == "Denoise": + self.__update_knob_value(node, TankWriteNodeHandler.OUTPUT_KNOB_NAME, "") + node.node(TankWriteNodeHandler.WRITE_NODE_NAME)["fill"].setValue(False) + node.knob(TankWriteNodeHandler.OUTPUT_KNOB_NAME).setEnabled(False) + node.knob("_promoted_1").setValue(False) + elif write_type == "Cleanup": + self.__update_knob_value(node, TankWriteNodeHandler.OUTPUT_KNOB_NAME, "") + node.node(TankWriteNodeHandler.WRITE_NODE_NAME)["fill"].setValue(False) + node.knob(TankWriteNodeHandler.OUTPUT_KNOB_NAME).setEnabled(False) + node.knob("_promoted_1").setValue(False) + elif write_type == "Final": + self.__update_knob_value(node, TankWriteNodeHandler.OUTPUT_KNOB_NAME, "") + node.node(TankWriteNodeHandler.WRITE_NODE_NAME)["fill"].setValue(True) + print "*****Set the Final-DPX fill to true" + node.knob(TankWriteNodeHandler.OUTPUT_KNOB_NAME).setEnabled(False) + elif write_type == "Test": + self.__update_knob_value(node, TankWriteNodeHandler.OUTPUT_KNOB_NAME, "") + # write_type_profile = "Exr 16 bit" + node.knob(TankWriteNodeHandler.OUTPUT_KNOB_NAME).setEnabled(True) + # Pop warning that the renders saved to the Test location + user_name = self._app.context.user['name'].split() + nuke.message( + "Hi %s" % str(user_name[0]) + "\n" + "\n" + "Please be aware that this is a temporary location.\n" + "Renders saved here will be removed at the end of the week.\n") + # Updates the predefined profile based on the write type + self.__update_knob_value(node, "tk_profile_list", write_type_profile) + # reset profile + self.__set_profile(node, write_type_profile, write_type, reset_all_settings=True) + elif self._project == "The Favourite" or self._project == "SSVFX_PIPELINE" : + write_type = self.get_node_write_type_name(node) + write_type_profile = "Dpx" + if write_type== "Version": + self.__update_knob_value(node, TankWriteNodeHandler.OUTPUT_KNOB_NAME, "") + node.knob(TankWriteNodeHandler.OUTPUT_KNOB_NAME).setEnabled(False) + node.knob("_promoted_1").setValue(False) + elif write_type == "Precomp": + write_type_profile = "Exr 16 bit" + self.__update_knob_value(node, TankWriteNodeHandler.OUTPUT_KNOB_NAME, "") + node.knob(TankWriteNodeHandler.OUTPUT_KNOB_NAME).setEnabled(True) + node.knob("_promoted_1").setValue(False) + elif write_type == "Element": + write_type_profile = "Exr 16 bit" + self.__update_knob_value(node, TankWriteNodeHandler.OUTPUT_KNOB_NAME, "") + node.knob(TankWriteNodeHandler.OUTPUT_KNOB_NAME).setEnabled(True) + node.knob("_promoted_1").setValue(False) + elif write_type == "Denoise": + write_type_profile = "Exr 16 bit" + self.__update_knob_value(node, TankWriteNodeHandler.OUTPUT_KNOB_NAME, "") + node.knob(TankWriteNodeHandler.OUTPUT_KNOB_NAME).setEnabled(True) + node.knob("_promoted_1").setValue(False) + elif write_type == "Cleanup": + write_type_profile = "Dpx" + self.__update_knob_value(node, TankWriteNodeHandler.OUTPUT_KNOB_NAME, "") + node.knob(TankWriteNodeHandler.OUTPUT_KNOB_NAME).setEnabled(True) + node.knob("_promoted_1").setValue(False) + elif write_type == "Final": + self.__update_knob_value(node, TankWriteNodeHandler.OUTPUT_KNOB_NAME, "") + node.knob(TankWriteNodeHandler.OUTPUT_KNOB_NAME).setEnabled(False) + elif write_type == "Test": + self.__update_knob_value(node, TankWriteNodeHandler.OUTPUT_KNOB_NAME, "") + # write_type_profile = "Exr 16 bit" + node.knob(TankWriteNodeHandler.OUTPUT_KNOB_NAME).setEnabled(True) + # Pop warning that the renders saved to the Test location + user_name = self._app.context.user['name'].split() + nuke.message( + "Hi %s" % str(user_name[0]) + "\n" + "\n" + "Please be aware that this is a temporary location.\n" + "Renders saved here will be removed at the end of the week.\n") + # Updates the predefined profile based on the write type + self.__update_knob_value(node, "tk_profile_list", write_type_profile) + # reset profile + self.__set_profile(node, write_type_profile, write_type, reset_all_settings=True) + else: + write_type = self.get_node_write_type_name(node) + # write_type_profile = "Dpx" + write_type_profile = "Exr 16 bit" + if write_type== "Version": + self.__update_knob_value(node, TankWriteNodeHandler.OUTPUT_KNOB_NAME, "") + node.knob(TankWriteNodeHandler.OUTPUT_KNOB_NAME).setEnabled(False) + elif write_type == "Precomp": + self.__update_knob_value(node, TankWriteNodeHandler.OUTPUT_KNOB_NAME, "") + # write_type_profile = "Exr 16 bit" + node.knob(TankWriteNodeHandler.OUTPUT_KNOB_NAME).setEnabled(True) + elif write_type == "Element": + self.__update_knob_value(node, TankWriteNodeHandler.OUTPUT_KNOB_NAME, "") + # write_type_profile = "Exr 16 bit" + node.knob(TankWriteNodeHandler.OUTPUT_KNOB_NAME).setEnabled(True) + elif write_type == "Denoise": + self.__update_knob_value(node, TankWriteNodeHandler.OUTPUT_KNOB_NAME, "") + # write_type_profile = "Exr 16 bit" + node.knob(TankWriteNodeHandler.OUTPUT_KNOB_NAME).setEnabled(True) + elif write_type == "Cleanup": + self.__update_knob_value(node, TankWriteNodeHandler.OUTPUT_KNOB_NAME, "") + # write_type_profile = "Exr 16 bit" + node.knob(TankWriteNodeHandler.OUTPUT_KNOB_NAME).setEnabled(True) + elif write_type == "Final": + self.__update_knob_value(node, TankWriteNodeHandler.OUTPUT_KNOB_NAME, "") + # write_type_profile = "Exr 16 bit" + node.knob(TankWriteNodeHandler.OUTPUT_KNOB_NAME).setEnabled(False) + elif write_type == "Test": + self.__update_knob_value(node, TankWriteNodeHandler.OUTPUT_KNOB_NAME, "") + # write_type_profile = "Exr 16 bit" + node.knob(TankWriteNodeHandler.OUTPUT_KNOB_NAME).setEnabled(True) + # Pop warning that the renders saved to the Test location + user_name = self._app.context.user['name'].split() + nuke.message( + "Hi %s" % str(user_name[0]) + "\n" + "\n" + "Please be aware that this is a temporary location.\n" + "Renders saved here will be removed at the end of the week.\n") + # Updates the predefined profile based on the write type + self.__update_knob_value(node, "tk_profile_list", write_type_profile) + # reset profile + self.__set_profile(node, write_type_profile, write_type, reset_all_settings=True) + elif self._curr_entity_type == 'Asset': + write_type = self.get_node_write_type_name(node) + write_type_profile = "Exr 16 bit" + if write_type== "Version": + self.__update_knob_value(node, TankWriteNodeHandler.OUTPUT_KNOB_NAME, "") + node.knob(TankWriteNodeHandler.OUTPUT_KNOB_NAME).setEnabled(True) + write_type_profile = "Exr 16 bit" + elif write_type == "Precomp": + self.__update_knob_value(node, TankWriteNodeHandler.OUTPUT_KNOB_NAME, "") + node.knob(TankWriteNodeHandler.OUTPUT_KNOB_NAME).setEnabled(True) + write_type_profile = "Exr 16 bit" + elif write_type == "Element": + self.__update_knob_value(node, TankWriteNodeHandler.OUTPUT_KNOB_NAME, "") + node.knob(TankWriteNodeHandler.OUTPUT_KNOB_NAME).setEnabled(True) + write_type_profile = "Exr 16 bit" + elif write_type == "Denoise": + self.__update_knob_value(node, TankWriteNodeHandler.OUTPUT_KNOB_NAME, "") + node.knob(TankWriteNodeHandler.OUTPUT_KNOB_NAME).setEnabled(False) + write_type_profile = "Exr 16 bit" + elif write_type == "Cleanup": + self.__update_knob_value(node, TankWriteNodeHandler.OUTPUT_KNOB_NAME, "") + node.knob(TankWriteNodeHandler.OUTPUT_KNOB_NAME).setEnabled(False) + write_type_profile = "Exr 16 bit" + elif write_type == "Final": + self.__update_knob_value(node, TankWriteNodeHandler.OUTPUT_KNOB_NAME, "") + node.knob(TankWriteNodeHandler.OUTPUT_KNOB_NAME).setEnabled(False) + write_type_profile = "Exr 16 bit" + elif write_type == "Test": + self.__update_knob_value(node, TankWriteNodeHandler.OUTPUT_KNOB_NAME, "") + node.knob(TankWriteNodeHandler.OUTPUT_KNOB_NAME).setEnabled(True) + write_type_profile = "Exr 16 bit" + # Pop warning that the renders saved to the Test location + user_name = self._app.context.user['name'].split() + nuke.message( + "Hi %s" % str(user_name[0]) + "\n" + "\n" + "Please be aware that this is a temporary location.\n" + "Renders saved here will be removed at the end of the week.\n") + # Updates the predefined profile based on the write type + self.__update_knob_value(node, "tk_profile_list", write_type_profile) + # reset profile + self.__set_profile(node, write_type_profile, write_type, reset_all_settings=True) + elif knob.name() == "sync_from_sg": + # Sync from SG + self.sync_frames_from_SG() + elif knob.name() == "refresh_version_info": + # set the write type for creation of correct output + write_type = self.get_node_write_type_name(node) + # self.get_sg_info() + self.__update_version_preview(node, write_type) + elif knob.name() == "write_type_info": + write_type_url = "http://10.80.10.239/mediawiki-1.25.2/index.php?title=VFX_Wiki#SG_Write_Nodes" + webbrowser.open_new_tab(write_type_url) else: # Propogate changes to certain knobs from the gizmo/group to the # encapsulated Write node. @@ -1925,7 +2550,6 @@ def __get_current_script_path(self): return script_path - def __on_script_save(self): """ Called when the script is saved. @@ -1975,7 +2599,7 @@ def __on_script_save(self): "tk_write_node_settings", unicode(base64.b64encode(knob_changes)), ) - + def __on_user_create(self): """ Called when the user creates a Shotgun Write node. Not called when loading @@ -1991,16 +2615,71 @@ def __on_user_create(self): # setup the new node: self.__setup_new_node(node) - + + # set the write type for creation of correct output + write_type = self.get_node_write_type_name(node) + # populate the initial output name based on the render template: - render_template = self.get_render_template(node) + render_template = self.get_render_template(node, write_type) self.__populate_initial_output_name(render_template, node) + def __update_version_preview(self, node, write_type): + """ + #Updates the version info fields on the tank write node. + """ + # Version specific UI names + version_ui = ["latest_version", "version_date", "version_description", "Description", "refresh_version_info", "version_info_breaker"] + if write_type != "Version": + self.__hide_UI(node, version_ui, False) + if not self._version_info: + return + else: + self.__hide_UI(node, version_ui, True) + if self._version_info: + self.__update_knob_value(node, "latest_version", str(self._version_info['code'])) + self.__update_knob_value(node, "version_date", str(self._version_info['created_at'])) + self.__update_knob_value(node, "version_description", str(self._version_info['description'])) + if not self._version_info: + return + + def __hide_UI(self, node, ui_name_array, visibility): + + if not ui_name_array: + return + else: + for i in ui_name_array: + k = node.knob(i) + if k: + k.setVisible(visibility) + + def __disable_UI(self, node, ui_name_array, enabled): + + if not ui_name_array: + return + else: + for i in ui_name_array: + k = node.knob(i) + k.setEnabled(enabled) + def __update_knob_values(self, node, name, values_list): + if not values_list: + return None + else: + k = node.knob(name) + if k.values != values_list: + k.setValues(values_list) + def __get_list_index(self, the_list, item_name): + if the_list: + if item_name in the_list: + return the_list.index(item_name) + else: + nuke.tprint ("Couldn't find the given item_name in list") + return 0 + def __construct_version_name(self, node): - - \ No newline at end of file + if self._version_info: + print self._version_info