How to build an automatic Write node

Written by Tim BOWMAN on .

Generally, most of the information needed for the output of a Nuke script is contained in it's name. We can reduce manual name-changing on behalf of comp artists and simultaneously increase consistency by creating a Write node that creates (and updates) it's own output path by parsing the script's name. I call a Write node that's customized in this way an AutoWrite node. We're only adding a little functionality to a Write node, so instead of wrapping it in a gizmo, I find it simpler to create a Python script. This is how I set it up.

First, we drop the Write node that will become our AutoWrite.

w = nuke.createNode('Write', inpanel=True)

Then, we look for AutoWrites that we've previously made (if any) so that our new one can have a unique name.

count = 1
while nuke.exists('AutoWrite' + str(count)):
    count += 1
w.knob('name').setValue('AutoWrite' + str(count))

Most projects at most facilities will have a standard set of path fragments. My list includes: project root, sequence name (or abbreviation), shot name. Feel free to add your own. We will create a new tab on the Write node to help us isolate each fragment so we can easily re-assemble them for the output path. This tab will contain knobs for each path fragment.

t = nuke.Tab_Knob("Path Fragments")
w.addKnob(t)
w.addKnob(nuke.EvalString_Knob('proj_root', 'Project Root', ''))
w.addKnob(nuke.EvalString_Knob('seq', 'Sequence', ''))
w.addKnob(nuke.EvalString_Knob('shot', 'Shot Name', ''))
w.addKnob(nuke.EvalString_Knob('script', 'Script Name', ''))

If you run this now, you'll get a tab with a bunch of empty knobs. This is usually how I start when I'm setting up a new show. I suck at TCL expressions, so I use the trial-and-error technique for chopping up my script path into the appropriate fragments. And for that technique you need feedback about what your expression is doing. For that reason, I use the AutoWrite's label to display the output of the individual fragment knobs as well as the full output path.

feedback = """
Output Path: [value file]

Project Root: [value proj_root]
Sequence: [value seq]
Shot Name: [value shot]
Script Name: [value script]
"""
w.knob('label').setValue(feedback)

The next step is to figure out what expressions you need to use in the path fragments tab to hack your useful bits out of your path. Here's what a common path loks like for me:

/Volumes/PICO/show/testproj/abc/abc123/nuke/abc123_comp_v01.nk

And these are the expressions I use to create each chunk:

Project Root: In this case, the show root directory consists of the first 5 path components, so I can just split them, limit the range and re-join.

[join [lrange [split [value root.name] / ] 0 4 ] / ]

If your facility uses environment variables, you may be able to use something nicely simple like [getenv SHOW_DIR].

Path component #6 is my sequence abbreviation, so Sequence goes like this:

[lrange [split [value root.name] / ] 5 5 ]

Repeat that idea for Shot Name:

[lrange [split [value root.name] / ] 6 6 ]

Script Name is nicely simple -- we can just take the last path component and split the extensinon off of it.

[file rootname [file tail [value root.name] ] ]

Once you've figured these out for your facility, you can put them in the knobs during creation. So now the section that created the tab should look like this:

t = nuke.Tab_Knob("Path Fragments")
w.addKnob(t)
w.addKnob(nuke.EvalString_Knob('proj_root', 'Project Root', '[join [lrange [split [value root.name] / ] 0 4 ] / ]'))
w.addKnob(nuke.EvalString_Knob('seq', 'Sequence', '[lrange [split [value root.name] / ] 5 5 ]'))
w.addKnob(nuke.EvalString_Knob('shot', 'Shot Name', '[lrange [split [value root.name] / ] 6 6 ]'))
w.addKnob(nuke.EvalString_Knob('script', 'Script Name', '[file rootname [file tail [value root.name] ] ]'))

All that's left is to re-assemble the path components in the file knob:

output_path = "[value proj_root]/[value seq]/[value shot]/comps/[value script]/[value input.width]x[value input.height]/[value script].%04d.dpx"
w.knob('file').fromScript(output_path)

And that's it. Running this script will create and customize a Write node and set it to follow along as the script's name is updated.

Here's the catch:

This script creates output paths that often don't exist on the filesystem. This will cause an error from Nuke when you start a render. The solution to this problem is to implement the beforeRender callback (createWriteDir()) described in the Nuke documentation (pg 575 in the Nuke6.1v2 manual.)

