Last active
July 27, 2025 04:58
-
-
Save herronelou/1397f1ca00c1bb2263cb75c8977fac37 to your computer and use it in GitHub Desktop.
Nuke Workspace from File
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import nuke | |
import os | |
def parse_nuke_layout(filepath): | |
""" | |
Parses the XML window layout from the header of a Nuke (.nk) script file. | |
Args: | |
filepath (str): The full path to the .nk script file. | |
Returns: | |
str or None: The XML layout string if found, otherwise None. | |
""" | |
if not os.path.exists(filepath): | |
nuke.tprint(f"Error: File not found at {filepath}") | |
return None | |
try: | |
with open(filepath, 'r', encoding='utf-8') as f: | |
xml_lines = [] | |
in_layout_block = False | |
# Search for the start of the layout block within the first 10 lines | |
for i, line in enumerate(f): | |
if i >= 10 and not in_layout_block: | |
# Abort if the layout hasn't started by line 10 | |
nuke.tprint("No 'define_window_layout_xml' found in the first 10 lines.") | |
return None | |
stripped_line = line.strip() | |
if stripped_line.startswith('define_window_layout_xml'): | |
in_layout_block = True | |
# Extract the XML declaration part from the first line | |
# It starts with '<?xml ...' | |
xml_start_index = stripped_line.find('<?xml') | |
if xml_start_index != -1: | |
xml_lines.append(stripped_line[xml_start_index:]) | |
continue | |
if in_layout_block: | |
# The block ends with a single '}' on a line | |
if stripped_line == '}': | |
# We have found the end of the block | |
full_xml = "\n".join(xml_lines) | |
# The last line in the XML block is '</layout>', so we remove the final '}' | |
# which is part of the nuke script syntax, not the XML. | |
return full_xml.strip() | |
xml_lines.append(line) | |
# If the loop finishes and we were in a block, the file is likely malformed | |
if in_layout_block: | |
nuke.tprint("Warning: Reached end of file but layout block was not properly closed.") | |
return None | |
except Exception as e: | |
nuke.tprint(f"An error occurred while reading the file: {e}") | |
return None | |
return None | |
def apply_and_save_script_layout(): | |
""" | |
This function is intended to be used as a Nuke onScriptLoad callback. | |
It parses the layout from the opened script, saves it to a temporary | |
workspace file, and applies it using a QTimer to avoid instability. | |
""" | |
# Check if we are in a GUI session | |
if not nuke.GUI: | |
return | |
# Import UI-specific modules safely | |
try: | |
import hiero.ui | |
from PySide2.QtCore import QTimer | |
except ImportError: | |
nuke.tprint("Could not import hiero.ui or PySide2.QtCore. Cannot set workspace.") | |
return | |
# Get the path of the currently opened script | |
script_path = nuke.root().name() | |
if script_path == 'Root' or not script_path: | |
# This can happen on script launch before a file is opened. | |
return | |
nuke.tprint(f"Checking for layout in: {script_path}") | |
# Parse the layout from the script | |
layout_xml = parse_nuke_layout(script_path) | |
if layout_xml: | |
def deferred_set_workspace(): | |
""" | |
This function contains the logic that will be deferred. | |
It writes the layout to a file and then applies it. | |
""" | |
try: | |
# Define the workspace name and path | |
workspace_name = "_script_layout" | |
nuke_dir = os.path.expanduser("~/.nuke") | |
workspace_dir = os.path.join(nuke_dir, "Workspaces", "Nuke") | |
# Ensure the target directory exists | |
if not os.path.exists(workspace_dir): | |
os.makedirs(workspace_dir) | |
nuke.tprint(f"Created directory: {workspace_dir}") | |
workspace_filepath = os.path.join(workspace_dir, f"{workspace_name}.xml") | |
# Write the XML content to the workspace file | |
with open(workspace_filepath, 'w', encoding='utf-8') as f: | |
f.write(layout_xml) | |
nuke.tprint(f"Saved script layout to: {workspace_filepath}") | |
# Apply the workspace layout | |
hiero.ui.setWorkspace(workspace_name) | |
nuke.tprint(f"Applied workspace: '{workspace_name}'") | |
except Exception as e: | |
nuke.tprint(f"Error saving or applying workspace: {e}") | |
# Use QTimer.singleShot to delay the execution. | |
# A delay of 0ms pushes the call to the end of the Qt event queue, | |
# ensuring the Nuke UI is stable before we try to modify it. | |
QTimer.singleShot(10, deferred_set_workspace) | |
else: | |
nuke.tprint("No layout found in script or an error occurred during parsing.") | |
# --- HOW TO USE --- | |
# | |
# 1. Save this script as a Python file (e.g., 'layout_manager.py') in your .nuke folder. | |
# 2. In your 'menu.py' file, add the following lines: | |
# | |
# import layout_manager | |
# nuke.addOnScriptLoad(layout_manager.apply_and_save_script_layout) | |
# | |
# 3. Restart Nuke. Now, whenever you open a .nk script that contains a layout | |
# definition in its header, that layout will be automatically applied. | |
# This will only work once it created the first XML and you've restarted nuke, as it seems to not let you load a layout by name if it's not in the menu already. | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment