# Copyright (c) 2009 The Foundry Visionmongers Ltd.  All Rights Reserved.
import nuke_internal as nuke
import nukescripts
import random
import os
import textwrap
[docs]def copy_knobs(args):
  thisGroup = nuke.thisGroup()
  if( thisGroup is not nuke.root() and ( thisGroup.locked() or thisGroup.subgraphLocked() ) ):
    raise RuntimeError("Can't paste knob values because " + thisGroup.name() + " is locked")
  selNodes = thisGroup.selectedNodes()
  groupCopy = nuke.nodes.Group(name = "____tempcopyknobgroup__")
  with groupCopy:
    nuke.nodePaste(nukescripts.cut_paste_file())
  excludedKnobs = ["name", "xpos", "ypos"]
  try:
    nodes = groupCopy.nodes()
    for i in groupCopy.nodes():
      for j in selNodes:
        k1 = i.knobs()
        k2 = j.knobs()
        intersection = dict([(item, k1[item]) for item in list(k1.keys()) if item not in excludedKnobs and item in k2])
        for k in list(intersection.keys()):
          x1 = i[k]
          x2 = j[k]
          x2.fromScript(x1.toScript())
  except Exception as e:
    nuke.delete(groupCopy)
    raise e
  nuke.delete(groupCopy) 
[docs]def connect_selected_to_viewer(inputIndex):
  """Connects the selected node to the given viewer input index, ignoring errors if no node is selected."""
  selection = None
  try:
    selection = nuke.selectedNode()
  except ValueError:  # no node selected
    pass
  if selection is not None and selection.Class() == 'Viewer':
    selection = None
  nuke.connectViewer(inputIndex, selection) 
[docs]def toggle_monitor_out():
  """Toggles monitor out (switches it on if it's off, or vice versa) for the currently active viewer."""
  viewerWindow = nuke.activeViewer()
  if viewerWindow is not None:
    viewerNode = viewerWindow.node()
    monitorOutEnableKnob = viewerNode["monitorOutEnable"]
    enabled = monitorOutEnableKnob.value()
    monitorOutEnableKnob.setValue(not enabled) 
[docs]def clear_selection_recursive(group = nuke.root()):
  """Sets all nodes to unselected, including in child groups."""
  for n in group.selectedNodes():
    n.setSelected(False)
  groups = [i for i in group.nodes() if i.Class() == 'Group']
  for i in groups:
    clear_selection_recursive(i) 
[docs]def goofy_title():
  """Returns a random message for use as an untitled script name.
  Can be assigned to nuke.untitled as a callable.
  Put a goofy_title.txt somewhere in your NUKE_PATH to customise."""
  goofyFile = None
  for dir in nuke.pluginPath():
      fileName = os.path.join(dir, "goofy_title.txt")
      if os.path.exists(fileName):
          goofyFile = fileName
          break
  if goofyFile is None:
      return "Missing goofy_title.txt"
  file = open(goofyFile)
  lines = file.readlines()
  file.close()
  lines = [line.strip() for line in lines]
  lines = [line for line in lines if len(line) > 0 and line[0] != '#']
  if len(lines) < 1:
    return "Empty goofy_title.txt"
  return random.choice(lines) 
[docs]def declone(node):
  if node.clones() == 0:
    return
  args = node.writeKnobs(nuke.WRITE_ALL | nuke.WRITE_USER_KNOB_DEFS | nuke.WRITE_NON_DEFAULT_ONLY | nuke.TO_SCRIPT)
  newnode = nuke.createNode(node.Class(), knobs = args)
  nuke.inputs(newnode, nuke.inputs(node))
  num_inputs = nuke.inputs(node)
  for i in range(num_inputs):
     newnode.setInput(i, node.input(i))
  node.setInput(0, newnode)
  nuke.delete(node) 
