It that checks the context you are pasting in and the type of node you are trying to paste in some cases.
For example, it will paste copied nodes in geo level as sop imports when pasting in lops and it wil paste cop networks as an image node when working in a materialXSubnet.
I bind it to ctrl + shift + v
import hou import sys import os def debug_log(message): """Ensures messages are printed and flushed to console.""" print(message) sys.stdout.flush() def validate_clipboard_nodes(clipboard): """Validate clipboard content and ensure it contains valid Houdini node paths.""" valid_nodes = [] paths = clipboard.split() debug_log(f"Clipboard split: {paths}") for path in paths: if not path.startswith("/"): continue try: node = hou.node(path) if node: valid_nodes.append(node) except hou.ObjectWasDeleted: debug_log(f"Node deleted: {path}") except Exception as e: debug_log(f"Error accessing {path}: {e}") return valid_nodes def get_context_type(network): """Determine context type, treating OBJ/geo as SOP context.""" category = network.childTypeCategory().name().lower() if category == "object": if network.type().name() == "geo": return "sop" else: return "object" return category def offset_positions(base_pos, count, dx=2.0, dy=0.0): """Return offset positions for laying out new nodes.""" return [hou.Vector2(base_pos[0] + i * dx, base_pos[1] + i * dy) for i in range(count)] def layout_new_nodes(network, new_nodes): """Layout only the newly created nodes.""" try: network.layoutChildren(items=new_nodes) except Exception as e: debug_log(f"Layout failed: {e}") # --- SOP --- def create_object_merges(network, nodes, pos): created = [] positions = offset_positions(pos, len(nodes)) for i, src in enumerate(nodes): om = network.createNode("object_merge", f"copied_object_merge{i+1}") om.setPosition(positions[i]) om.parm("objpath1").set(src.path()) created.append(om) return created def create_font(network, pos, clipboard_text): font = network.createNode("font", "clipboard_text") font.setPosition(pos) if font.parm("text"): font.parm("text").set(clipboard_text) return [font] # --- OBJ --- def create_geos(network, nodes, pos): created = [] positions = offset_positions(pos, len(nodes)) for i, src in enumerate(nodes): geo = network.createNode("geo", f"copied_geo{i+1}") geo.setPosition(positions[i]) om = geo.createNode("object_merge", "inside_merge") om.parm("objpath1").set(src.path()) created.append(geo) return created def create_geo_with_font(network, pos, clipboard_text): geo = network.createNode("geo", "clipboard_geo_text") geo.setPosition(pos) font = geo.createNode("font", "clipboard_text") font.parm("text").set(clipboard_text) return [geo] # --- LOP / COP --- def create_sopimports(network, nodes, pos, prefix="copied_sopimport"): created = [] positions = offset_positions(pos, len(nodes)) for i, src in enumerate(nodes): si = network.createNode("sopimport", f"{prefix}{i+1}") si.setPosition(positions[i]) if si.parm("usesoppath"): si.parm("usesoppath").set(1) if si.parm("soppath"): si.parm("soppath").set(src.path()) created.append(si) return created def create_sopcreate_with_font(network, pos, clipboard_text): sc = network.createNode("sopcreate", "clipboard_sopcreate") sc.setPosition(pos) font = sc.createNode("font", "clipboard_text") font.parm("text").set(clipboard_text) return [sc] # --- COP special rules --- def create_in_cop_context(network, valid_nodes, pos, clipboard): """Handle COP paste rules: node paths → sopimport, text → font.""" if valid_nodes: return create_sopimports(network, valid_nodes, pos, prefix="copied_sopimport") else: # Heuristic: if clipboard looks like a file path to an image, drop a file COP if os.path.splitext(clipboard)[1].lower() in (".jpg", ".jpeg", ".png", ".exr", ".tif", ".tiff"): file_cop = network.createNode("file", "clipboard_file") file_cop.setPosition(pos) file_cop.parm("filename1").set(clipboard) return [file_cop] else: # Plain text → font COP font_cop = network.createNode("font", "clipboard_text") font_cop.setPosition(pos) font_cop.parm("text").set(clipboard) return [font_cop] # --- Main --- def main(): pane = hou.ui.curDesktop().paneTabUnderCursor() if not pane: hou.ui.displayMessage("No active network pane found") return network = pane.pwd() pos = pane.cursorPosition() clipboard = hou.ui.getTextFromClipboard().strip() context_type = get_context_type(network) valid_nodes = validate_clipboard_nodes(clipboard) created_nodes = [] if context_type == "sop": created_nodes = create_object_merges(network, valid_nodes, pos) if valid_nodes else create_font(network, pos, clipboard) elif context_type == "object": created_nodes = create_geos(network, valid_nodes, pos) if valid_nodes else create_geo_with_font(network, pos, clipboard) elif context_type == "lop": created_nodes = create_sopimports(network, valid_nodes, pos) if valid_nodes else create_sopcreate_with_font(network, pos, clipboard) elif context_type in ("cop2", "cop"): created_nodes = create_in_cop_context(network, valid_nodes, pos, clipboard) else: hou.ui.displayMessage(f"Unsupported context: {context_type}") return if created_nodes: for i, node in enumerate(created_nodes): node.setSelected(i == 0, clear_all_selected=True) if i == 0 else node.setSelected(True) layout_new_nodes(network, created_nodes) else: hou.ui.displayMessage("No valid nodes or text in clipboard") main()