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 from fnFlipbookRenderer import getFlipbookRenderer
16
17
18
19
20
21
22
26
27 - def get(self, knob, defaultValue = None):
28 """Return the given knob's stored last state value.
29 If none exists, defaultValue is returned.
30 Values are stored in a dict referenced by knob name, so names must be unique!"""
31 return self.getValue(knob.name(), defaultValue)
32
33 - def save(self, knob):
34 """Store the knob's current value as the 'last state' for the next time the dialog is opened.
35 Values are stored in a dict referenced by knob name, so names must be unique!"""
36 self.saveValue(knob.name(), knob.value())
37
38 - def setKnob(self, knob, defaultValue = None):
42 """Stores the value with the given id."""
43 self._state[id] = value
44 - def getValue(self, id, defaultValue = None):
45 """Recalls the value. If it was not set before, it will return the defaultValue."""
46 return self._state.get(id, defaultValue)
47
48 _gRenderDialogState = DialogState()
49 _gFlipbookDialogState = DialogState()
50 _gViewerCaptureDialogState = DialogState()
51
55
57 return "uk.co.thefoundry.ExecuteDialog"
58
60 """Add knobs that must appear before the render knobs."""
61 return
62
63 - def _addPostKnobs(self):
64 """Add knobs that must appear after the render knobs."""
65 return
66
68 """Add knobs that must appear at the very end."""
69 return
70
75
84
89
90 - def __init__(self, dialogState, groupContext, nodeSelection = [], exceptOnError = True):
91 self._state = dialogState
92 self._nodeSelection = nodeSelection
93 self._exceptOnError = exceptOnError
94
95 nukescripts.PythonPanel.__init__(self, self._titleString(), self._idString(), False)
96
97 self._viewers = {}
98 for n in nuke.allNodes("Viewer", groupContext):
99 self._viewers[n.name()] = n
100 self._specialRanges = ["input", "global", "custom"]
101
102 self._addPreKnobs()
103
104
105 self._rangeEnum = nuke.Enumeration_Knob( "frame_range", "Frame range", self._specialRanges + self._viewers.keys() )
106 self._state.setKnob(self._rangeEnum, "input")
107 self.addKnob( self._rangeEnum )
108
109 self._frameRange = nuke.String_Knob( "frame_range_string", "")
110 self._frameRange.clearFlag(nuke.STARTLINE)
111 if self._rangeEnum.value() == "custom":
112 self._state.setKnob(self._frameRange, str(nuke.root().frameRange()))
113 else:
114 self._setFrameRangeFromSource(self._rangeEnum.value())
115
116 self.addKnob(self._frameRange)
117
118
119 self._useProxy = nuke.Boolean_Knob("use_proxy", "Use proxy")
120 self._useProxy.setFlag(nuke.STARTLINE)
121 self._state.setKnob(self._useProxy, nuke.root().proxy())
122 self.addKnob(self._useProxy)
123
124 self._addPostKnobs()
125
126 self._continueOnError = nuke.Boolean_Knob("continue", "Continue on error")
127 self._state.setKnob(self._continueOnError, True)
128 self._continueOnError.setFlag(nuke.STARTLINE)
129 self.addKnob(self._continueOnError)
130
131 self._addViewKnob()
132
133 self._addTrailingKnobs()
134
144
160
162 """Set the framerange knob to have the framerange from the given viewer."""
163 viewerRange = str(self._viewers[viewer].playbackRange())
164 self._frameRange.setValue(viewerRange)
165
172
175
190
192 deprecatedWarningShown = False
193 shouldShowBGRender = False
194 ShowWarning = True
197
198
200 return "uk.co.thefoundry.RenderDialog"
201
202 - def __init__(self,
203 dialogState,
204 groupContext,
205 nodeSelection = [],
206 exceptOnError = True,
207 allowFrameServer = True):
208 self._allowFrameServer = allowFrameServer
209 ExecuteDialog.__init__(self, dialogState, groupContext, nodeSelection, exceptOnError)
210
217
218
223
224
225 - def _addPostKnobs(self):
226
227 bgRenderValue = self._state.getValue("bg_render",False)
228 RenderDialog.shouldShowBGRender = RenderDialog.shouldShowBGRender or bgRenderValue
229
230
231 self._frameserverRender = nuke.Boolean_Knob("frameserver_render", "Render using frame server")
232 if self._allowFrameServer:
233
234
235
236 if bgRenderValue:
237 self._frameserverRender.setValue(False)
238 self._state.save(self._frameserverRender)
239 else:
240
241 selectFrameServer = nuke.toNode("preferences").knob("RenderWithFrameServer").getValue()
242 self._state.setKnob(self._frameserverRender,selectFrameServer)
243 self._frameserverRender.setVisible(True)
244 else:
245 self._frameserverRender.setValue(False)
246 self._frameserverRender.setVisible(False)
247 self._frameserverRender.setFlag(nuke.STARTLINE)
248 self.addKnob(self._frameserverRender)
249
250
251 self._bgRender = nuke.Boolean_Knob("bg_render", "Render in background")
252 self._state.setKnob(self._bgRender, bgRenderValue)
253 self._bgRender.setVisible(RenderDialog.shouldShowBGRender)
254 self._bgRender.setFlag(nuke.STARTLINE)
255 self.addKnob(self._bgRender)
256
257 self._numThreads = nuke.Int_Knob("num_threads", "Thread limit")
258 self._numThreads.setVisible(self.isBackgrounded())
259 self._state.setKnob(self._numThreads, max(nuke.NUM_CPUS / 2, 1))
260 self.addKnob(self._numThreads)
261 self._maxMem = nuke.String_Knob("max_memory", "Memory limit")
262 self._state.setKnob(self._maxMem, str(max(nuke.memory("max_usage") / 2097152, 16)) + "M")
263 self._maxMem.setVisible(self.isBackgrounded())
264 self.addKnob(self._maxMem)
265 if self.isBackgrounded():
266 self._showDeprecatedWarningMessage()
267
269
270 return {
271 "maxThreads": self._numThreads.value(),
272 "maxCache": self._maxMem.value() }
273
314
315
317 """Return whether the background rendering option is enabled."""
318
319 return self._bgRender.value()
320
321
323 movWriteNodes = []
324 nonMovWriteNodes = []
325
326 for node in nodeSelection:
327 knob = node.knobs().get("file_type",None)
328 if knob:
329 fileType = knob.value()
330 if fileType in ("mov", "mov32", "mov64", "ffmpeg"):
331 movWriteNodes.append(node)
332 else:
333 nonMovWriteNodes.append(node)
334 return (nonMovWriteNodes, movWriteNodes)
335
336
338
339 if not nuke.env['studio']:
340 return False
341
342 if len(self._nodeSelection) > 1 or len(self._nodeSelection) < 1:
343 return False
344
345 write = self._nodeSelection[0]
346
347 if write == nuke.root():
348
349
350
351
352
353 writeNodes = nuke.allNodes( 'Write' )
354 writeNodes.extend( nuke.allNodes( 'WriteGeo') )
355 writeNodes.extend( nuke.allNodes( 'ParticleCache') )
356 writeNodes.extend( nuke.allNodes( 'DiskCache') )
357
358 if len(writeNodes) > 1:
359 return False
360
361 if len(writeNodes) > 0:
362 write = writeNodes[0]
363
364 timelineWriteNode = None
365
366 try:
367 from foundry.frameserver.nuke.workerapplication import GetWriteNode
368 timelineWriteNode = GetWriteNode()
369 except:
370 pass
371
372 if not timelineWriteNode:
373 return False
374
375 if timelineWriteNode.fullName() != write.fullName():
376 return False
377
378
379 try:
380 from hiero.ui import isInAnyProject
381 return isInAnyProject( nuke.scriptName() )
382 except:
383 pass
384
385 return False
386
388 import datetime
389 now = datetime.datetime.now()
390 timestamp = now.strftime('%H%M_%S.%f_%d-%m-%Y')
391
392 if nuke.env['nc']:
393 nukeExt = ".nknc"
394 else:
395 nukeExt = ".nk"
396
397 wasSaved = False
398
399 if nuke.Root().name() == 'Root':
400 fileName = "%s.%s%s"%(prefix,timestamp, nukeExt)
401 filePath = "/".join([os.environ["NUKE_TEMP_DIR"], fileName])
402 nuke.scriptSaveToTemp(filePath)
403 wasSaved = True
404 else:
405 originalPath = nuke.scriptName()
406
407 if nuke.Root().modified() == True or forceSaveNew:
408 extensionIndex = originalPath.rfind(nukeExt)
409 noExt = originalPath[:extensionIndex] if extensionIndex>=0 else originalPath
410 head, tail = os.path.split(noExt)
411 fileName = "%s.%s.%s%s"%(prefix,tail,timestamp,nukeExt)
412 filePath = "/".join([head,fileName])
413 try:
414 nuke.scriptSaveToTemp(filePath)
415 wasSaved = True
416 except RuntimeError:
417 nuke.tprint("Exception when saving script to: %s. Saving to NUKE_TEMP_DIR"%filePath)
418 fileName = originalPath.split()[-1]
419 filePath = "/".join([os.environ["NUKE_TEMP_DIR"], fileName])
420 nuke.scriptSaveToTemp(filePath)
421 wasSaved = True
422
423 else:
424 filePath = originalPath
425 return (filePath, wasSaved)
426
427
429
430 nodeSelection = self._nodeSelection
431 nodesToRenderCount= len(nodeSelection)
432 if nodesToRenderCount==1:
433 if nodeSelection[0] == nuke.root():
434 nodeSelection = nuke.allNodes("Write")
435 nodeSelection.extend(nuke.allNodes("DeepWrite"))
436 (nodesToRender, movNodesToRender) = self.extractMovNodes(nodeSelection)
437
438
439 for node in nodeSelection:
440 path = node["file"].getText()
441 needsPadding = frame_ranges.getRange(0).frames()>1
442 hasPadding = nuke.filename( node, nuke.REPLACE ) != path or node["file_type"].value()=="mov"
443 if needsPadding and not hasPadding:
444 nuke.message("%s cannot be executed for multiple frames."%node.fullName())
445 return
446
447 needsViews = (len(views)!=1 or views[0] != "main" )
448 hasViews = path.find("%v")!=-1 or path.find("%V")!=-1 or node["file_type"].value()=="exr"
449 if needsViews and not hasViews:
450 nuke.message("%s cannot be executed for multiple views."%node.fullName())
451 return
452
453
454 if movNodesToRender:
455 if RenderDialog.ShowWarning:
456 from PySide2.QtWidgets import (QMessageBox, QCheckBox)
457 message = "It is currently not possible to render container formats using the frame server. Select Continue to render in the current Nuke session.\n\nPlease see the user guide for more information."
458 messageBox = QMessageBox(QMessageBox.Warning, "Warning", message, QMessageBox.Cancel)
459 messageBox.addButton("Continue",QMessageBox.AcceptRole)
460 dontShowCheckBox = QCheckBox("Don't show this message again")
461 dontShowCheckBox.blockSignals(True)
462 messageBox.addButton(dontShowCheckBox, QMessageBox.ResetRole)
463 result = messageBox.exec_()
464 if result != QMessageBox.AcceptRole:
465 return
466 if dontShowCheckBox.isChecked():
467 RenderDialog.ShowWarning = False
468
469 import hiero.ui.nuke_bridge.FnNsFrameServer as FrameServer
470 sortRenderOrder = lambda w : w.knobs()["render_order"].getValue()
471 if nodesToRender:
472 nodesToRender.sort(key = sortRenderOrder)
473 name, wasSaved = self.saveFileToRender("tmp_unsaved",False)
474 try:
475 for node in nodesToRender:
476 FrameServer.renderFrames(
477 name,
478 frame_ranges,
479 node.fullName(),
480 self._selectedViews())
481 finally:
482 if wasSaved:
483 os.unlink(name)
484
485 if movNodesToRender:
486 movNodesToRender.sort(key = sortRenderOrder)
487 nuke.executeMultiple(movNodesToRender, frame_ranges, views, continueOnError = self._continueOnError.value())
488
489
491
492
493 if self.isTimelineWrite() and self._timelineRender.value():
494 from hiero.ui.nuke_bridge.nukestudio import scriptSaveAndReRender
495 scriptSaveAndReRender()
496 return
497
498 frame_ranges = nuke.FrameRanges(self._frameRange.value().split(','))
499 views = self._selectedViews()
500 rootProxyMode = nuke.root().proxy()
501 try:
502 nuke.Undo().disable()
503 nuke.root().setProxy(self._useProxy.value())
504 if self._frameserverRender.value():
505 self.renderToFrameServer(frame_ranges,views)
506
507 elif (self.isBackgrounded()):
508 nuke.executeBackgroundNuke(nuke.EXE_PATH, self._nodeSelection,
509 frame_ranges, views, self._getBackgroundLimits(), continueOnError = self._continueOnError.value())
510
511 else:
512
513 nuke.executeMultiple(self._nodeSelection, frame_ranges, views, continueOnError = self._continueOnError.value())
514 except RuntimeError, e:
515 if self._exceptOnError or e.args[0][0:9] != "Cancelled":
516 raise
517 finally:
518 nuke.root().setProxy(rootProxyMode)
519 nuke.Undo().enable()
520
524
526 return "uk.co.thefoundry.FlipbookDialog"
527
528 - def __init__(self, dialogState, groupContext, node, takeNodeSettings):
544
546 self._flipbookEnum = nuke.Enumeration_Knob( "flipbook", "Flipbook", flipbooking.gFlipbookFactory.getNames() )
547 self._state.setKnob(self._flipbookEnum, "Default")
548 self.addKnob( self._flipbookEnum )
549 self._viewerForSettings = nuke.Enumeration_Knob("viewer_settings", "Take settings from", ["-"] + self._viewers.keys())
550 if not self._takeNodeSettings:
551 self._viewerForSettings.setValue("-")
552 self.addKnob(self._viewerForSettings)
553
554 self._defaultValues = nuke.PyScript_Knob("default", "Defaults")
555 self.addKnob(self._defaultValues)
556
557
558 self._useRoi = nuke.Boolean_Knob("use_roi", "Enable ROI")
559 self._useRoi.setFlag(nuke.STARTLINE)
560 self._state.setKnob(self._useRoi, False)
561 self.addKnob(self._useRoi)
562 self._roi = nuke.BBox_Knob("roi", "Region of Interest")
563 self._state.setKnob(self._roi, (0, 0, 0, 0))
564 self.addKnob(self._roi)
565 self._roi.setVisible(self._useRoi.value())
566
567
568 self._channels = nuke.Channel_Knob( "channels_knob", "Channels")
569 if self._node.Class() == "Write":
570 self._channels.setValue(self._node.knob("channels").value())
571 else:
572 self._state.setKnob(self._channels, "rgba")
573 self._channels.setFlag(nuke.STARTLINE | nuke.NO_CHECKMARKS)
574 self.addKnob( self._channels )
575
576 - def _addPostKnobs( self ):
577 super(FlipbookDialog, self)._addPostKnobs()
578
579 self._cleanup = nuke.Boolean_Knob("cleanup", "Delete existing temporary files")
580 self._cleanup.setFlag(nuke.STARTLINE)
581 self._state.setKnob(self._cleanup, True)
582 self.addKnob(self._cleanup)
583
584
585 self._luts = nuke.Enumeration_Knob("lut", "LUT", nuke.ViewerProcess.registeredNames())
586 if self._takeNodeSettings:
587 self._state.setKnob(self._luts, self._lutFromViewer(self._viewerForSettings.value()))
588 else:
589 self._state.setKnob(self._luts, self._lutFromViewer())
590 self.addKnob(self._luts)
591
592 self._burnInLUT = nuke.Boolean_Knob("burnin", "Burn in the LUT")
593 self._state.setKnob(self._burnInLUT, False)
594 self.addKnob(self._burnInLUT)
595
596
597 audioList = []
598 audioList.append( "None" )
599 for node in nuke.allNodes("AudioRead"):
600 audioList.append( node.name() )
601 self._audioSource = nuke.Enumeration_Knob( "audio", "Audio", audioList )
602 self._state.setKnob(self._audioSource, audioList[0] )
603 self.addKnob( self._audioSource )
604
607
617
620
644
647
656
658 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
659
663
665 RenderDialog.knobChanged(self, knob)
666 if knob == self._defaultValues:
667 self._setKnobAndStore(self._useRoi, False)
668 self._setKnobAndStore(self._roi, (0, 0, 0, 0))
669 self._roi.setVisible(False)
670 self._maxMem.setVisible(False)
671 self._numThreads.setVisible(False)
672 self._setKnobAndStore(self._viewerForSettings, "-")
673 self._setKnobAndStore(self._channels, "rgba")
674 self._setKnobAndStore(self._useProxy, False)
675 self._setKnobAndStore(self._frameRange, str(nuke.root().frameRange()))
676 self._setKnobAndStore(self._rangeEnum, "input")
677 self._setKnobAndStore(self._continueOnError, True)
678 self._setKnobAndStore(self._bgRender, False)
679 self._setKnobAndStore(self._luts, "sRGB")
680 self._setKnobAndStore(self._burnInLUT, False)
681 self._setKnobAndStore(self._cleanup, True)
682 self._setKnobAndStore(self._maxMem, str(max(nuke.memory("max_usage") / 2097152, 16)) + "M")
683 self._setKnobAndStore(self._numThreads, max(nuke.NUM_CPUS / 2, 1))
684 elif (knob == self._viewerForSettings):
685 if self._viewerForSettings.value() != "-":
686 viewer = self._viewers[self._viewerForSettings.value()]
687 self._setKnobAndStore(self._useRoi, viewer.roiEnabled())
688 roi = viewer.roi()
689 if roi != None:
690 self._roi.fromDict(roi)
691 self._state.save(self._roi)
692 self._channels.fromScript(viewer.knob("channels").toScript())
693 self._state.save(self._channels)
694 self._setKnobAndStore(self._useProxy, nuke.root().proxy())
695 self._frameRangeFromViewer(viewer.name())
696 self._state.save(self._frameRange)
697 self._setKnobAndStore(self._rangeEnum, viewer.name())
698 self._roi.setVisible(self._useRoi.value())
699 self._setKnobAndStore(self._luts, self._lutFromViewer(viewer.name()))
700 elif (knob == self._useRoi):
701 self._roi.setVisible(self._useRoi.value())
702 elif self._isViewerSettingKnob(knob):
703 self._viewerForSettings.setValue("-")
704 self._state.save(self._viewerForSettings)
705 elif knob == self._luts:
706 self._burnInLUT.setEnabled(self._luts.value() != "None")
707
708 if knob == self._flipbookEnum:
709 for k in self._customKnobs:
710 self.removeKnob(k)
711 self.removeKnob(self.okButton)
712 self.removeKnob(self.cancelButton)
713 self._customKnobs = []
714 self.flipbookKnobs()
715 self._makeOkCancelButton()
716 elif knob in self._customKnobs:
717 try:
718 flipbookToRun = flipbooking.gFlipbookFactory.getApplication(self._flipbookEnum.value())
719 flipbookToRun.dialogKnobChanged(self, knob)
720 except NotImplementedError:
721 pass
722
724 """Delete all the files in the range to be rendered."""
725 temporariesPath = self._getIntermediatePath()
726 isFilePerView = temporariesPath.find("%V") != -1
727 selectedViews = self._selectedViews()
728 temporariesPath = temporariesPath .replace("%V", "%s")
729 for r in nuke.FrameRanges(self._frameRange.value().split(',')):
730 deleteRange = xrange(r.minFrame(), r.maxFrame() + 1)
731
732 for v in selectedViews:
733 for i in deleteRange:
734 if isFilePerView and (len(selectedViews) > 1):
735 f = temporariesPath % (i, v,)
736 else:
737 f = temporariesPath % i
738
739 if os.access(f, os.F_OK):
740 os.remove(f)
741
744
772
816
818 """Helper function to get the appropriate colorspace to set on the Write node when burning the
819 viewer color transform into the render."""
820
821 writeColorspace = None
822
823 lut = self._getLUT()
824
825
826 usingNukeColorspaces = False
827 rootNode = nuke.root()
828 if rootNode:
829 colorManagementKnob = rootNode.knob("colorManagement")
830 if colorManagementKnob:
831 usingNukeColorspaces = (colorManagementKnob.value() == "Nuke")
832
833 if usingNukeColorspaces:
834
835
836
837
838
839
840
841
842
843 if lut == "rec1886":
844 writeColorspace = "Gamma2.4"
845 elif lut != "None":
846 writeColorspace = lut
847
848 else:
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870 displayName = ""
871 viewName = ""
872
873 NUKE_LUT_FORMAT = '(.*) \((.*)\)$'
874 matchResult = re.match( NUKE_LUT_FORMAT , lut)
875 if matchResult is not None:
876
877 viewName = matchResult.group(1)
878 displayName = matchResult.group(2)
879
880 if rootNode:
881 writeColorspace = rootNode.getOCIOColorspaceFromViewTransform(displayName, viewName)
882
883 return writeColorspace
884
885
946
948 return self._luts.value()
949
957
959 options = {
960 }
961
962 try:
963 options['pixelAspect'] = float(nuke.value(nodeToFlipbook.name()+".pixel_aspect"))
964 except:
965 pass
966
967 try:
968 f = nodeToFlipbook.format()
969 options['dimensions'] = { 'width' : f.width(), 'height' : f.height() }
970 except:
971 pass
972
973
974 if not self._burnInLUT.value():
975 inputColourspace = "linear"
976 outputColourspace = "linear"
977
978 if self._node.Class() == "Read" or self._node.Class() == "Write":
979 lut = self._node.knob("colorspace").value()
980
981 if lut[:7] == "default":
982 lut = lut[9:-1]
983 inputColourspace = lut
984
985
986 lut = self._getLUT()
987 if lut != "None":
988 outputColourspace = lut
989
990 if inputColourspace == outputColourspace:
991 options["lut"] = inputColourspace
992 else:
993 options["lut"] = inputColourspace + "-" + outputColourspace
994
995 audioTrack = self._getAudio()
996 if audioTrack != "":
997 options["audio"] = audioTrack
998
999
1000 if self._useRoi.value():
1001 roi = self._roi.toDict()
1002 if (roi["r"] - roi["x"] > 0) and (roi["t"] - roi["y"] > 0):
1003 options["roi"] = bboxToTopLeft(int(nuke.value(nodeToFlipbook.name()+".actual_format.height")), roi)
1004
1005
1006 return options
1007
1043
1047
1049 return "uk.co.thefoundry.ViewerCaptureDialog"
1050
1051 - def __init__(self, dialogState, groupContext, node):
1097
1111
1114
1116 """ Return an instance of a CaptureViewer class, which when executed captures the viewer.
1117 """
1118 flipbook = flipbooking.gFlipbookFactory.getApplication(self._flipbookEnum.value())
1119 if flipbook and not os.access(flipbook.path(), os.X_OK):
1120 raise RuntimeError("%s cannot be executed (%s)." % (flipbook.name(), flipbook.path(),) )
1121
1122
1123 frameRange = self._frameRange.value()
1124 viewer = self._node
1125 selectedViews = self._selectedViews()
1126 defaultWritePath = self._getIntermediatePath()
1127 customWritePath = self._file.value() if self._customWrite.value() else ""
1128 doFlipbook = not self._noFlipbook.value()
1129 doCleanup = self._cleanup.value()
1130
1131 return captureViewer.CaptureViewer(flipbook, frameRange, viewer, selectedViews, defaultWritePath, customWritePath, doFlipbook, doCleanup)
1132
1134 """Shows the dialog if dialog.showModalDialog() == True"""
1135 if (dialog.showModalDialog() == True):
1136 dialog.run()
1137
1139 """Present a dialog that executes the given list of nodes."""
1140 groupContext = nuke.root()
1141 d = ExecuteDialog(_gRenderDialogState, groupContext, nodesToExecute, exceptOnError)
1142 _showDialog(d)
1143
1144 -def showRenderDialog(nodesToRender, exceptOnError = True, allowFrameServer = True):
1149
1151 """Returns the flipbook dialog object created when flipbooking node"""
1152 if node is None:
1153 raise RuntimeError("Can't launch flipbook, require a node.");
1154 if node.Class() == "Viewer" and node.inputs() == 0:
1155 raise RuntimeError("Can't launch flipbook, there is nothing connected to the viewed input.");
1156
1157 if not (nuke.canCreateNode("Write")):
1158 nuke.message("Flipbooking is not permitted in Nuke Assist")
1159 return
1160
1161 groupContext = nuke.root()
1162
1163 e = FlipbookDialog(_gFlipbookDialogState, groupContext, node, takeNodeSettings)
1164 return e
1165
1170
1171
1172
1181
1201
1203 """Present a dialog that flipbooks the currently selected node."""
1204 try:
1205 showFlipbookDialog(nuke.selectedNode())
1206 except ValueError, ve:
1207 raise RuntimeError("Can't launch flipbook, %s." % (ve.args[0]))
1208
1210 """Convert the roi passed from a origin at the bottom left to the top left.
1211 Also replaces the r and t keys with w and h keys.
1212 @param height: the height used to determine the top.
1213 @param roi: the roi with a bottom left origin, must have x, y, r & t keys.
1214 @result dict with x, y, w & h keys"""
1215 topLeftRoi = {
1216 "x": roi["x"],
1217 "y": height - roi["y"] - (roi["t"] - roi["y"]),
1218 "w": roi["r"] - roi["x"],
1219 "h": roi["t"] - roi["y"] }
1220 return topLeftRoi
1221
1223 """ 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."""
1224 _gRenderDialogState.saveValue(name, value)
1225
1227 """ 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."""
1228 _gFlipbookDialogState.saveValue(name, value)
1229