如何用Python修改Nuke的Contact Sheet实现EXR与HUD合并?
解决Nuke中Contact Sheet的EXR+HUD叠加及节点布局问题
问题描述
我用Python脚本在Nuke里生成Contact Sheet,现有逻辑是把实体列表按30个一组拆分,给每个实体创建EXR的Read节点(路径为空时用Constant节点),再连到Contact Sheet。现在要优化成:每个实体同时创建EXR和HUD的Read节点,用Merge节点叠加后再连到Contact Sheet,但尝试时出现节点布局混乱、连接错误的问题,求解决实现流程和布局问题。
现有脚本代码:
def create_contact_sheet(self,entities, version_paths, first_frame,last_frame): movie_per_sheet = 30 print ("no of movies per sheet:", movie_per_sheet) shot_list_splits = [entities[i:i + movie_per_sheet] for i in range(0, len(entities), movie_per_sheet)] print ("no of required sheets:", len(shot_list_splits)) print("entities", entities) print("shot version split", version_paths) last_batch_rnode_pos = 0 first_rnode_xpos = None r_node_xpos = None print('version map', version_paths) for sht_index, e_sht_list in enumerate(shot_list_splits): nodes_for_backdrop = [] # create contact sheet: ctt_sheet = nuke.createNode("ContactSheet", "sheet_%s" %(sht_index + 1)) print (">>> create ctt_sheet",ctt_sheet.name()) nodes_for_backdrop.append(ctt_sheet) ctt_sheet["rows"].setValue(5) ctt_sheet["columns"].setValue(6) ctt_sheet["width"].setValue(5 * 1920) ctt_sheet["height"].setValue(6 * 1080) ctt_sheet.setSelected(False) for index, e_shot in enumerate(e_sht_list): exr_path = version_paths[e_shot][0] if exr_path == None: exr_r_node = nuke.createNode("Constant") else: exr_r_node = nuke.createNode("Read", "name %s file %s" % (e_shot, exr_path)) nodes_for_backdrop.append(exr_r_node) r_node_xpos = last_batch_rnode_pos + index * 100 exr_r_node["xpos"].setValue(r_node_xpos) exr_r_node["ypos"].setValue(0) exr_r_node['first'].setValue(first_frame) exr_r_node['last'].setValue(last_frame) ctt_sheet.setInput(index, exr_r_node) exr_r_node.setSelected(False) # store first node xpos to place ctt_sheet_xpos if index == 0: first_rnode_xpos = r_node_xpos # fix ctt_sheet_xpos ctt_sheet_xpos = (len(e_sht_list) * 100) / 2 + first_rnode_xpos ctt_sheet["xpos"].setValue(ctt_sheet_xpos) ctt_sheet["ypos"].setValue(300) last_batch_rnode_pos = r_node_xpos + 200 self.create_back_drop(nodes_for_backdrop, ctt_sheet) def create_back_drop(self, nodes, ctt_sheet): """ make sure nodes were selected before execution this function """ # select nodes for e_node in nodes: e_node.setSelected(True) node = nukescripts.autoBackdrop() node["label"].setValue(ctt_sheet.name()) # de-select nodes for e_node in nodes: e_node.setSelected(False)
注:version_paths字典格式为{'实体名':[exr路径, hud路径]},部分路径可能为None。
解决方案
修改思路
- 为每个实体创建EXR节点、HUD节点,通过
Merge节点(默认Over模式)叠加后再连接到Contact Sheet - 对每组节点(EXR、HUD、Merge)做纵向分层布局,横向保持统一偏移,避免节点重叠
- 处理空路径情况:HUD路径为空时,直接将EXR节点作为Merge的输入,保证流程正常运行
修改后的完整代码
def create_contact_sheet(self, entities, version_paths, first_frame, last_frame): movie_per_sheet = 30 print("no of movies per sheet:", movie_per_sheet) shot_list_splits = [entities[i:i + movie_per_sheet] for i in range(0, len(entities), movie_per_sheet)] print("no of required sheets:", len(shot_list_splits)) print("entities", entities) print("shot version split", version_paths) last_batch_rnode_pos = 0 first_rnode_xpos = None group_xpos = None print('version map', version_paths) for sht_index, e_sht_list in enumerate(shot_list_splits): nodes_for_backdrop = [] # 创建Contact Sheet节点 ctt_sheet = nuke.createNode("ContactSheet", f"sheet_{sht_index + 1}") print(f">>> create ctt_sheet {ctt_sheet.name()}") nodes_for_backdrop.append(ctt_sheet) ctt_sheet["rows"].setValue(5) ctt_sheet["columns"].setValue(6) ctt_sheet["width"].setValue(5 * 1920) ctt_sheet["height"].setValue(6 * 1080) ctt_sheet.setSelected(False) for index, e_shot in enumerate(e_sht_list): # 计算当前节点组的横向位置(扩大偏移量避免拥挤) group_xpos = last_batch_rnode_pos + index * 150 # 1. 创建EXR节点 exr_path = version_paths[e_shot][0] if exr_path is None: exr_r_node = nuke.createNode("Constant", f"name {e_shot}_EXR") else: exr_r_node = nuke.createNode("Read", f"name {e_shot}_EXR file {exr_path}") exr_r_node["xpos"].setValue(group_xpos) exr_r_node["ypos"].setValue(0) exr_r_node['first'].setValue(first_frame) exr_r_node['last'].setValue(last_frame) exr_r_node.setSelected(False) nodes_for_backdrop.append(exr_r_node) # 2. 创建HUD节点 hud_path = version_paths[e_shot][1] hud_r_node = None if hud_path is not None: hud_r_node = nuke.createNode("Read", f"name {e_shot}_HUD file {hud_path}") hud_r_node["xpos"].setValue(group_xpos) hud_r_node["ypos"].setValue(100) hud_r_node['first'].setValue(first_frame) hud_r_node['last'].setValue(last_frame) hud_r_node.setSelected(False) nodes_for_backdrop.append(hud_r_node) # 3. 创建Merge节点并连接 merge_node = nuke.createNode("Merge", f"name {e_shot}_Merge") merge_node["xpos"].setValue(group_xpos) merge_node["ypos"].setValue(200) merge_node.setSelected(False) nodes_for_backdrop.append(merge_node) # 连接Merge节点:A接EXR,B接HUD(HUD存在时) merge_node.setInput(0, exr_r_node) if hud_r_node is not None: merge_node.setInput(1, hud_r_node) # 将Merge节点连接到Contact Sheet的对应输入口 ctt_sheet.setInput(index, merge_node) # 记录第一个节点组的位置,用于定位Contact Sheet if index == 0: first_rnode_xpos = group_xpos # 调整Contact Sheet的位置,使其居中于节点组上方 ctt_sheet_xpos = (len(e_sht_list) * 150) / 2 + first_rnode_xpos ctt_sheet["xpos"].setValue(ctt_sheet_xpos) ctt_sheet["ypos"].setValue(350) # 下移一点避免和Merge重叠 # 更新下一批节点的起始位置 last_batch_rnode_pos = group_xpos + 200 # 创建背景板 self.create_back_drop(nodes_for_backdrop, ctt_sheet) def create_back_drop(self, nodes, ctt_sheet): """ 为节点创建背景板,需确保节点被选中 """ # 选中所有节点 for e_node in nodes: e_node.setSelected(True) node = nukescripts.autoBackdrop() node["label"].setValue(ctt_sheet.name()) # 取消选中 for e_node in nodes: e_node.setSelected(False)
关键修改说明
- 节点布局:每个实体的节点组横向偏移
index * 150(比原100大,避免拥挤),纵向分层:EXR在y=0,HUD在y=100,Merge在y=200,结构清晰不重叠 - 连接逻辑:Merge节点的A输入固定接EXR,B输入接HUD(路径非空时),最终将Merge输出连到Contact Sheet的对应输入口,替换原有的EXR直接连接
- 空路径处理:HUD路径为空时跳过HUD节点创建,Merge仅保留EXR输入,保证流程不中断
- Contact Sheet定位:根据节点组总宽度重新计算居中位置,并下移y坐标避免和Merge节点重叠
- 背景板范围:将HUD、Merge节点加入
nodes_for_backdrop列表,确保所有相关节点都被包含在背景板内
内容的提问来源于stack exchange,提问作者Kartikey
相关产品推荐
相关产品推荐

