This simple code copies and pastes your selection while maintaining the input connection of the highest-positioned node. I have this code set to the hotkey shift+d.

Expand Duplicate Nodes
import nuke
import nukescripts
#####################################################################
# We create a list of all the selected nodes. We will use it to
# check what function will run depending on single or
# multiple selection.
def selectedNamesList():
selected = nuke.selectedNodes()
# We use a global variable to call it from other functions.
global selNames
selNames = []
for i in selected:
nodeName = i['name'].value()
selNames.append(nodeName)
selNames = sorted(selNames)
#####################################################################
# This function will run if there is just one node selected.
def duplicateNodeSimple():
global selNames
# Deselect all nodes.
for node in nuke.allNodes():
node['selected'].setValue(False)
# We go through all the nodes of the global variable selNames.
# We select each and save their X and Y positions.
n = nuke.toNode(selNames[0])
n['selected'].setValue(True)
xpos = n.xpos()
ypos = n.ypos()
# We create an empty list and populate it with the node's
# dependencies names (nodes connected to its inputs).
dependenciesList = []
for node in nuke.selectedNode().dependencies():
depName = node.name()
dependenciesList.append(depName)
# We check the number of the highest input connected and use
# it to populate an empty list with 1 (if the input in that
# position is connected) and 0 (if it is not connected).
# For example, if a Merge has A connected, but not B or mask,
# the list would be [0,1].
maxInputConnected = nuke.selectedNode().inputs()
connectedList = []
for i in range(maxInputConnected):
connResult = 1 if nuke.selectedNode().input(i) else 0
connectedList.append(connResult)
# We also save the number of dependencies found
depNumber = len(connectedList)
# We copy the node, deselect it, paste the new one and
# set its position apart from the original one.
nuke.nodeCopy(nukescripts.cut_paste_file())
n['selected'].setValue(False)
nuke.nodePaste(nukescripts.cut_paste_file())
nuke.selectedNode().setXYpos((xpos+90),(ypos+10))
# We create two different counters: one for the number of
# dependencies and another one for the number of
# connections. A node can have two dependencies and three
# connections, for example a Merge with B and Mask connected,
# but not A, will have two dependencies (or one if B and Mask
# are connected to the same node) and three connections (A
# counts as a connection even if it is not connected).
depCounter = 0
connCounter = 0
while connCounter <= (depNumber-1):
# Using connectedList we created before, we connect
# inputs to the correct node (if it is 1).
if connectedList[connCounter] == 1:
nuke.selectedNode().setInput(connCounter, nuke.toNode(str(dependenciesList[depCounter])))
depCounter = depCounter + 1
connCounter = connCounter + 1
# Or leave them disconnected (if it is 0).
elif connectedList[depCounter] == 0:
connCounter = connCounter + 1
# We could deselect the new node if we want to, but I preferred to
# let it selected just in case you want to do more copies from it.
# for node in nuke.allNodes():
# node['selected'].setValue(False)
# Delete stored variables.
# del n
# del xpos
# del ypos
# del selNames
#####################################################################
# This function will run if there is more than one node selected.
def duplicateNodeMulti():
global selNames
# We create an empty list and populate it with each node's
# dependencies names (nodes connected to its inputs).
dependenciesList = []
for node in selNames:
nodeName = nuke.toNode(node)
nodeName['selected'].setValue(True)
for i in nodeName.dependencies():
depName = i.name()
dependenciesList.append(depName)
nodeName['selected'].setValue(False)
# Because we don't know if there will be any dependencies or not,
# we will create an exception for IndexError just in case there
# are no dependencies.
try:
# This variable will compare the list of selected nodes and
# the list of the dependencies of each of those selected
# nodes. The name present in dependenciesList that is not
# present in selNames is the node we will need to connect
# our duplicated node to.
topConnNode = [item for item in dependenciesList if item not in selNames][0]
# We check now all the nodes connected to the node we found in
# previous list comparison.
topNodeOutputList = []
for node in nuke.toNode(topConnNode).dependent():
topNodeOutputName = node.name()
topNodeOutputList.append(topNodeOutputName)
# And compared the new created list with the list of selected
# nodes to find what is the first node to duplicate.
topToDuplicate = [item for item in topNodeOutputList if item in selNames][0]
# Now that we have the first node located, we check its
# dependencies and creat a list with all of them.
nuke.toNode(topToDuplicate)['selected'].setValue(True)
nodeToDuplicateDepList = []
for node in nuke.selectedNode().dependencies():
depName = node.name()
nodeToDuplicateDepList.append(depName)
# If the list we just created has more than one element, it
# means the node has more than 1 input connected.
if len(nodeToDuplicateDepList) > 1:
# We create a list of 1 (if the input is connected) and
# 0 (if the input is not connected).
maxInputConnected = nuke.selectedNode().inputs()
connectedList = []
for i in range(maxInputConnected):
connResult = 1 if nuke.selectedNode().input(i) else 0
connectedList.append(connResult)
depNumber = len(connectedList)
# We copy, deselect the first node, and paste the new one.
nuke.nodeCopy(nukescripts.cut_paste_file())
nuke.selectedNode()['selected'].setValue(False)
nuke.nodePaste(nukescripts.cut_paste_file())
# We create two different counters: one for the number of
# dependencies and one for the number of connections.
depCounter = 0
connCounter = 0
while connCounter <= (depNumber-1):
# Using connectedList we created before, we connect
# inputs to the correct node (if it is 1).
if connectedList[connCounter] == 1:
nuke.selectedNode().setInput(connCounter, nuke.toNode(str(nodeToDuplicateDepList[depCounter])))
depCounter = depCounter + 1
connCounter = connCounter + 1
# Or leave them disconnected (if the input is 0).
elif connectedList[depCounter] == 0:
connCounter = connCounter + 1
# We deselect the node we just pasted.
topDuplicated = nuke.selectedNode().name()
nuke.toNode(topDuplicated)['selected'].setValue(False)
# We select all the nodes in our selNodes list, except
# the one we already duplicated.
for node in selNames:
nodeName = nuke.toNode(node)
if not nodeName['name'].getValue() == topToDuplicate:
nodeName['selected'].setValue(True)
# We copy the selected nodes and deselect them.
nuke.nodeCopy(nukescripts.cut_paste_file())
for node in nuke.selectedNodes():
node['selected'].setValue(False)
# We select the node we already duplicated.
nuke.toNode(topDuplicated)['selected'].setValue(True)
# And paste the other nodes under it.
nuke.nodePaste(nukescripts.cut_paste_file())
nuke.toNode(topDuplicated)['selected'].setValue(True)
# If the list we created before (nodeToDuplicateDepList) has
# one element, it means the node has one input connected.
else:
# We copy, deselect, paste the new node and connect it.
nuke.nodeCopy(nukescripts.cut_paste_file())
nuke.toNode(topToDuplicate)['selected'].setValue(False)
nuke.nodePaste(nukescripts.cut_paste_file())
nuke.selectedNode().setInput(0, nuke.toNode(topConnNode))
# We deselect the node we just pasted.
topDuplicated = nuke.selectedNode().name()
nuke.toNode(topDuplicated)['selected'].setValue(False)
# We select all the nodes in our selNodes list, except
# the one we already duplicated.
for node in selNames:
nodeName = nuke.toNode(node)
if not nodeName['name'].getValue() == topToDuplicate:
nodeName['selected'].setValue(True)
# We copy the selected nodes and deselect them.
nuke.nodeCopy(nukescripts.cut_paste_file())
for node in nuke.selectedNodes():
node['selected'].setValue(False)
# We select the node we already duplicated.
nuke.toNode(topDuplicated)['selected'].setValue(True)
# And paste the other nodes under it.
nuke.nodePaste(nukescripts.cut_paste_file())
nuke.toNode(topDuplicated)['selected'].setValue(True)
# If there the selection is not connected to anything, we will
# have an IndexError. In that case:
except IndexError:
# We select every node of our original selection.
for node in selNames:
nodeName = nuke.toNode(node)
nodeName['selected'].setValue(True)
# Copy and deselect the original nodes.
nuke.nodeCopy(nukescripts.cut_paste_file())
for node in nuke.selectedNodes():
node['selected'].setValue(False)
# And paste the new ones.
nuke.nodePaste(nukescripts.cut_paste_file())
#####################################################################
# We use this function to know how many nodes we selected, and apply
# one function or another depending on this number.
def duplicateNodeCheck():
selectedNamesList()
global selNames
if len(selNames) < 1:
nuke.message('Please, select one or more nodes to duplicate.')
elif len(selNames) > 1:
duplicateNodeMulti()
else:
duplicateNodeSimple()