[docs]def showname():
  '''Shows the current script path and, if the selected node is a Read or Write node, the filename from it.'''
  # get the nuke script path
  # we always need this
  nukescript = nuke.value("root.name")
  # look if there is a selected node
  # if not, output the script only
  p = nuke.Panel("Current Info", 500)
  try:
    n = nuke.selectedNode()
    if n.Class() == "Read" or n.Class() == "Write":
      a = nuke.value(n.name()+".first", nuke.value("root.first_frame"))
      b = nuke.value(n.name()+".last", nuke.value("root.last_frame"))
      curfile = n.knob("file").value()+" "+str(a)+"-"+str(b)
      p.addSingleLineInput("Filename", curfile)
      p.addSingleLineInput("Script", nukescript)
      p.show()
    else:
      p.addSingleLineInput("Script", nukescript)
      p.show()
  except:
    p.addSingleLineInput("Script", nukescript)
    p.show() 
[docs]def swapAB(n):
  """Swaps the first two inputs of a node."""
  thisGroup = nuke.thisGroup()
  if thisGroup is not nuke.root() and ( thisGroup.locked() or thisGroup.subgraphLocked() ) :
    lockedReason = "published" if thisGroup.subgraphLocked() else "locked"
    raise RuntimeError("Can't swap nodes because " + thisGroup.name() + " is " + lockedReason)
  if max(n.inputs(), n.minimumInputs()) > 1:
    a = n.input(0)
    n.setInput(0, n.input(1))
    n.setInput(1, a) 
[docs]def print_callback_info(verbose=False, callbackTypes=None):
  """
  Returns a list of all currently active callbacks, with the following optional
  arguments:
      verbose=False      : prints the documentation as well as the callback
      callbackTypes=None : limit the callback info to a particular callback
                           type (e.g. ['OnCreates'])
  """
  # list of all callback types
  all_Callback_Types = [ 'onUserCreates',
                         'onCreates',
                         'onScriptLoads',
                         'onScriptSaves',
                         'onScriptCloses',
                         'onDestroys',
                         'knobChangeds',
                         'updateUIs',
                         'autolabels',
                         'beforeRenders',
                         'beforeFrameRenders',
                         'afterRenders',
                         'afterFrameRenders',
                         'renderProgresses',
                         'filenameFilters',
                         'validateFilenames',
                         'autoSaveFilters',
                         'autoSaveRestoreFilters',
                         'autoSaveDeleteFilters',
                      ]
  #if no callbackTypes defined or is an invalid type, then default search all callback types
  is_valid_type = (type(callbackTypes) is dict) or (type(callbackTypes) is list)
  if ( not callbackTypes or (not is_valid_type ) ):
    callbackTypes = all_Callback_Types
  callback_defs = {}
  for callbackType in callbackTypes:
    callback_defs[callbackType] = eval('nuke.%s' %(callbackType))
  # find the max target name length
  maxTargetNameLen = max( list(map( len,' '.join( [ (k+'').join( list(callback_defs[k].keys()) ) for k in list(callback_defs.keys()) ] ).split(' ') )) )
  indent = (maxTargetNameLen+8)*' '
  sortedCallbackTypes = sorted( callback_defs.keys() )
  for callbackType in sortedCallbackTypes:
    for callbackTarget in list(callback_defs[callbackType].keys()):
      for func, a, b, c in callback_defs[callbackType][callbackTarget]:
        id = '%s%s%s : '%(callbackType[:-1],'_'*(maxTargetNameLen-len(callbackType)-len(callbackTarget)+1),callbackTarget)
        print('%s%s' %(id, func.__name__))
        if verbose:
          doc = func.__doc__
          if not doc:
            doc='NO DOCUMENTATION'
          docNoReturns = str(doc).lstrip().replace('\n','')
          docNoConsecutiveSpaces = " ".join(docNoReturns.split())
          docWrappedText = textwrap.wrap(docNoConsecutiveSpaces, 60)
          for line in docWrappedText:
            print(indent + line.replace('\n', '\n' + indent))