Initial Commit
This commit is contained in:
+111
@@ -0,0 +1,111 @@
|
||||
"""
|
||||
nuke_runner.py — Launch Nuke in terminal mode and stream its output.
|
||||
|
||||
Nuke is started ONCE with:
|
||||
Nuke.exe -t process.py jobs.json
|
||||
|
||||
stdout/stderr are merged and forwarded line-by-line via Qt signals so the
|
||||
GUI log window updates in real time. Progress is inferred from the
|
||||
structured output lines that process.py prints.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
import subprocess
|
||||
|
||||
from PySide6.QtCore import QThread, Signal
|
||||
|
||||
# Lines emitted by process.py that carry progress information:
|
||||
# [3/10] Rendering: UNG_101_012_010_BG01_TT_v001
|
||||
# [3/10] Done: UNG_101_012_010_BG01_TT_v001
|
||||
_RENDERING_RE = re.compile(r"^\[(\d+)/(\d+)\]\s+Rendering:\s+(.+)$")
|
||||
_DONE_RE = re.compile(r"^\[(\d+)/(\d+)\]\s+Done:\s+(.+)$")
|
||||
|
||||
|
||||
class NukeRunner(QThread):
|
||||
"""Runs a single Nuke process and emits its output as Qt signals.
|
||||
|
||||
Signals
|
||||
-------
|
||||
log_message(str)
|
||||
Each non-empty output line from Nuke.
|
||||
progress_update(int, int)
|
||||
(jobs_done, jobs_total) whenever a job completes.
|
||||
job_started(str)
|
||||
basename of the job Nuke has just started rendering.
|
||||
job_done(str)
|
||||
basename of the job Nuke has just finished.
|
||||
finished_signal(bool)
|
||||
True if Nuke exited with code 0, False otherwise.
|
||||
"""
|
||||
|
||||
log_message: Signal = Signal(str)
|
||||
progress_update: Signal = Signal(int, int)
|
||||
job_started: Signal = Signal(str)
|
||||
job_done: Signal = Signal(str)
|
||||
finished_signal: Signal = Signal(bool)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
nuke_exe: str,
|
||||
process_script: str,
|
||||
jobs_file: str,
|
||||
total: int = 0,
|
||||
parent=None,
|
||||
) -> None:
|
||||
super().__init__(parent)
|
||||
self.nuke_exe = nuke_exe
|
||||
self.process_script = process_script
|
||||
self.jobs_file = jobs_file
|
||||
self.total = total
|
||||
|
||||
def set_total(self, total: int) -> None:
|
||||
self.total = total
|
||||
|
||||
# ── Thread entry point ────────────────────────────────────────────
|
||||
|
||||
def run(self) -> None:
|
||||
cmd = [self.nuke_exe, "--indie", "-t", self.process_script, self.jobs_file]
|
||||
self.log_message.emit("Launching: " + " ".join(cmd))
|
||||
|
||||
try:
|
||||
proc = subprocess.Popen(
|
||||
cmd,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
text=True,
|
||||
bufsize=1,
|
||||
)
|
||||
except FileNotFoundError:
|
||||
self.log_message.emit(
|
||||
f"ERROR: Nuke executable not found: {self.nuke_exe}"
|
||||
)
|
||||
self.finished_signal.emit(False)
|
||||
return
|
||||
except OSError as exc:
|
||||
self.log_message.emit(f"ERROR: Could not launch Nuke: {exc}")
|
||||
self.finished_signal.emit(False)
|
||||
return
|
||||
|
||||
for raw_line in proc.stdout:
|
||||
line = raw_line.rstrip()
|
||||
if not line:
|
||||
continue
|
||||
|
||||
self.log_message.emit(line)
|
||||
|
||||
m = _DONE_RE.match(line)
|
||||
if m:
|
||||
current, total, basename = int(m.group(1)), int(m.group(2)), m.group(3)
|
||||
self.total = total
|
||||
self.progress_update.emit(current, total)
|
||||
self.job_done.emit(basename.strip())
|
||||
continue
|
||||
|
||||
m = _RENDERING_RE.match(line)
|
||||
if m:
|
||||
basename = m.group(3)
|
||||
self.job_started.emit(basename.strip())
|
||||
|
||||
proc.wait()
|
||||
self.finished_signal.emit(proc.returncode == 0)
|
||||
Reference in New Issue
Block a user