Skip to content

Instantly share code, notes, and snippets.

@jedypod
Last active August 28, 2018 01:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jedypod/2258780590638b878e5e to your computer and use it in GitHub Desktop.
Save jedypod/2258780590638b878e5e to your computer and use it in GitHub Desktop.
Render Nuke jobs in the terminal on OSX and Linux
from __future__ import with_statement
import os
import sys
import re
import glob
import subprocess
import nuke
import nukescripts
import afnuke
import aftractor.jobs
## Add this to your menu.py if you want to replace the default Render Selected entry:
# import terminal_render
# nuke.menu("Nuke").findItem("Render").findItem("Render Selected Write Nodes...").setScript('terminal_render.render(nuke.selectedNodes())')
## Determine Platform Type
from sys import platform as __platform
if __platform == "linux" or __platform == "linux2":
_platform = 'linux'
elif __platform == "darwin":
_platform = 'osx'
elif __platform == "win32":
_platform = 'win'
if nuke.GUI:
# Dialog state for local render
_dstate = nukescripts.DialogState()
def terminal_render(write_nodes, frameranges, views, instances, threads, mem):
if _platform == "win":
nuke.message("Windows machines not supported.")
return
nuke.scriptSave()
script_path = nuke.root().knob("name").value()
write_node_names = ','.join([n.fullName() for n in write_nodes])
for framerange in frameranges:
first_frame = framerange.minFrame()
last_frame = framerange.maxFrame()
num_frames = last_frame + 1 - first_frame
chunk_size = int(num_frames / instances)
for i in range(0, instances):
render_start = first_frame + i * chunk_size
render_end = first_frame + ((i+1) * chunk_size)
if i > 0:
render_start += 1
nk_cmd = '{0} -X {1} -c {2} -f -m {3} --view {4} -F {5}-{6} {7}'.format(
nuke.EXE_PATH, write_node_names, mem, threads, ','.join(map(str, views)),
str(render_start), str(render_end), script_path)
print nk_cmd
if _platform == "osx":
cmd = '''osascript 2>/dev/null <<EOF
tell application "Terminal"
if not (exists window 1) then reopen
activate
do script "{0}"
end tell
EOF'''.format(nk_cmd)
elif _platform == "linux":
#cmd = 'xterm -e "bash {0}; bash"'.format(nk_cmd)
cmd = 'gnome-terminal -e "bash -c \\"{0}; exec bash\\""'.format(nk_cmd)
subprocess.Popen(cmd, shell=True)
def tractor_render(script_path, writes, first_frame, last_frame):
first_frame = str(first_frame)
last_frame = str(last_frame)
args = ['--chunk', '6',
'--copy', '1',
'--nodes', write,
'--start', first_frame,
'--end', last_frame,
'--file', script_path,
'--publish_auto',
'--publish_variation', 'main',
'--publish_step', 'comp',
'--publish_status', 'rev',
'--publish_comment', 'bfd of latest comps',
'--publish_layers', 'bfd_comps',
'--publish_extensions', 'exr',
'--publish_greatest', '1',
]
job = aftractor.jobs.NukeRender()
job.parseArgs(args)
job.launch()
def render():
nuke_script = nuke.root().name()
for write in writes:
framerange = write.frameRange()
write_name = write.name()
first_frame = str(framerange.first())
last_frame = str(framerange.last())
print write_name, first_frame, last_frame
args = ['--chunk', chunk_size,
'--copy', '1',
'--nodes', write_name,
'--start', first_frame,
'--end', last_frame,
'--file', nuke_script,
]
print args
job = aftractor.jobs.NukeRender()
job.parseArgs(args)
job.launch()
# Expanded from nukescripts.renderdialog
class RenderDialog(nukescripts.ExecuteDialog):
def _titleString(self):
return "Render"
def _idString(self):
return "uk.co.thefoundry.RenderDialog"
def __init__(self, dialogState, groupContext, nodeSelection = [], exceptOnError = True):
nukescripts.ExecuteDialog.__init__(self, dialogState, groupContext, nodeSelection, exceptOnError)
def _addPostKnobs(self):
# Background render stuff
self._bgRender = nuke.Boolean_Knob("bg_render", "Render in background")
self._state.setKnob(self._bgRender, False)
self._bgRender.setFlag(nuke.STARTLINE)
self.addKnob(self._bgRender)
# Terminal Render
self._termRender = nuke.Boolean_Knob("terminal_render", "Render in Terminal")
self._state.setKnob(self._termRender, False)
self._termRender.setFlag(nuke.STARTLINE)
self.addKnob(self._termRender)
if self._bgRender.value() or self._termRender.value():
self.bg_render_knobs = True
else:
self.bg_render_knobs = False
self._numInstances = nuke.Int_Knob("num_instances", "Instance Number")
self._numInstances.setVisible(self.bg_render_knobs)
self._state.setKnob(self._numInstances, 1)
self.addKnob(self._numInstances)
self._numThreads = nuke.Int_Knob("num_threads", "Thread limit")
self._numThreads.setVisible(self.bg_render_knobs)
self._state.setKnob(self._numThreads, max(nuke.NUM_CPUS / 2, 1))
self.addKnob(self._numThreads)
self._maxMem = nuke.String_Knob("max_memory", "Memory limit")
self._state.setKnob(self._maxMem, str(max(nuke.memory("max_usage") / 2097152, 16)) + "M")
self._maxMem.setVisible(self.bg_render_knobs)
self.addKnob(self._maxMem)
# Tractor Render
self._tractorRender = nuke.Boolean_Knob("tractor_render", "Render on Tractor")
self._state.setKnob(self._tractorRender, False)
self._tractorRender.setFlag(nuke.STARTLINE)
self.addKnob(self._tractorRender)
self._chunkSize = nuke.Int_Knob("chunk_size", "Chunk Size")
self._chunkSize.setVisible(False)
self._state.setKnob(self._chunkSize, 4)
self.addKnob(self._chunkSize)
def _getBackgroundLimits(self):
return {
"maxThreads": self._numThreads.value(),
"maxCache": self._maxMem.value() }
def knobChanged(self, knob):
nukescripts.ExecuteDialog.knobChanged(self, knob)
if knob == self._bgRender:
self._termRender.setValue(False)
self._tractorRender.setValue(False)
self._numThreads.setVisible(self._bgRender.value() or self._termRender.value())
self._maxMem.setVisible(self._bgRender.value() or self._termRender.value())
if knob == self._termRender:
self._bgRender.setValue(False)
self._tractorRender.setValue(False)
self._numThreads.setVisible(self._bgRender.value() or self._termRender.value())
self._maxMem.setVisible(self._bgRender.value() or self._termRender.value())
self._numInstances.setVisible(self._termRender.value())
if knob == self._tractorRender:
self._bgRender.setValue(False)
self._termRender.setValue(False)
self._chunkSize.setVisible(self._tractorRender.value())
def isBackgrounded(self):
"""Return whether the background rendering option is enabled."""
return self._bgRender.value()
def run(self):
frame_ranges = nuke.FrameRanges(self._frameRange.value().split(','))
views = self._selectedViews()
rootProxyMode = nuke.root().proxy()
try:
nuke.Undo().disable()
nuke.root().setProxy(self._useProxy.value())
if (self.isBackgrounded()):
nuke.executeBackgroundNuke(nuke.EXE_PATH, self._nodeSelection, frame_ranges, views, self._getBackgroundLimits(), continueOnError = self._continueOnError.value())
elif self._termRender.value():
terminal_render(self._nodeSelection, frame_ranges, views, self._numInstances.value(), self._numThreads.value(), self._maxMem.value())
elif self._tractorRender.value():
tractor_render(nuke.root().knob("name").value(), self._nodeSelection, framerange, views, self.chunk_size)
else:
nuke.executeMultiple(self._nodeSelection, frame_ranges, views, continueOnError = self._continueOnError.value())
except RuntimeError, e:
if self._exceptOnError or e.args[0][0:9] != "Cancelled": # TO DO: change this to an exception type
raise
finally:
nuke.root().setProxy(rootProxyMode)
nuke.Undo().enable()
def render(write_nodes):
# Render only Write nodes and Groups with a Write node inside
approved_writes = []
for write_node in write_nodes:
if write_node.Class() == 'Group':
if 'Write' in [n.Class() for n in write_node.nodes()]:
approved_writes.append(write_node)
if write_node.Class() == 'Write':
approved_writes.append(write_node)
if approved_writes:
d = RenderDialog(_dstate, nuke.root(), approved_writes, False)
if d.showModalDialog() == True:
nuke.scriptSave()
d.run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment