1
2 from __future__ import with_statement
3 import nuke
4 import nukescripts
5 import flipbooking
6 from nukescripts import pyAppUtils
7 import os
8 import string
9 import subprocess
10 import uuid
11 import re
12
13
14
15
16
17
21
22 - def get(self, knob, defaultValue = None):
23 """Return the given knob's stored last state value.
24 If none exists, defaultValue is returned.
25 Values are stored in a dict referenced by knob name, so names must be unique!"""
26 return self.getValue(knob.name(), defaultValue)
27
28 - def save(self, knob):
29 """Store the knob's current value as the 'last state' for the next time the dialog is opened.
30 Values are stored in a dict referenced by knob name, so names must be unique!"""
31 self.saveValue(knob.name(), knob.value())
32
33 - def setKnob(self, knob, defaultValue = None):
37 """Stores the value with the given id."""
38 self._state[id] = value
39 - def getValue(self, id, defaultValue = None):
40 """Recalls the value. If it was not set before, it will return the defaultValue."""
41 return self._state.get(id, defaultValue)
42
43 _gRenderDialogState = DialogState()
44 _gFlipbookDialogState = DialogState()
45
49
51 return "uk.co.thefoundry.ExecuteDialog"
52
54 """Add knobs that must appear before the render knobs."""
55 return
56
57 - def _addPostKnobs(self):
58 """Add knobs that must appear after the render knobs."""
59 return
60
65
74
79
80 - def __init__(self, dialogState, groupContext, nodeSelection = [], exceptOnError = True):
81 self._state = dialogState
82 self._nodeSelection = nodeSelection
83 self._exceptOnError = exceptOnError
84
85 nukescripts.PythonPanel.__init__(self, self._titleString(), self._idString(), False)
86
87 self._viewers = {}
88 for n in nuke.allNodes("Viewer", groupContext):
89 self._viewers[n.name()] = n
90 self._specialRanges = ["input", "global", "custom"]
91
92 self._addPreKnobs()
93
94
95 self._rangeEnum = nuke.Enumeration_Knob( "frame_range", "Frame range", self._specialRanges + self._viewers.keys() )
96 self._state.setKnob(self._rangeEnum, "input")
97 self.addKnob( self._rangeEnum )
98 self._frameRange = nuke.String_Knob( "frame_range_string", "")
99 self._frameRange.clearFlag(nuke.STARTLINE)
100 if self._rangeEnum.value() == "custom":
101 self._state.setKnob(self._frameRange, str(nuke.root().frameRange()))
102 else:
103 self._setFrameRangeFromSource(self._rangeEnum.value())
104
105 self.addKnob(self._frameRange)
106
107
108 self._useProxy = nuke.Boolean_Knob("use_proxy", "Use proxy")
109 self._useProxy.setFlag(nuke.STARTLINE)
110 self._state.setKnob(self._useProxy, nuke.root().proxy())
111 self.addKnob(self._useProxy)
112
113 self._addPostKnobs()
114
115 self._continueOnError = nuke.Boolean_Knob("continue", "Continue on error")
116 self._state.setKnob(self._continueOnError, True)
117 self._continueOnError.setFlag(nuke.STARTLINE)
118 self.addKnob(self._continueOnError)
119
120 self._addViewKnob()
121
123 self._state.save(knob)
124 if (knob == self._frameRange):
125 self._rangeEnum.setValue("custom")
126 self._state.save(self._rangeEnum)
127 if (knob == self._rangeEnum):
128 self._setFrameRangeFromSource(knob.value())
129 self._state.save(self._frameRange)
130
144
146 """"Set the framerange knob to have the framerange from the given viewer."""
147 viewerRange = str(self._viewers[viewer].knob("frame_range").value())
148 if viewerRange == "":
149 viewerRange = str(self._viewers[viewer].playbackRange())
150 self._frameRange.setValue(viewerRange)
151
158
161
176
180
182 return "uk.co.thefoundry.RenderDialog"
183
184 - def __init__(self, dialogState, groupContext, nodeSelection = [], exceptOnError = True):
186
187 - def _addPostKnobs(self):
188
189 self._bgRender = nuke.Boolean_Knob("bg_render", "Render in background")
190 self._state.setKnob(self._bgRender, False)
191 self._bgRender.setFlag(nuke.STARTLINE)
192 self.addKnob(self._bgRender)
193 self._numThreads = nuke.Int_Knob("num_threads", "Thread limit")
194 self._numThreads.setVisible(self._bgRender.value())
195 self._state.setKnob(self._numThreads, max(nuke.NUM_CPUS / 2, 1))
196 self.addKnob(self._numThreads)
197 self._maxMem = nuke.String_Knob("max_memory", "Memory limit")
198 self._state.setKnob(self._maxMem, str(max(nuke.memory("max_usage") / 2097152, 16)) + "M")
199 self._maxMem.setVisible(self._bgRender.value())
200 self.addKnob(self._maxMem)
201
202
204 return {
205 "maxThreads": self._numThreads.value(),
206 "maxCache": self._maxMem.value() }
207
214
216 """Return whether the backgroun rendering option is enabled."""
217 return self._bgRender.value()
218
220 frame_ranges = nuke.FrameRanges(self._frameRange.value().split(','))
221 views = self._selectedViews()
222 rootProxyMode = nuke.root().proxy()
223 try:
224 nuke.Undo().disable()
225 nuke.root().setProxy(self._useProxy.value())
226 if (self.isBackgrounded()):
227 nuke.executeBackgroundNuke(nuke.EXE_PATH, self._nodeSelection,
228 frame_ranges, views, self._getBackgroundLimits(), continueOnError = self._continueOnError.value())
229 else:
230 nuke.executeMultiple(self._nodeSelection, frame_ranges, views, continueOnError = self._continueOnError.value())
231 except RuntimeError, e:
232 if self._exceptOnError or e.args[0][0:9] != "Cancelled":
233 raise
234 finally:
235 nuke.root().setProxy(rootProxyMode)
236 nuke.Undo().enable()
237
241
243 return "uk.co.thefoundry.FlipbookDialog"
244
245 - def __init__(self, dialogState, groupContext, node, takeNodeSettings):
246
247 self._node = node
248 self._takeNodeSettings = takeNodeSettings
249
250
251 RenderDialog.__init__(self, dialogState, groupContext)
252
253
254 self._state.setKnob(self._rangeEnum, "input")
255 self._setFrameRangeFromSource(self._rangeEnum.value())
256
257 if self._takeNodeSettings:
258 self._viewerForSettings.setValue(node.name())
259 self.knobChanged(self._viewerForSettings)
260
262 self._flipbookEnum = nuke.Enumeration_Knob( "flipbook", "Flipbook", flipbooking.gFlipbookFactory.getNames() )
263 self._state.setKnob(self._flipbookEnum, "FrameCycler")
264 self.addKnob( self._flipbookEnum )
265 self._viewerForSettings = nuke.Enumeration_Knob("viewer_settings", "Take settings from", ["-"] + self._viewers.keys())
266 if not self._takeNodeSettings:
267 self._viewerForSettings.setValue("-")
268 self.addKnob(self._viewerForSettings)
269
270 self._defaultValues = nuke.PyScript_Knob("default", "Defaults")
271 self.addKnob(self._defaultValues)
272
273
274 self._useRoi = nuke.Boolean_Knob("use_roi", "Enable ROI")
275 self._useRoi.setFlag(nuke.STARTLINE)
276 self._state.setKnob(self._useRoi, False)
277 self.addKnob(self._useRoi)
278 self._roi = nuke.BBox_Knob("roi", "Region of Interest")
279 self._state.setKnob(self._roi, (0, 0, 0, 0))
280 self.addKnob(self._roi)
281 self._roi.setVisible(self._useRoi.value())
282
283
284 self._channels = nuke.Channel_Knob( "channels_knob", "Channels")
285 if self._node.Class() == "Write":
286 self._channels.setValue(self._node.knob("channels").value())
287 else:
288 self._state.setKnob(self._channels, "rgba")
289 self._channels.setFlag(nuke.STARTLINE | nuke.NO_CHECKMARKS)
290 self.addKnob( self._channels )
291
292 - def _addPostKnobs( self ):
293 super(FlipbookDialog, self)._addPostKnobs()
294
295 self._cleanup = nuke.Boolean_Knob("cleanup", "Delete existing temporary files")
296 self._cleanup.setFlag(nuke.STARTLINE)
297 self._state.setKnob(self._cleanup, True)
298 self.addKnob(self._cleanup)
299
300
301 self._luts = nuke.Enumeration_Knob("lut", "LUT", nuke.ViewerProcess.registeredNames())
302 if self._takeNodeSettings:
303 self._state.setKnob(self._luts, self._lutFromViewer(self._viewerForSettings.value()))
304 else:
305 self._state.setKnob(self._luts, self._lutFromViewer())
306 self.addKnob(self._luts)
307
308 self._burnInLUT = nuke.Boolean_Knob("burnin", "Burn in the LUT")
309 self._state.setKnob(self._burnInLUT, False)
310 self.addKnob(self._burnInLUT)
311
312
313 audioList = []
314 audioList.append( "None" )
315 for node in nuke.allNodes("AudioRead"):
316 audioList.append( node.name() )
317 self._audioSource = nuke.Enumeration_Knob( "audio", "Audio", audioList )
318 self.addKnob( self._audioSource )
319
320
323
339
342
351
353 return knob == self._useRoi or knob == self._roi or knob == self._channels or knob == self._useProxy or knob == self._frameRange or knob == self._rangeEnum or knob == self._luts
354
358
360 RenderDialog.knobChanged(self, knob)
361 if knob == self._defaultValues:
362 self._setKnobAndStore(self._useRoi, False)
363 self._setKnobAndStore(self._roi, (0, 0, 0, 0))
364 self._roi.setVisible(False)
365 self._maxMem.setVisible(False)
366 self._numThreads.setVisible(False)
367 self._setKnobAndStore(self._viewerForSettings, "-")
368 self._setKnobAndStore(self._channels, "rgba")
369 self._setKnobAndStore(self._useProxy, False)
370 self._setKnobAndStore(self._frameRange, str(nuke.root().frameRange()))
371 self._setKnobAndStore(self._rangeEnum, "input")
372 self._setKnobAndStore(self._continueOnError, True)
373 self._setKnobAndStore(self._bgRender, False)
374 self._setKnobAndStore(self._luts, "sRGB")
375 self._setKnobAndStore(self._burnInLUT, False)
376 self._setKnobAndStore(self._cleanup, True)
377 self._setKnobAndStore(self._maxMem, str(max(nuke.memory("max_usage") / 2097152, 16)) + "M")
378 self._setKnobAndStore(self._numThreads, max(nuke.NUM_CPUS / 2, 1))
379 elif (knob == self._viewerForSettings):
380 if self._viewerForSettings.value() != "-":
381 viewer = self._viewers[self._viewerForSettings.value()]
382 self._setKnobAndStore(self._useRoi, viewer.roiEnabled())
383 roi = viewer.roi()
384 if roi != None:
385 self._roi.fromDict(roi)
386 self._state.save(self._roi)
387 self._channels.fromScript(viewer.knob("channels").toScript())
388 self._state.save(self._channels)
389 self._setKnobAndStore(self._useProxy, nuke.root().proxy())
390 self._frameRangeFromViewer(viewer.name())
391 self._state.save(self._frameRange)
392 self._setKnobAndStore(self._rangeEnum, viewer.name())
393 self._roi.setVisible(self._useRoi.value())
394 self._setKnobAndStore(self._luts, self._lutFromViewer(viewer.name()))
395 elif (knob == self._useRoi):
396 self._roi.setVisible(self._useRoi.value())
397 elif self._isViewerSettingKnob(knob):
398 self._viewerForSettings.setValue("-")
399 self._state.save(self._viewerForSettings)
400 elif knob == self._luts:
401 self._burnInLUT.setEnabled(self._luts.value() != "None")
402
404 """Delete all the files in the range to be rendered."""
405 temporariesPath = self._getIntermediatePath()
406 temporariesPath = temporariesPath .replace("%V", "%s")
407 for r in nuke.FrameRanges(self._frameRange.value().split(',')):
408 deleteRange = xrange(r.minFrame(), r.maxFrame() + 1)
409
410 for v in self._selectedViews():
411 for i in deleteRange:
412 if len(self._selectedViews()) > 1:
413 f = temporariesPath % (i, v,)
414 else:
415 f = temporariesPath % i
416
417 if os.access(f, os.F_OK):
418 os.remove(f)
419
422
450
483
544
546 return self._luts.value()
547
549 nukeNode = nuke.toNode( self._audioSource.value() )
550 ret = ""
551 if nukeNode != None:
552 ret = nukeNode["file"].getEvaluatedValue()
553
554
555
556 if nuke.env['WIN32'] == False:
557 if ret != "":
558
559 if (' ' in ret) == True :
560 nuke.message("Framecycler does not currently support spaces in the audio file name, to enable audio please ensure the audio file contains no spaces.")
561 ret = ""
562 return ret
563
565 options = {
566 }
567
568 try:
569 options['pixelAspect'] = float(nuke.value(nodeToFlipbook.name()+".pixel_aspect"))
570 except:
571 pass
572
573 try:
574 f = nodeToFlipbook.format()
575 options['dimensions'] = { 'width' : f.width(), 'height' : f.height() }
576 except:
577 pass
578
579
580 if not self._burnInLUT.value():
581 inputColourspace = "linear"
582 outputColourspace = "linear"
583
584 if self._node.Class() == "Read" or self._node.Class() == "Write":
585 lut = self._node.knob("colorspace").value()
586
587 if lut[:7] == "default":
588 lut = lut[9:-1]
589 inputColourspace = lut
590
591
592 lut = self._getLUT()
593 if lut != "None":
594 outputColourspace = lut
595
596 if inputColourspace == outputColourspace:
597 options["lut"] = inputColourspace
598 else:
599 options["lut"] = inputColourspace + "-" + outputColourspace
600
601 audioTrack = self._getAudio()
602 if audioTrack != "":
603 options["audio"] = audioTrack
604
605
606 if self._useRoi.value():
607 roi = self._roi.toDict()
608 if (roi["r"] - roi["x"] > 0) and (roi["t"] - roi["y"] > 0):
609 options["roi"] = bboxToTopLeft(int(nuke.value(nodeToFlipbook.name()+".actual_format.height")), roi)
610
611 return options
612
614 flipbookToRun = flipbooking.gFlipbookFactory.getApplication(self._flipbookEnum.value())
615 if (flipbookToRun):
616 if not os.access(flipbookToRun.path(), os.X_OK):
617 raise RuntimeError("%s cannot be executed (%s)." % (flipbookToRun.name(), flipbookToRun.path(),) )
618
619 nodeToFlipbook = None
620 rootProxyMode = nuke.root().proxy()
621 try:
622
623 nuke.Undo().disable()
624 nuke.root().setProxy(self._useProxy.value())
625
626 if self._cleanup.value():
627 self._deleteTemporaries()
628 calledOnNode = self._node
629 if self._node.Class() == "Viewer":
630 self._node = self._node.input(int(self._node.knob("input_number").value()))
631
632 runFlipbook = False
633
634 if not self._requireIntermediateNode():
635 nodeToFlipbook = self._node
636 runFlipbook = True
637 else:
638 nodeToFlipbook = self._createIntermediateNode()
639 runFlipbook = not self._bgRender.value()
640
641 if nodeToFlipbook and runFlipbook:
642 filename = nuke.filename(nodeToFlipbook)
643 if filename is None or filename == "":
644 raise RuntimeError("Cannot run a flipbook on '%s', expected to find a filename and there was none." % (nodeToFlipbook.fullName(),))
645 flipbookToRun.run(filename,
646 nuke.FrameRanges(self._frameRange.value().split(',')),
647 self._selectedViews(), self._getOptions(nodeToFlipbook))
648 finally:
649 if self._node != nodeToFlipbook:
650 nuke.delete(nodeToFlipbook)
651 nuke.root().setProxy(rootProxyMode)
652 nuke.Undo().enable()
653 else:
654 raise RuntimeError("No flipbook called " + self._flipbookEnum.value() + " found. Was it deregistered while the dialog was open?")
655
657 """Present a dialog that executes the given list of nodes."""
658 groupContext = nuke.root()
659 d = ExecuteDialog(_gRenderDialogState, groupContext, nodesToExecute, exceptOnError)
660 if d.showModalDialog() == True:
661 d.run()
662
664 """Present a dialog that renders the given list of nodes."""
665 groupContext = nuke.root()
666 d = RenderDialog(_gRenderDialogState, groupContext, nodesToRender, exceptOnError)
667 if d.showModalDialog() == True:
668 d.run()
669
671 """Present a dialog that flipbooks the given node."""
672 if node is None:
673 raise RuntimeError("Can't launch flipbook, require a node.");
674 if node.Class() == "Viewer" and node.inputs() == 0:
675 raise RuntimeError("Can't launch flipbook, there is nothing connected to the viewed input.");
676
677 groupContext = nuke.root()
678
679 e = FlipbookDialog(_gFlipbookDialogState, groupContext, node, takeNodeSettings)
680 if (e.showModalDialog() == True):
681 e.run()
682
684 """Present a dialog that flipbooks the currently selected node."""
685 try:
686 showFlipbookDialog(nuke.selectedNode())
687 except ValueError, ve:
688 raise RuntimeError("Can't launch flipbook, %s." % (ve.args[0]))
689
691 """Convert the roi passed from a origin at the bottom left to the top left.
692 Also replaces the r and t keys with w and h keys.
693 @param height: the height used to determine the top.
694 @param roi: the roi with a bottom left origin, must have x, y, r & t keys.
695 @result dict with x, y, w & h keys"""
696 topLeftRoi = {
697 "x": roi["x"],
698 "y": height - roi["y"] - (roi["t"] - roi["y"]),
699 "w": roi["r"] - roi["x"],
700 "h": roi["t"] - roi["y"] }
701 return topLeftRoi
702
704 """ Set a particular option to the given value. The type of the value differs per option, giving the wrong value may result in exceptions. The options are read every time the dialog is opened, though not every knob in the dialog has it's value stored."""
705 _gRenderDialogState.saveValue(name, value)
706
708 """ Set a particular option to the given value. The type of the value differs per option, giving the wrong value may result in exceptions. The options are read every time the dialog is opened, though not every knob in the dialog has it's value stored."""
709 _gFlipbookDialogState.saveValue(name, value)
710