Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avalon Nuke Copy and Paste feature #30

Open
wants to merge 2 commits into
base: production
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 137 additions & 0 deletions avalon/nuke/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import contextlib
import nuke

from . import lib
from .. import api, io


Expand Down Expand Up @@ -120,6 +121,142 @@ def reset_resolution():
nuke.root()["format"].setValue(fmt)


def copy_selected_nodes():
main_container = nuke.toNode(lib.AVALON_CONTAINERS)
if main_container is None:
nuke.nodeCopy("%clipboard%")
return
nodes = nuke.selectedNodes()

# Create a temp main container, which contains the nodes should to be copied
lib.reset_selection()
lib.select_nodes([main_container])
nuke.nodeCopy("%clipboard%")
nuke.nodePaste("%clipboard%")
temp_main_container = nuke.selectedNodes()[0]

# Get all container nodes that should be copied
container_nodes = []
for node in nodes:
node_id = lib.get_id(node)
container_id = lib.get_id(node, container_id=True)
if node_id and container_id:
container_node = lib.lsattr("avalon:avalonId",
value=container_id,
group=temp_main_container,
recursive=True)
container_nodes += container_node
container_nodes = list(set(container_nodes))

# Get all nodes that should be copied
temp_main_container.begin()
target_nodes = []
for container_node in container_nodes:
target_nodes.append(container_node)
target_nodes += lib.lsattr("avalon:containerId",
value=container_node["avalon:avalonId"].getValue(),
group=temp_main_container,
recursive=True)
all_nodes = nuke.allNodes(group=temp_main_container, recurseGroups=True)
for node in all_nodes:
if not (node in target_nodes):
nuke.delete(node)
temp_main_container.end()

lib.reset_selection()
lib.select_nodes([temp_main_container])
lib.select_nodes(nodes)
nuke.nodeCopy("%clipboard%")
nuke.delete(temp_main_container)


def paste_selected_nodes():
id_update_dict = {}

def update_node_id(node):
node_id = lib.get_id(node)
if node_id:
if node_id in id_update_dict:
new_node_id = id_update_dict[node_id]
else:
new_node_id = str(io.ObjectId())
id_update_dict[node_id] = new_node_id
node["avalon:avalonId"].setValue(new_node_id)

container_id = lib.get_id(node, container_id=True)
if container_id:
if container_id in id_update_dict:
new_container_id = id_update_dict[container_id]
else:
new_container_id = str(io.ObjectId())
id_update_dict[container_id] = new_container_id
node["avalon:containerId"].setValue(new_container_id)

main_container = lib.main_container_node()
lib.reset_selection()
nuke.nodePaste("%clipboard%")
nodes = nuke.selectedNodes()
for node in nodes:
update_node_id(node)
if node.name().startswith(lib.AVALON_CONTAINERS) and node.Class() == "Group":
node.begin()
all_nodes = nuke.allNodes(group=node, recurseGroups=True)
for _node in all_nodes:
update_node_id(_node)
lib.reset_selection()
lib.select_nodes(all_nodes)
nuke.nodeCopy("%clipboard%")
node.end()
main_container.begin()
nuke.nodePaste("%clipboard%")
main_container.end()
nuke.delete(node)
lib.reset_selection()
lib.select_nodes(nodes)
nuke.nodeCopy("%clipboard%")


def delete_selected_nodes():
main_container = nuke.toNode(lib.AVALON_CONTAINERS)
nodes = nuke.selectedNodes()
if main_container is None:
nuke.delete(nodes)
return

# Get all container node ids that should be deleted
container_ids = []
for node in nodes:
container_id = lib.get_id(node, container_id=True)
if container_id:
container_ids.append(container_id)
container_ids = list(set(container_ids))

with viewer_update_and_undo_stop():
# Delete nodes in Main container
main_container.begin()
target_nodes = []
for container_id in container_ids:
target_nodes += lib.lsattr("avalon:avalonId",
value=container_id,
group=main_container,
recursive=False)
target_nodes += lib.lsattr("avalon:containerId",
value=container_id,
group=main_container,
recursive=False)
for node in target_nodes:
nuke.delete(node)
main_container.end()

# Delete nodes in Node Graph
target_nodes = []
for container_id in container_ids:
target_nodes += lib.lsattr("avalon:containerId",
value=container_id,
recursive=False)
for node in target_nodes:
nuke.delete(node)

@contextlib.contextmanager
def viewer_update_and_undo_stop():
"""Lock viewer from updating and stop recording undo steps"""
Expand Down
17 changes: 17 additions & 0 deletions avalon/nuke/lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
mold = knobby.util.mold

AVALON_TAB = "avalon"
AVALON_CONTAINERS = "AVALON_CONTAINERS"


@contextlib.contextmanager
Expand Down Expand Up @@ -61,6 +62,22 @@ def select_nodes(nodes):
node["selected"].setValue(True)


def main_container_node():
"""Get main container node. Create if not exist.
"""

main_container = nuke.toNode(AVALON_CONTAINERS)
if main_container is None:
main_container = nuke.createNode("Group")
main_container.setName(AVALON_CONTAINERS)
main_container["postage_stamp"].setValue(True)
main_container["note_font_size"].setValue(40)
main_container["tile_color"].setValue(int("0x283648FF", 16))
main_container["xpos"].setValue(-500)
main_container["ypos"].setValue(-500)

return main_container

def lsattr(attr, value=None, type=None, group=None, recursive=False):
"""Return nodes matching `key` and `value`

Expand Down
15 changes: 6 additions & 9 deletions avalon/nuke/pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,15 +139,7 @@ def containerise(name,

nuke.nodeCopy("_containerizing_")

main_container = nuke.toNode(AVALON_CONTAINERS)
if main_container is None:
main_container = nuke.createNode("Group")
main_container.setName(AVALON_CONTAINERS)
main_container["postage_stamp"].setValue(True)
main_container["note_font_size"].setValue(40)
main_container["tile_color"].setValue(int("0x283648FF", 16))
main_container["xpos"].setValue(-500)
main_container["ypos"].setValue(-500)
main_container = lib.main_container_node()

main_container.begin()
nuke.nodePaste("_containerizing_")
Expand Down Expand Up @@ -438,6 +430,11 @@ def _install_menu():
menu.addCommand("Reset Frame Range", command.reset_frame_range)
menu.addCommand("Reset Resolution", command.reset_resolution)

menu.addSeparator()
menu.addCommand("Avalon Copy", command.copy_selected_nodes, "Alt+Shift+C")
menu.addCommand("Avalon Paste", command.paste_selected_nodes, "Alt+Shift+V")
menu.addCommand("Avalon Delete", command.delete_selected_nodes, "Alt+Del")

# add reload pipeline only in debug mode
if bool(os.getenv("NUKE_DEBUG")):
menu.addSeparator()
Expand Down