def set_knobs(cls, node): """ Set values from context on a given knob. Args: node (nuke.Node): current node we are baking in the queue. """ if node.knob("set_knobs") and node.knob("set_knobs").value(): # get set knob list, split, and strip white space knobs = node.knob("knobs_to_set").value() raw_knobs_to_set = knobs.split(",") for raw_knob in raw_knobs_to_set: try: knob = raw_knob.strip() # clean up any spaces raw_name, raw_value = knob.split("=") name = raw_name.strip() value = raw_value.strip() getLog().info("Ada: Knobs to set: {0} : {1}".format( name, value)) cls.bake_knob(node, name, value=value) except ValueError: msg = "Unable to set: {0} missing value after '='" getLog().warning(msg.format(node.name()))
def execute_code(node): """ Take code from multi-eval stringe knob, put it in a button then execute the button. This allows the user to to create complex python script on nodes and reference nuke.thisNode(). Args: node (nuke.Node): Node that hasa execute_code knob. Returns: bool: True if code execution is fine, otherwise false. """ if node.knob("execute_code") and node.knob("execute_code").value(): success = False # execute code inside a temp knob attached to the node py_script_knob = nuke.PyScript_Knob("__run", "Run") node.addKnob(py_script_knob) code = node.knob("code_to_execute").toScript() try: py_script_knob.setValue(code) py_script_knob.execute() success = True except Exception as e: getLog().warning( "'{0}' failed to execute code:\n{1}\nreason:\n\t{2}\n". format(node.fullName(), code, e)) finally: # if nuke.delete(nuke.thisNode) is called in code to execute # the node.removeKnob will throw a ValueError if not node: return success node.removeKnob(py_script_knob) return success
def fuel(template): """ Fuel an empty script with a template and set the basic things that are required. Views should be handled here but inherited from the template graph file. Args: template (str): Path to template nuke script. """ getLog().info("Ada: Fuel begin.") nuke.scriptOpen(template) template = nuke.Ada.template.location try: getLog().info("Ada: Reading template: {0}".format(template)) nuke.scriptReadFile(template) except RuntimeError as err: getLog().warning("Ada: '{0}'".format(err)) start = nuke.Ada.script_frame_range.start end = nuke.Ada.script_frame_range.end root = nuke.Root() root["first_frame"].setValue(start) root["last_frame"].setValue(end) getLog().info("Ada: setting root range: start: {0}, end: {1}".format( start, end)) getLog().info("Ada: Fuel end.")
def execute_callbacks(cls, kind, template_class=None): """ Callbacks are registered as global before/after or template specific. Args: kind (str): The callback group we want to execute. template_class (str): Script class name stored on the root node of Nuke. """ cbs_to_execute = callback_kinds[kind] cb_message = "Ada: Executing {kind} callback: {name}" for callback_name in cbs_to_execute: if kind.startswith("GLOBAL"): getLog().info( cb_message.format(kind=" ".join(kind.lower().split("_")), name=callback_name)) for cb in cbs_to_execute[callback_name]: cb.run() if kind.startswith("TEMPLATE") and template_class == callback_name: getLog().info( cb_message.format(kind=" ".join(kind.lower().split("_")), name=callback_name)) for cb in cbs_to_execute[callback_name]: cb.run()
def load_ada_context_from_file(): """From a root script find an associated ada file with it.""" root_script = nuke.Root().name() if not root_script.endswith(".nk"): return script_directory = os.path.dirname(root_script) script_name = os.path.basename(root_script) ada_context_file = "{0}.{1}".format(os.path.splitext(script_name)[0], "ada") ada_file_path = os.path.join(script_directory, ada_context_file) getLog().info("Ada: Attempting to find context file: {0}".format(ada_file_path)) if os.path.exists(ada_file_path): getLog().debug("Ada: context found: {0}".format(ada_file_path)) # read the ada context file from the script dir f = open(ada_file_path, "rb") nuke.Ada.ParseFromString(f.read()) f.close() getLog().debug( "Ada: context successfully loaded from file: {0}".format(nuke.Ada) ) elif nuke.Root().knobs().get("__ada"): get_ada_from_root = nuke.Root()["__ada"].value() nuke.Ada.ParseFromString(get_ada_from_root) getLog().debug( "Ada: context successfully loaded from root: {0}".format(nuke.Ada) )
def run(cls, nodes=None, bake_list=None, pause=None): """ Once the nodes are gathered up then we can iterate over them and execute each knob on the tab from the top to the bottom. Args: nodes (list): Un-sorted nodes either a selection in Nuke. bake_list (list): List of nodes to be processed, these have already been gathered and sorted. pause (int): The queue order which you want to stop at. Returns: list: all the nodes which were executed successfully. """ # any nuke script can add a __template_class_text knob to its # root node and register a callback against it cls.template_class = (nuke.Root().knobs().get( "__template_class__", nuke.Text_Knob("null", "null")).value()) cls.template_class = None if cls.template_class == "" else cls.template_class # if we are not continuing a previous bake, then we need to # gather, sort and validate the nodes. if not bake_list: cls.input_nodes = nodes or nuke.allNodes(recurseGroups=True) gathered_nodes = cls.gather(cls.input_nodes) bake_list = cls.alpha_numeric_sort(gathered_nodes) getLog().info("Ada: Nodes to bake: {0}".format(bake_list)) # run validation code / frame work before executing tabs cls.validate(bake_list) # queue nodes for baking queue = cls.queue(bake_list) getLog().info("template class: {}".format(cls.template_class)) # execute registered global/template callbacks before cls.execute_callbacks("GLOBAL_BEFORE") cls.execute_callbacks("TEMPLATE_BEFORE", template_class=cls.template_class) # process the nodes which have ada tabs results = cls.process(queue, bake_list, pause) # execute registered global/template callbacks after cls.execute_callbacks("TEMPLATE_AFTER") cls.execute_callbacks("GLOBAL_AFTER", template_class=cls.template_class) return results
def ada_knob_changed(): """ When a user interacts with the knobs to serialise knob they will be modifying context information, this knob changed keeps that context information up to date. """ this_node = nuke.thisNode() this_knob = nuke.thisKnob() if this_knob.name() == "knobs_to_serialise" and has_ada_tab(this_node): # split all the values in knobs_to_serialise by their line for alias in this_knob.value().split("\n"): current_alias = alias.strip() if current_alias == "": continue # for this alias get the correct object with the data inside the function ada_data = deconstruct_knobs_to_serialise(current_alias) # check to make sure that the serialised data can be parsed if not ada_data: continue # look up the knob on this node knob = this_node[ada_data.knob.strip()] # get the current expression expression = knob.toScript() getLog().info("Current expression {0}:{1}".format( knob.name(), expression)) execution_type = KNOB_TO_EXECUTION_TYPE.get(knob.Class()) if isinstance(ada_data, KnobAlias): set_alias_knob(knob, expression, ada_data, "aliases") add_knob_to_ada_tab(this_node, execution_type, "aliases", knob, ada_data.alias) if isinstance(ada_data, KnobInput): set_alias_knob(knob, expression, ada_data, "inputs") add_knob_to_ada_tab(this_node, execution_type, "inputs", knob, ada_data.alias) if isinstance(ada_data, KnobOutput): set_alias_knob(knob, expression, ada_data, "outputs") add_knob_to_ada_tab(this_node, execution_type, "outputs", knob, ada_data.alias)
def bake_knobs(cls, node): """ Take the string in the knobs to bake knob and then create a list of knobs to run bake_knob on. Args: node (nuke.Node): current node we are baking in the queue. """ if node.knob("knobs_to_bake") and node.knob("knobs_to_bake").value(): # get bake knob list, split, and strip white space knobs = node.knob("knobs_to_bake").value() raw_knobs_to_set = knobs.split(",") for raw_knob in raw_knobs_to_set: knob = raw_knob.strip() getLog().info(" Ada: Baking Knob: {0}.{1}".format( node.name(), knob)) cls.bake_knob(node, knob)
def process(cls, queues, all_nodes, break_point): """ Bulk of the work happens here, knobs are baked, set, buttons are pressed, code is executed etc. Args: queues (itertools.groupby): Groups of nodes to process. all_nodes (list): List of all nodes to bake. break_point (int): Point at which we want Ada to stop executing to inspect something. Returns: list: Nodes which were successfully execute. """ processed_nodes = [] for queue_order, queue in queues: getLog().info("Ada: Executing Queue: {0}".format(queue_order)) if queue_order == break_point: to_process = list(set(all_nodes) - set(processed_nodes)) return to_process for node_name in list(queue): node = nuke.toNode(node_name) getLog().info(" Ada: Executing Node {0}".format(node.name())) # this is where values are converted from ada # expressions to serialised values. cls.bake_knobs(node) cls.set_knobs(node) # buttons are pressed if added in the Ada tab. cls.execute_buttons(node) # finally code is executed. cls.execute_code(node) # if all goes well return the node we just processed # to the list of processed nodes. processed_nodes.append(node_name) return processed_nodes
def execute_buttons(node): """ Execute any buttons the user has added to the execute knobs list. Args: node (nuke.Node): The node we are currently baking. """ if node.knob("execute_knobs") and node.knob("execute_knobs").value(): # get button list, split, and strip white space buttons = node.knob("knobs_to_execute").value() raw_knobs_to_exec = buttons.split(",") for raw_knob in raw_knobs_to_exec: knob = raw_knob.strip() knob_object = node.knob(knob) if not knob_object or not hasattr(knob_object, "execute"): getLog().warning( "Executing knob: '{0}' not found on node '{1}'".format( knob, node.name())) else: getLog().info("Ada: Button to execute: {0}".format(knob)) knob_object.setFlag(0x0000000000010000) knob_object.execute()
def set_alias_knob(knob, expression, alias_data, kind): """ Add an expression to the correct knob and add an alias to the ada context or if the user changes it then remove the old context item and add a new one. Args: knob (nuke.Knob): The knob we want to add the expression to. expression (str): The current expression on the node's knob. alias_data (namedtuple): The data from the knobs_to_serialise knob. kind (str): """ this_alias = alias_data.alias.strip() value = alias_data.default_value.strip() expression_to_set = "[Ada {kind} {value}]".format(kind=kind, value=this_alias) get_ada_kind = getattr(nuke.Ada, kind) if this_alias in get_ada_kind: getLog().info("Updating context data: {0} = {1}".format( this_alias, value)) get_ada_kind[this_alias] = value elif this_alias not in get_ada_kind: getLog().info("Setting context data: {0} = {1}".format( this_alias, value)) get_ada_kind[this_alias] = value knob.setExpression(expression_to_set) if this_alias not in expression and "[Ada" in expression: old_alias = expression.split("]")[0].split(" ")[-1] del get_ada_kind[old_alias] knob.setExpression(expression_to_set) else: getLog().info("Updating context data: {0} = {1}".format( this_alias, value))
def bake_knob(node, name, value=None): """ Function to set a knob using knob=value or bake the knob directly. Args: node (nuke.Node): A node we are trying to bake a knob on. name (str): The knob name we are trying to set. value ([Optional str]): If value is None execute the """ knob = node.knob(name) if not knob: return if value is None: if isinstance(knob, nuke.Array_Knob): views = nuke.views() start = nuke.Ada.script_frame_range.start end = nuke.Ada.script_frame_range.end for view in views: curves = knob.animations(view) for curve in curves: values = set() index = curve.knobIndex() for frame in range(start, end + 1): knob.setKeyAt(frame, index, view) value = curve.evaluate(frame) values.add(value) knob.setValueAt(value, frame, index, view) if len(values) > 1: knob.setExpression("curve", channel=index, view=view) else: knob.clearAnimated() # "BAKE" elif isinstance(knob, nuke.File_Knob): knob.setValue(str(nuke.tcl("subst", knob.value()))) elif isinstance(knob, nuke.EvalString_Knob): knob.setValue(knob.evaluate()) else: getLog().warning("{}.{} unsupported knob type {}".format( node.name(), name, type(knob))) else: try: knob.clearAnimated() except AttributeError: pass try: if isinstance(knob, nuke.Int_Knob): knob.setValue(int(value)) elif isinstance(knob, nuke.Enumeration_Knob): try: knob.setValue(int(value)) except ValueError: knob.setValue(str(value)) elif isinstance(knob, nuke.Array_Knob): knob.setValue(float(value)) else: knob.setValue(value) except TypeError as ee: getLog().warning("Ada: set {}.{}={} TypeError: {}".format( node.name(), name, value, ee)) except ValueError as ee: getLog().warning("Ada: set {}.{}={} ValueError: {}".format( node.name(), name, value, ee))