My finished version of this script is here on Nukepedia if you just want to skip to the end.

I hope this has been helpful. Drop a comment if I need to clarify something further.

Comments   

 
# Kristijan M 2010-11-25 10:28
Interesting. Thank you for sharing.
 
 
# Diogo Girondi 2010-12-14 19:45
Worths noting that the example code mentioned on the manuals on page 575 will raise a error case the path on the Write already exists. Which will become a problem for most people that implement that exactly as it is on the manual.

The solution is to amend the code of page 575 with a try/except on the os.makedirs( osdir ) bit like:

def createWriteDir():
import nuke, os
file = nuke.filename(nuke.thisNode())
dir = os.path.dirname( file )
osdir = nuke.callbacks.filenameFilter( dir )
try:
os.makedirs( osdir )
return
except:
return

nuke.addBeforeRender( createWriteDir )
 
 
# sundar dar 2016-01-05 08:13
Hi Diogo Girondi

I NEED SOME PY SCRIPT

SUCH WHEN I AM CLICK READNODE I WANT CREATE BACKDROP WHATEVER IN READ NODE SHOT NO & FRAMERANGE IN BACKDROP
 
 
# Tim BOWMAN 2010-12-15 05:50
Thanks Diogo -- you're exactly right. Good idea putting the filenameFilter in there, too.
 
 
# David Johnson 2011-02-21 06:00
Thanks Tim, thanks Diogo. Now that I'm learning python its nice to have some practical and useful examples to start with.
 
 
# Eva Snyder 2011-07-03 19:44
Ah, Thanks Tim and Diogo! This is Sooo helpful. (Diogo's fix for the createWriteDir is a much needed add to the manual). Also, this needs the slight update of the before render info being on p. 595 of the Nuke 6.2 manuals.
 
 
# Patrick Clancey 2011-07-26 14:55
Hey all. My createWriteDir script doesn't seem to work on 6.3v1. Anybody had the same problem? Am I missing something?
 
 
# Ryan Quinlan 2011-12-01 18:52
How do you have it create a directory for the current view? I put %V in my path and it creates a directory called "%V". Is there a way to have it evaluate and not just see the variable as a string?
 
 
# Luke di Rago 2011-12-14 10:35
Hi Tim! First of all, thanks! I'm not quite Python-saavy at the moment, so this helped a lot. I have set up this and it's working great except for a little detail: I've done 3 AutoWrites: One for my final output in DPX, another in PNG with a slate stamp, and a third with a quicktime output from the last PNG write. The problem comes when I render in background, since Nuke creates a new Script called "24156412316574 21021421.nk", so my stamp and filenames get their names from the script name, which is not "SEQ_SHOT_v001" anymore, but the strange number. Is there any way to put a 'hold' to the Script Name variable so it doesn't get all messed up? Thanks! Luke
 
 
# Tim BOWMAN 2011-12-15 05:04
Hi Luke -- that's an interesting problem. You could "bake" the file path by calling evaluate() on your write nodes' file knobs and then set the value, but then you'd need some way to get back to your original expression-base d path. Maybe try my bgNukes background render script (https://github.com/timbowman/bgNukes) which renders in the background without saving a separate script. I think it's still working with the recent versions of Nuke, but I honestly havent tested it in a while.
 
 
# Jason Gu 2013-12-16 08:30
Hi Tim Bowman i think the Automatic Write node is powerful , but i could not download the Automatic Write node.py, could you give me a link again?
[email protected]
Cheers!
 
 
+2 # lucas van rossum 2012-03-15 10:38
hi ryan
A litle modification for multiview compatibility...

def createWriteDir( ):
file = nuke.filename(n uke.thisNode())
dir = os.path.dirname ( file )
vues=nuke.selec tedNode()['view s'].value()
viewList = vues.split(' ')
for view in viewList:
osdir = nuke.callbacks. filenameFilter( dir.replace("%V ",view) )
print dir.replace("%V ",view)
try:
os.makedirs( osdir )
pass
except:
pass

nuke.addBeforeR ender( createWriteDir )
 
 
-1 # Varun Hadkar 2012-06-01 11:54
This is just awesome! Thanks Tim and Dg!
 
 
# giancarlo derchie 2017-10-19 01:56
我是一名刚入行的TD,您的代码 我写了好几次都没成功,不知道是 否有具体的步骤?
 

You have no rights to post comments

We have 3138 guests and 113 members online