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 self._frameserverRender = nuke.Boolean_Knob("frameserver_render", "Render using frame server")
227 select_frame_server = nuke.toNode("preferences").knob("RenderWithFrameServer").getValue()
228 self._state.setKnob(self._frameserverRender, select_frame_server)
229 self._frameserverRender.setVisible(self.frameserverRenderAvailable())
230 self._frameserverRender.setFlag(nuke.STARTLINE)
231 self.addKnob(self._frameserverRender)
232
233 self._bgRender = nuke.Boolean_Knob("bg_render", "Render in background")
234 self._state.setKnob(self._bgRender, False)
235 self._bgRender.setVisible(self.backgroundRenderAvailable())
236 self._bgRender.setFlag(nuke.STARTLINE)
237 self.addKnob(self._bgRender)
238
239 self._numThreads = nuke.Int_Knob("num_threads", "Thread limit")
240 self._numThreads.setVisible(self.isBackgrounded())
241 self._state.setKnob(self._numThreads, max(nuke.NUM_CPUS / 2, 1))
242 self.addKnob(self._numThreads)
243 self._maxMem = nuke.String_Knob("max_memory", "Memory limit")
244 self._state.setKnob(self._maxMem, str(max(nuke.memory("max_usage") / 2097152, 16)) + "M")
245 self._maxMem.setVisible(self.isBackgrounded())
246 self.addKnob(self._maxMem)
247 if self.isBackgrounded():
248 self._showDeprecatedWarningMessage()
249
251
252 return {
253 "maxThreads": self._numThreads.value(),
254 "maxCache": self._maxMem.value() }
255
290
291
296
297
301
302
306
307
310
311
313 movWriteNodes = []
314 nonMovWriteNodes = []
315
316 for node in nodeSelection:
317 knob = node.knobs().get("file_type",None)
318 if knob:
319 fileType = knob.value()
320 if fileType in ("mov", "mov32", "mov64", "ffmpeg"):
321 movWriteNodes.append(node)
322 else:
323 nonMovWriteNodes.append(node)
324 return (nonMovWriteNodes, movWriteNodes)
325
326
328
329 if not nuke.env['studio']:
330 return False
331
332 if len(self._nodeSelection) > 1 or len(self._nodeSelection) < 1:
333 return False
334
335 write = self._nodeSelection[0]
336
337 if write == nuke.root():
338
339
340
341
342
343 writeNodes = nuke.allNodes( 'Write' )
344 writeNodes.extend( nuke.allNodes( 'WriteGeo') )
345 writeNodes.extend( nuke.allNodes( 'ParticleCache') )
346 writeNodes.extend( nuke.allNodes( 'DiskCache') )
347
348 if len(writeNodes) > 1:
349 return False
350
351 if len(writeNodes) > 0:
352 write = writeNodes[0]
353
354 timelineWriteNode = None
355
356 try:
357 from foundry.frameserver.nuke.workerapplication import GetWriteNode
358 timelineWriteNode = GetWriteNode()
359 except:
360 pass
361
362 if not timelineWriteNode:
363 return False
364
365 if timelineWriteNode.fullName() != write.fullName():
366 return False
367
368
369 try:
370 from hiero.ui import isInAnyProject
371 return isInAnyProject( nuke.scriptName() )
372 except:
373 pass
374
375 return False
376
378 import datetime
379 now = datetime.datetime.now()
380 timestamp = now.strftime('%H%M_%S.%f_%d-%m-%Y')
381
382 if nuke.env['nc']:
383 nukeExt = ".nknc"
384 else:
385 nukeExt = ".nk"
386
387 wasSaved = False
388
389 if nuke.Root().name() == 'Root':
390 fileName = "%s.%s%s"%(prefix,timestamp, nukeExt)
391 filePath = "/".join([os.environ["NUKE_TEMP_DIR"], fileName])
392 nuke.scriptSaveToTemp(filePath)
393 wasSaved = True
394 else:
395 originalPath = nuke.scriptName()
396
397 if nuke.Root().modified() == True or forceSaveNew:
398 extensionIndex = originalPath.rfind(nukeExt)
399 noExt = originalPath[:extensionIndex] if extensionIndex>=0 else originalPath
400 head, tail = os.path.split(noExt)
401 fileName = "%s.%s.%s%s"%(prefix,tail,timestamp,nukeExt)
402 filePath = "/".join([head,fileName])
403 try:
404 nuke.scriptSaveToTemp(filePath)
405 wasSaved = True
406 except RuntimeError:
407 nuke.tprint("Exception when saving script to: %s. Saving to NUKE_TEMP_DIR"%filePath)
408 fileName = originalPath.split()[-1]
409 filePath = "/".join([os.environ["NUKE_TEMP_DIR"], fileName])
410 nuke.scriptSaveToTemp(filePath)
411 wasSaved = True
412
413 else:
414 filePath = originalPath
415 return (filePath, wasSaved)
416
418 nodeSelection = self._nodeSelection
419 nodesToRenderCount= len(nodeSelection)
420 if nodesToRenderCount==1:
421 if nodeSelection[0] == nuke.root():
422 nodeSelection = nuke.allNodes("Write")
423 nodeSelection.extend(nuke.allNodes("DeepWrite"))
424 return nodeSelection
425
431
433 nodeSelection = self.getNodeSelection()
434 (nodesToRender, movNodesToRender) = self.extractMovNodes(nodeSelection)
435
436
437 for node in nodeSelection:
438 path = node["file"].getText()
439 needsPadding = frame_ranges.getRange(0).frames()>1
440 hasPadding = nuke.filename( node, nuke.REPLACE ) != path or node["file_type"].value()=="mov"
441 if needsPadding and not hasPadding:
442 nuke.message("%s cannot be executed for multiple frames."%node.fullName())
443 return
444
445 needsViews = (len(views)!=1 or views[0] != "main" )
446 hasViews = path.find("%v")!=-1 or path.find("%V")!=-1 or node["file_type"].value()=="exr"
447 if needsViews and not hasViews:
448 nuke.message("%s cannot be executed for multiple views."%node.fullName())
449 return
450
451
452 if movNodesToRender:
453 if RenderDialog.ShowWarning:
454 from PySide2.QtWidgets import (QMessageBox, QCheckBox)
455 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."
456 messageBox = QMessageBox(QMessageBox.Warning, "Warning", message, QMessageBox.Cancel)
457 messageBox.addButton("Continue",QMessageBox.AcceptRole)
458 dontShowCheckBox = QCheckBox("Don't show this message again")
459 dontShowCheckBox.blockSignals(True)
460 messageBox.addButton(dontShowCheckBox, QMessageBox.ResetRole)
461 result = messageBox.exec_()
462 if result != QMessageBox.AcceptRole:
463 return
464 if dontShowCheckBox.isChecked():
465 RenderDialog.ShowWarning = False
466
467 import hiero.ui.nuke_bridge.FnNsFrameServer as FrameServer
468 sortRenderOrder = lambda w : w.knobs()["render_order"].getValue()
469 if nodesToRender:
470 nodesToRender.sort(key = sortRenderOrder)
471 name, wasSaved = self.saveFileToRender("tmp_unsaved",False)
472 try:
473 for node in nodesToRender:
474 FrameServer.renderFrames(
475 name,
476 frame_ranges,
477 node.fullName(),
478 self._selectedViews())
479 finally:
480 if wasSaved:
481 os.unlink(name)
482
483 if movNodesToRender:
484 movNodesToRender.sort(key = sortRenderOrder)
485 nuke.executeMultiple(movNodesToRender, frame_ranges, views, continueOnError = self._continueOnError.value())
486
487
489
490 if self.isTimelineWrite() and self._timelineRender.value():
491 from hiero.ui.nuke_bridge.nukestudio import scriptSaveAndReRender
492 scriptSaveAndReRender()
493 return
494
495 frame_ranges = nuke.FrameRanges(self._frameRange.value().split(','))
496 views = self._selectedViews()
497 rootProxyMode = nuke.root().proxy()
498 try:
499 nuke.Undo().disable()
500 nuke.root().setProxy(self._useProxy.value())
501 if self.isFrameserverEnabled():
502 self.renderToFrameServer(frame_ranges,views)
503 elif self.isBackgrounded():
504 nuke.executeBackgroundNuke(nuke.EXE_PATH, self._nodeSelection,
505 frame_ranges, views, self._getBackgroundLimits(), continueOnError = self._continueOnError.value())
506 else:
507 nuke.executeMultiple(self._nodeSelection, frame_ranges, views, continueOnError = self._continueOnError.value())
508 except RuntimeError, e:
509 if self._exceptOnError or e.args[0][0:9] != "Cancelled":
510 raise
511 finally:
512 nuke.root().setProxy(rootProxyMode)
513 nuke.Undo().enable()
514
518
520 return "uk.co.thefoundry.FlipbookDialog"
521
522 - def __init__(self, dialogState, groupContext, node, takeNodeSettings):
538
540 self._flipbookEnum = nuke.Enumeration_Knob( "flipbook", "Flipbook", flipbooking.gFlipbookFactory.getNames() )
541 self._state.setKnob(self._flipbookEnum, "Default")
542 self.addKnob( self._flipbookEnum )
543 self._viewerForSettings = nuke.Enumeration_Knob("viewer_settings", "Take settings from", ["-"] + self._viewers.keys())
544 if not self._takeNodeSettings:
545 self._viewerForSettings.setValue("-")
546 self.addKnob(self._viewerForSettings)
547
548 self._defaultValues = nuke.PyScript_Knob("default", "Defaults")
549 self.addKnob(self._defaultValues)
550
551
552 self._useRoi = nuke.Boolean_Knob("use_roi", "Enable ROI")
553 self._useRoi.setFlag(nuke.STARTLINE)
554 self._state.setKnob(self._useRoi, False)
555 self.addKnob(self._useRoi)
556 self._roi = nuke.BBox_Knob("roi", "Region of Interest")
557 self._state.setKnob(self._roi, (0, 0, 0, 0))
558 self.addKnob(self._roi)
559 self._roi.setVisible(self._useRoi.value())
560
561
562 self._channels = nuke.Channel_Knob( "channels_knob", "Channels")
563 if self._node.Class() == "Write":
564 self._channels.setValue(self._node.knob("channels").value())
565 else:
566 self._state.setKnob(self._channels, "rgba")
567 self._channels.setFlag(nuke.STARTLINE | nuke.NO_CHECKMARKS)
568 self.addKnob( self._channels )
569
570 - def _addPostKnobs( self ):
571 super(FlipbookDialog, self)._addPostKnobs()
572
573 self._cleanup = nuke.Boolean_Knob("cleanup", "Delete existing temporary files")
574 self._cleanup.setFlag(nuke.STARTLINE)
575 self._state.setKnob(self._cleanup, True)
576 self.addKnob(self._cleanup)
577
578
579 self._luts = nuke.Enumeration_Knob("lut", "LUT", nuke.ViewerProcess.registeredNames())
580 if self._takeNodeSettings:
581 self._state.setKnob(self._luts, self._lutFromViewer(self._viewerForSettings.value()))
582 else:
583 self._state.setKnob(self._luts, self._lutFromViewer())
584 self.addKnob(self._luts)
585
586 self._burnInLUT = nuke.Boolean_Knob("burnin", "Burn in the LUT")
587 self._state.setKnob(self._burnInLUT, False)
588 self.addKnob(self._burnInLUT)
589
590
591 audioList = []
592 audioList.append( "None" )
593 for node in nuke.allNodes("AudioRead"):
594 audioList.append( node.name() )
595 self._audioSource = nuke.Enumeration_Knob( "audio", "Audio", audioList )
596 self._state.setKnob(self._audioSource, audioList[0] )
597 self.addKnob( self._audioSource )
598
601
611
614
638
641
650
652 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
653
657
659 RenderDialog.knobChanged(self, knob)
660 if knob == self._defaultValues:
661 self._setKnobAndStore(self._useRoi, False)
662 self._setKnobAndStore(self._roi, (0, 0, 0, 0))
663 self._roi.setVisible(False)
664 self._maxMem.setVisible(False)
665 self._numThreads.setVisible(False)
666 self._setKnobAndStore(self._viewerForSettings, "-")
667 self._setKnobAndStore(self._channels, "rgba")
668 self._setKnobAndStore(self._useProxy, False)
669 self._setKnobAndStore(self._frameRange, str(nuke.root().frameRange()))
670 self._setKnobAndStore(self._rangeEnum, "input")
671 self._setKnobAndStore(self._continueOnError, True)
672 self._setKnobAndStore(self._bgRender, False)
673 self._setKnobAndStore(self._luts, "sRGB")
674 self._setKnobAndStore(self._burnInLUT, False)
675 self._setKnobAndStore(self._cleanup, True)
676 self._setKnobAndStore(self._maxMem, str(max(nuke.memory("max_usage") / 2097152, 16)) + "M")
677 self._setKnobAndStore(self._numThreads, max(nuke.NUM_CPUS / 2, 1))
678 elif (knob == self._viewerForSettings):
679 if self._viewerForSettings.value() != "-":
680 viewer = self._viewers[self._viewerForSettings.value()]
681 self._setKnobAndStore(self._useRoi, viewer.roiEnabled())
682 roi = viewer.roi()
683 if roi != None:
684 self._roi.fromDict(roi)
685 self._state.save(self._roi)
686 self._channels.fromScript(viewer.knob("channels").toScript())
687 self._state.save(self._channels)
688 self._setKnobAndStore(self._useProxy, nuke.root().proxy())
689 self._frameRangeFromViewer(viewer.name())
690 self._state.save(self._frameRange)
691 self._setKnobAndStore(self._rangeEnum, viewer.name())
692 self._roi.setVisible(self._useRoi.value())
693 self._setKnobAndStore(self._luts, self._lutFromViewer(viewer.name()))
694 elif (knob == self._useRoi):
695 self._roi.setVisible(self._useRoi.value())
696 elif self._isViewerSettingKnob(knob):
697 self._viewerForSettings.setValue("-")
698 self._state.save(self._viewerForSettings)
699 elif knob == self._luts:
700 self._burnInLUT.setEnabled(self._luts.value() != "None")
701
702 if knob == self._flipbookEnum:
703 for k in self._customKnobs:
704 self.removeKnob(k)
705 self.removeKnob(self.okButton)
706 self.removeKnob(self.cancelButton)
707 self._customKnobs = []
708 self.flipbookKnobs()
709 self._makeOkCancelButton()
710 elif knob in self._customKnobs:
711 try:
712 flipbookToRun = flipbooking.gFlipbookFactory.getApplication(self._flipbookEnum.value())
713 flipbookToRun.dialogKnobChanged(self, knob)
714 except NotImplementedError:
715 pass
716
718 """Delete all the files in the range to be rendered."""
719 temporariesPath = self._getIntermediatePath()
720 isFilePerView = temporariesPath.find("%V") != -1
721 selectedViews = self._selectedViews()
722 temporariesPath = temporariesPath .replace("%V", "%s")
723 for r in nuke.FrameRanges(self._frameRange.value().split(',')):
724 deleteRange = xrange(r.minFrame(), r.maxFrame() + 1)
725
726 for v in selectedViews:
727 for i in deleteRange:
728 if isFilePerView and (len(selectedViews) > 1):
729 f = temporariesPath % (i, v,)
730 else:
731 f = temporariesPath % i
732
733 if os.access(f, os.F_OK):
734 os.remove(f)
735
738
766
810
812 """Helper function to get the appropriate colorspace to set on the Write node when burning the
813 viewer color transform into the render."""
814
815 writeColorspace = None
816
817 lut = self._getLUT()
818
819
820 usingNukeColorspaces = False
821 rootNode = nuke.root()
822 if rootNode:
823 colorManagementKnob = rootNode.knob("colorManagement")
824 if colorManagementKnob:
825 usingNukeColorspaces = (colorManagementKnob.value() == "Nuke")
826
827 if usingNukeColorspaces:
828
829
830
831
832
833
834
835
836
837 if lut == "rec1886":
838 writeColorspace = "Gamma2.4"
839 elif lut != "None":
840 writeColorspace = lut
841
842 else:
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864 displayName = ""
865 viewName = ""
866
867 NUKE_LUT_FORMAT = '(.*) \((.*)\)$'
868 matchResult = re.match( NUKE_LUT_FORMAT , lut)
869 if matchResult is not None:
870
871 viewName = matchResult.group(1)
872 displayName = matchResult.group(2)
873
874 if rootNode:
875 writeColorspace = rootNode.getOCIOColorspaceFromViewTransform(displayName, viewName)
876
877 return writeColorspace
878
879
940
942 return self._luts.value()
943
951
953 options = {
954 }
955
956 try:
957 options['pixelAspect'] = float(nuke.value(nodeToFlipbook.name()+".pixel_aspect"))
958 except:
959 pass
960
961 try:
962 f = nodeToFlipbook.format()
963 options['dimensions'] = { 'width' : f.width(), 'height' : f.height() }
964 except:
965 pass
966
967
968 if not self._burnInLUT.value():
969 inputColourspace = "linear"
970 outputColourspace = "linear"
971
972 if self._node.Class() == "Read" or self._node.Class() == "Write":
973 lut = self._node.knob("colorspace").value()
974
975 if lut[:7] == "default":
976 lut = lut[9:-1]
977 inputColourspace = lut
978
979
980 lut = self._getLUT()
981 if lut != "None":
982 outputColourspace = lut
983
984 if inputColourspace == outputColourspace:
985 options["lut"] = inputColourspace
986 else:
987 options["lut"] = inputColourspace + "-" + outputColourspace
988
989 audioTrack = self._getAudio()
990 if audioTrack != "":
991 options["audio"] = audioTrack
992
993
994 if self._useRoi.value():
995 roi = self._roi.toDict()
996 if (roi["r"] - roi["x"] > 0) and (roi["t"] - roi["y"] > 0):
997 options["roi"] = bboxToTopLeft(int(nuke.value(nodeToFlipbook.name()+".actual_format.height")), roi)
998
999
1000 return options
1001
1037
1041
1043 return "uk.co.thefoundry.ViewerCaptureDialog"
1044
1045 - def __init__(self, dialogState, groupContext, node):
1091
1105
1108
1110 """ Return an instance of a CaptureViewer class, which when executed captures the viewer.
1111 """
1112 flipbook = flipbooking.gFlipbookFactory.getApplication(self._flipbookEnum.value())
1113 if flipbook and not os.access(flipbook.path(), os.X_OK):
1114 raise RuntimeError("%s cannot be executed (%s)." % (flipbook.name(), flipbook.path(),) )
1115
1116
1117 frameRange = self._frameRange.value()
1118 viewer = self._node
1119 selectedViews = self._selectedViews()
1120 defaultWritePath = self._getIntermediatePath()
1121 customWritePath = self._file.value() if self._customWrite.value() else ""
1122 doFlipbook = not self._noFlipbook.value()
1123 doCleanup = self._cleanup.value()
1124
1125 return captureViewer.CaptureViewer(flipbook, frameRange, viewer, selectedViews, defaultWritePath, customWritePath, doFlipbook, doCleanup)
1126
1128 """Shows the dialog if dialog.showModalDialog() == True"""
1129 if (dialog.showModalDialog() == True):
1130 dialog.run()
1131
1133 """Present a dialog that executes the given list of nodes."""
1134 groupContext = nuke.root()
1135 d = ExecuteDialog(_gRenderDialogState, groupContext, nodesToExecute, exceptOnError)
1136 _showDialog(d)
1137
1138 -def showRenderDialog(nodesToRender, exceptOnError = True, allowFrameServer = True):
1143
1145 """Returns the flipbook dialog object created when flipbooking node"""
1146 if node is None:
1147 raise RuntimeError("Can't launch flipbook, require a node.");
1148 if node.Class() == "Viewer" and node.inputs() == 0:
1149 raise RuntimeError("Can't launch flipbook, there is nothing connected to the viewed input.");
1150
1151 if not (nuke.canCreateNode("Write")):
1152 nuke.message("Flipbooking is not permitted in Nuke Assist")
1153 return
1154
1155 groupContext = nuke.root()
1156
1157 e = FlipbookDialog(_gFlipbookDialogState, groupContext, node, takeNodeSettings)
1158 return e
1159
1164
1165
1166
1175
1195
1197 """Present a dialog that flipbooks the currently selected node."""
1198 try:
1199 showFlipbookDialog(nuke.selectedNode())
1200 except ValueError, ve:
1201 raise RuntimeError("Can't launch flipbook, %s." % (ve.args[0]))
1202
1204 """Convert the roi passed from a origin at the bottom left to the top left.
1205 Also replaces the r and t keys with w and h keys.
1206 @param height: the height used to determine the top.
1207 @param roi: the roi with a bottom left origin, must have x, y, r & t keys.
1208 @result dict with x, y, w & h keys"""
1209 topLeftRoi = {
1210 "x": roi["x"],
1211 "y": height - roi["y"] - (roi["t"] - roi["y"]),
1212 "w": roi["r"] - roi["x"],
1213 "h": roi["t"] - roi["y"] }
1214 return topLeftRoi
1215
1217 """ 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."""
1218 _gRenderDialogState.saveValue(name, value)
1219
1221 """ 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."""
1222 _gFlipbookDialogState.saveValue(name, value)
1223