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 from threading import Thread
14 from nukescripts import utils, sys, os, captureViewer
15
16
17
18
19
20
21
25
26 - def get(self, knob, defaultValue = None):
27 """Return the given knob's stored last state value.
28 If none exists, defaultValue is returned.
29 Values are stored in a dict referenced by knob name, so names must be unique!"""
30 return self.getValue(knob.name(), defaultValue)
31
32 - def save(self, knob):
33 """Store the knob's current value as the 'last state' for the next time the dialog is opened.
34 Values are stored in a dict referenced by knob name, so names must be unique!"""
35 self.saveValue(knob.name(), knob.value())
36
37 - def setKnob(self, knob, defaultValue = None):
41 """Stores the value with the given id."""
42 self._state[id] = value
43 - def getValue(self, id, defaultValue = None):
44 """Recalls the value. If it was not set before, it will return the defaultValue."""
45 return self._state.get(id, defaultValue)
46
47 _gRenderDialogState = DialogState()
48 _gFlipbookDialogState = DialogState()
49 _gViewerCaptureDialogState = DialogState()
50
54
56 return "uk.co.thefoundry.ExecuteDialog"
57
59 """Add knobs that must appear before the render knobs."""
60 return
61
62 - def _addPostKnobs(self):
63 """Add knobs that must appear after the render knobs."""
64 return
65
67 """Add knobs that must appear at the very end."""
68 return
69
74
83
88
89 - def __init__(self, dialogState, groupContext, nodeSelection = [], exceptOnError = True):
90 self._state = dialogState
91 self._nodeSelection = nodeSelection
92 self._exceptOnError = exceptOnError
93
94 nukescripts.PythonPanel.__init__(self, self._titleString(), self._idString(), False)
95
96 self._viewers = {}
97 for n in nuke.allNodes("Viewer", groupContext):
98 self._viewers[n.name()] = n
99 self._specialRanges = ["input", "global", "custom"]
100
101 self._addPreKnobs()
102
103
104 self._rangeEnum = nuke.Enumeration_Knob( "frame_range", "Frame range", self._specialRanges + self._viewers.keys() )
105 self._state.setKnob(self._rangeEnum, "input")
106 self.addKnob( self._rangeEnum )
107
108 self._frameRange = nuke.String_Knob( "frame_range_string", "")
109 self._frameRange.clearFlag(nuke.STARTLINE)
110 if self._rangeEnum.value() == "custom":
111 self._state.setKnob(self._frameRange, str(nuke.root().frameRange()))
112 else:
113 self._setFrameRangeFromSource(self._rangeEnum.value())
114
115 self.addKnob(self._frameRange)
116
117
118 self._useProxy = nuke.Boolean_Knob("use_proxy", "Use proxy")
119 self._useProxy.setFlag(nuke.STARTLINE)
120 self._state.setKnob(self._useProxy, nuke.root().proxy())
121 self.addKnob(self._useProxy)
122
123 self._addPostKnobs()
124
125 self._continueOnError = nuke.Boolean_Knob("continue", "Continue on error")
126 self._state.setKnob(self._continueOnError, True)
127 self._continueOnError.setFlag(nuke.STARTLINE)
128 self.addKnob(self._continueOnError)
129
130 self._addViewKnob()
131
132 self._addTrailingKnobs()
133
142
156
158 """"Set the framerange knob to have the framerange from the given viewer."""
159 viewerRange = str(self._viewers[viewer].knob("frame_range").value())
160 if viewerRange == "":
161 viewerRange = str(self._viewers[viewer].playbackRange())
162 self._frameRange.setValue(viewerRange)
163
170
173
188
192
194 return "uk.co.thefoundry.RenderDialog"
195
196 - def __init__(self, dialogState, groupContext, nodeSelection = [], exceptOnError = True):
198
205
206 - def _addPostKnobs(self):
207
208 self._bgRender = nuke.Boolean_Knob("bg_render", "Render in background")
209 self._state.setKnob(self._bgRender, False)
210 self._bgRender.setFlag(nuke.STARTLINE)
211 self.addKnob(self._bgRender)
212 self._numThreads = nuke.Int_Knob("num_threads", "Thread limit")
213 self._numThreads.setVisible(self._bgRender.value())
214 self._state.setKnob(self._numThreads, max(nuke.NUM_CPUS / 2, 1))
215 self.addKnob(self._numThreads)
216 self._maxMem = nuke.String_Knob("max_memory", "Memory limit")
217 self._state.setKnob(self._maxMem, str(max(nuke.memory("max_usage") / 2097152, 16)) + "M")
218 self._maxMem.setVisible(self._bgRender.value())
219 self.addKnob(self._maxMem)
220
221
223 return {
224 "maxThreads": self._numThreads.value(),
225 "maxCache": self._maxMem.value() }
226
256
257
259 """Return whether the background rendering option is enabled."""
260 return self._bgRender.value()
261
263
264 if not nuke.env['studio']:
265 return False
266
267 if len(self._nodeSelection) > 1 or len(self._nodeSelection) < 1:
268 return False
269
270 write = self._nodeSelection[0]
271
272 if write == nuke.root():
273
274
275
276
277
278 writeNodes = nuke.allNodes( 'Write' )
279 writeNodes.extend( nuke.allNodes( 'WriteGeo') )
280 writeNodes.extend( nuke.allNodes( 'ParticleCache') )
281 writeNodes.extend( nuke.allNodes( 'DiskCache') )
282
283 if len(writeNodes) > 1:
284 return False
285
286 if len(writeNodes) > 0:
287 write = writeNodes[0]
288
289 timelineWriteNode = None
290
291 try:
292 from foundry.frameserver.nuke.workerapplication import GetWriteNode
293 timelineWriteNode = GetWriteNode()
294 except:
295 pass
296
297 if not timelineWriteNode:
298 return False
299
300 if timelineWriteNode.name() != write.name():
301 return False
302
303
304 try:
305 from hiero.ui import isInAnyProject
306 return isInAnyProject( nuke.scriptName() )
307 except:
308 pass
309
310 return False
311
313 if self.isTimelineWrite() and self._frameServerRender.value():
314 from hiero.ui.nuke_bridge.nukestudio import scriptSaveAndReRender
315 scriptSaveAndReRender()
316 return
317
318 frame_ranges = nuke.FrameRanges(self._frameRange.value().split(','))
319 views = self._selectedViews()
320 rootProxyMode = nuke.root().proxy()
321 try:
322 nuke.Undo().disable()
323 nuke.root().setProxy(self._useProxy.value())
324 if (self.isBackgrounded()):
325 nuke.executeBackgroundNuke(nuke.EXE_PATH, self._nodeSelection,
326 frame_ranges, views, self._getBackgroundLimits(), continueOnError = self._continueOnError.value())
327 else:
328 nuke.executeMultiple(self._nodeSelection, frame_ranges, views, continueOnError = self._continueOnError.value())
329 except RuntimeError, e:
330 if self._exceptOnError or e.args[0][0:9] != "Cancelled":
331 raise
332 finally:
333 nuke.root().setProxy(rootProxyMode)
334 nuke.Undo().enable()
335
339
341 return "uk.co.thefoundry.FlipbookDialog"
342
343 - def __init__(self, dialogState, groupContext, node, takeNodeSettings):
359
361 self._flipbookEnum = nuke.Enumeration_Knob( "flipbook", "Flipbook", flipbooking.gFlipbookFactory.getNames() )
362 self._state.setKnob(self._flipbookEnum, "Default")
363 self.addKnob( self._flipbookEnum )
364 self._viewerForSettings = nuke.Enumeration_Knob("viewer_settings", "Take settings from", ["-"] + self._viewers.keys())
365 if not self._takeNodeSettings:
366 self._viewerForSettings.setValue("-")
367 self.addKnob(self._viewerForSettings)
368
369 self._defaultValues = nuke.PyScript_Knob("default", "Defaults")
370 self.addKnob(self._defaultValues)
371
372
373 self._useRoi = nuke.Boolean_Knob("use_roi", "Enable ROI")
374 self._useRoi.setFlag(nuke.STARTLINE)
375 self._state.setKnob(self._useRoi, False)
376 self.addKnob(self._useRoi)
377 self._roi = nuke.BBox_Knob("roi", "Region of Interest")
378 self._state.setKnob(self._roi, (0, 0, 0, 0))
379 self.addKnob(self._roi)
380 self._roi.setVisible(self._useRoi.value())
381
382
383 self._channels = nuke.Channel_Knob( "channels_knob", "Channels")
384 if self._node.Class() == "Write":
385 self._channels.setValue(self._node.knob("channels").value())
386 else:
387 self._state.setKnob(self._channels, "rgba")
388 self._channels.setFlag(nuke.STARTLINE | nuke.NO_CHECKMARKS)
389 self.addKnob( self._channels )
390
391 - def _addPostKnobs( self ):
392 super(FlipbookDialog, self)._addPostKnobs()
393
394 self._cleanup = nuke.Boolean_Knob("cleanup", "Delete existing temporary files")
395 self._cleanup.setFlag(nuke.STARTLINE)
396 self._state.setKnob(self._cleanup, True)
397 self.addKnob(self._cleanup)
398
399
400 self._luts = nuke.Enumeration_Knob("lut", "LUT", nuke.ViewerProcess.registeredNames())
401 if self._takeNodeSettings:
402 self._state.setKnob(self._luts, self._lutFromViewer(self._viewerForSettings.value()))
403 else:
404 self._state.setKnob(self._luts, self._lutFromViewer())
405 self.addKnob(self._luts)
406
407 self._burnInLUT = nuke.Boolean_Knob("burnin", "Burn in the LUT")
408 self._state.setKnob(self._burnInLUT, False)
409 self.addKnob(self._burnInLUT)
410
411
412 audioList = []
413 audioList.append( "None" )
414 for node in nuke.allNodes("AudioRead"):
415 audioList.append( node.name() )
416 self._audioSource = nuke.Enumeration_Knob( "audio", "Audio", audioList )
417 self._state.setKnob(self._audioSource, audioList[0] )
418 self.addKnob( self._audioSource )
419
422
432
435
451
454
463
465 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
466
470
472 RenderDialog.knobChanged(self, knob)
473 if knob == self._defaultValues:
474 self._setKnobAndStore(self._useRoi, False)
475 self._setKnobAndStore(self._roi, (0, 0, 0, 0))
476 self._roi.setVisible(False)
477 self._maxMem.setVisible(False)
478 self._numThreads.setVisible(False)
479 self._setKnobAndStore(self._viewerForSettings, "-")
480 self._setKnobAndStore(self._channels, "rgba")
481 self._setKnobAndStore(self._useProxy, False)
482 self._setKnobAndStore(self._frameRange, str(nuke.root().frameRange()))
483 self._setKnobAndStore(self._rangeEnum, "input")
484 self._setKnobAndStore(self._continueOnError, True)
485 self._setKnobAndStore(self._bgRender, False)
486 self._setKnobAndStore(self._luts, "sRGB")
487 self._setKnobAndStore(self._burnInLUT, False)
488 self._setKnobAndStore(self._cleanup, True)
489 self._setKnobAndStore(self._maxMem, str(max(nuke.memory("max_usage") / 2097152, 16)) + "M")
490 self._setKnobAndStore(self._numThreads, max(nuke.NUM_CPUS / 2, 1))
491 elif (knob == self._viewerForSettings):
492 if self._viewerForSettings.value() != "-":
493 viewer = self._viewers[self._viewerForSettings.value()]
494 self._setKnobAndStore(self._useRoi, viewer.roiEnabled())
495 roi = viewer.roi()
496 if roi != None:
497 self._roi.fromDict(roi)
498 self._state.save(self._roi)
499 self._channels.fromScript(viewer.knob("channels").toScript())
500 self._state.save(self._channels)
501 self._setKnobAndStore(self._useProxy, nuke.root().proxy())
502 self._frameRangeFromViewer(viewer.name())
503 self._state.save(self._frameRange)
504 self._setKnobAndStore(self._rangeEnum, viewer.name())
505 self._roi.setVisible(self._useRoi.value())
506 self._setKnobAndStore(self._luts, self._lutFromViewer(viewer.name()))
507 elif (knob == self._useRoi):
508 self._roi.setVisible(self._useRoi.value())
509 elif self._isViewerSettingKnob(knob):
510 self._viewerForSettings.setValue("-")
511 self._state.save(self._viewerForSettings)
512 elif knob == self._luts:
513 self._burnInLUT.setEnabled(self._luts.value() != "None")
514
515 if knob == self._flipbookEnum:
516 for k in self._customKnobs:
517 self.removeKnob(k)
518 self.removeKnob(self.okButton)
519 self.removeKnob(self.cancelButton)
520 self._customKnobs = []
521 self.flipbookKnobs()
522 self._makeOkCancelButton()
523 elif knob in self._customKnobs:
524 try:
525 flipbookToRun = flipbooking.gFlipbookFactory.getApplication(self._flipbookEnum.value())
526 flipbookToRun.dialogKnobChanged(self, knob)
527 except NotImplementedError:
528 pass
529
546
549
577
615
679
681 return self._luts.value()
682
690
692 options = {
693 }
694
695 try:
696 options['pixelAspect'] = float(nuke.value(nodeToFlipbook.name()+".pixel_aspect"))
697 except:
698 pass
699
700 try:
701 f = nodeToFlipbook.format()
702 options['dimensions'] = { 'width' : f.width(), 'height' : f.height() }
703 except:
704 pass
705
706
707 if not self._burnInLUT.value():
708 inputColourspace = "linear"
709 outputColourspace = "linear"
710
711 if self._node.Class() == "Read" or self._node.Class() == "Write":
712 lut = self._node.knob("colorspace").value()
713
714 if lut[:7] == "default":
715 lut = lut[9:-1]
716 inputColourspace = lut
717
718
719 lut = self._getLUT()
720 if lut != "None":
721 outputColourspace = lut
722
723 if inputColourspace == outputColourspace:
724 options["lut"] = inputColourspace
725 else:
726 options["lut"] = inputColourspace + "-" + outputColourspace
727
728 audioTrack = self._getAudio()
729 if audioTrack != "":
730 options["audio"] = audioTrack
731
732
733 if self._useRoi.value():
734 roi = self._roi.toDict()
735 if (roi["r"] - roi["x"] > 0) and (roi["t"] - roi["y"] > 0):
736 options["roi"] = bboxToTopLeft(int(nuke.value(nodeToFlipbook.name()+".actual_format.height")), roi)
737
738
739 return options
740
788
792
794 return "uk.co.thefoundry.ViewerCaptureDialog"
795
796 - def __init__(self, dialogState, groupContext, node):
842
855
858
860 """ Return an instance of a CaptureViewer class, which when executed captures the viewer.
861 """
862 flipbook = flipbooking.gFlipbookFactory.getApplication(self._flipbookEnum.value())
863 if flipbook and not os.access(flipbook.path(), os.X_OK):
864 raise RuntimeError("%s cannot be executed (%s)." % (flipbook.name(), flipbook.path(),) )
865
866
867 frameRange = self._frameRange.value()
868 viewer = self._node
869 selectedViews = self._selectedViews()
870 defaultWritePath = self._getIntermediatePath()
871 customWritePath = self._file.value() if self._customWrite.value() else ""
872 doFlipbook = not self._noFlipbook.value()
873 doCleanup = self._cleanup.value()
874
875 return captureViewer.CaptureViewer(flipbook, frameRange, viewer, selectedViews, defaultWritePath, customWritePath, doFlipbook, doCleanup)
876
878 """Present a dialog that executes the given list of nodes."""
879 groupContext = nuke.root()
880 d = ExecuteDialog(_gRenderDialogState, groupContext, nodesToExecute, exceptOnError)
881 if d.showModalDialog() == True:
882 d.run()
883
885 """Present a dialog that renders the given list of nodes."""
886 groupContext = nuke.root()
887 d = RenderDialog(_gRenderDialogState, groupContext, nodesToRender, exceptOnError)
888 if d.showModalDialog() == True:
889 d.run()
890
892 """Present a dialog that flipbooks the given node."""
893 if node is None:
894 raise RuntimeError("Can't launch flipbook, require a node.");
895 if node.Class() == "Viewer" and node.inputs() == 0:
896 raise RuntimeError("Can't launch flipbook, there is nothing connected to the viewed input.");
897
898 if not (nuke.canCreateNode("Write")):
899 nuke.message("Flipbooking is not permitted in Nuke Assist")
900 return
901
902 groupContext = nuke.root()
903
904 e = FlipbookDialog(_gFlipbookDialogState, groupContext, node, takeNodeSettings)
905 if (e.showModalDialog() == True):
906 e.run()
907
908
909
918
938
940 """Present a dialog that flipbooks the currently selected node."""
941 try:
942 showFlipbookDialog(nuke.selectedNode())
943 except ValueError, ve:
944 raise RuntimeError("Can't launch flipbook, %s." % (ve.args[0]))
945
947 """Convert the roi passed from a origin at the bottom left to the top left.
948 Also replaces the r and t keys with w and h keys.
949 @param height: the height used to determine the top.
950 @param roi: the roi with a bottom left origin, must have x, y, r & t keys.
951 @result dict with x, y, w & h keys"""
952 topLeftRoi = {
953 "x": roi["x"],
954 "y": height - roi["y"] - (roi["t"] - roi["y"]),
955 "w": roi["r"] - roi["x"],
956 "h": roi["t"] - roi["y"] }
957 return topLeftRoi
958
960 """ 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."""
961 _gRenderDialogState.saveValue(name, value)
962
964 """ 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."""
965 _gFlipbookDialogState.saveValue(name, value)
966