Package nukescripts :: Module renderdialog
[hide private]
[frames] | no frames]

Source Code for Module nukescripts.renderdialog

   1  # Copyright (c) 2010 The Foundry Visionmongers Ltd.  All Rights Reserved. 
   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  #import traceback 
  18   
  19  ############################################################################## 
  20  # Dialogs 
  21  ############################################################################## 
  22   
23 -class DialogState:
24 - def __init__(self):
25 self._state = {}
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):
39 """Convenience method for setting a value straight on a knob.""" 40 knob.setValue(self.get(knob, defaultValue))
41 - def saveValue(self, id, value):
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
52 -class ExecuteDialog(nukescripts.PythonPanel):
53 - def _titleString(self):
54 return "Execute"
55
56 - def _idString(self):
57 return "uk.co.thefoundry.ExecuteDialog"
58
59 - def _addPreKnobs(self):
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
67 - def _addTrailingKnobs(self):
68 """Add knobs that must appear at the very end.""" 69 return
70
71 - def _getDefaultViews(self):
72 oc = nuke.OutputContext() 73 allViews = [oc.viewname(i) for i in xrange(1, oc.viewcount())] 74 return " ".join(allViews)
75
76 - def _addViewKnob(self):
77 """Add knobs for view selection.""" 78 oc = nuke.OutputContext() 79 if (oc.viewcount() > 2): 80 self._viewSelection = nuke.MultiView_Knob("multi_view", "Views") 81 self._viewSelection.fromScript(self._state.get(self._viewSelection, self._getDefaultViews())) 82 self.addKnob(self._viewSelection) 83 self._viewSelection.clearFlag(nuke.NO_MULTIVIEW)
84
85 - def addKnob(self, knob):
86 """Add the knob and make sure it cannot be animated.""" 87 knob.setFlag(nuke.NO_ANIMATION | nuke.NO_MULTIVIEW) 88 super(ExecuteDialog, self).addKnob(knob)
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 # Frame range knobs 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 # Proxy 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
135 - def knobChanged( self, knob ):
136 self._state.save(knob) 137 if (knob == self._frameRange): 138 self._rangeEnum.setValue("custom") 139 self._state.save(self._rangeEnum) 140 self._state.saveValue("customRange", knob.value()) 141 if (knob == self._rangeEnum): 142 self._setFrameRangeFromSource(knob.value()) 143 self._state.save(self._frameRange)
144
145 - def _setFrameRangeFromSource(self, source):
146 if (source == "input"): 147 try: 148 activeInput = nuke.activeViewer().activeInput() 149 self._frameRange.setValue(str(nuke.activeViewer().node().upstreamFrameRange(activeInput))) 150 except: 151 self._frameRange.setValue(str(nuke.root().frameRange())) 152 elif (source == "global"): 153 self._frameRange.setValue(str(nuke.root().frameRange())) 154 elif (source == "custom"): 155 customRange = self._state.getValue("customRange", None) 156 if customRange: 157 self._frameRange.setValue(str(customRange)) 158 else: 159 self._frameRangeFromViewer(source);
160
161 - def _frameRangeFromViewer( self, viewer ):
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
166 - def _selectedViews(self):
167 try: 168 return self._viewSelection.value().split() 169 except AttributeError: 170 # If we didn't add the view selection knob, there should be just the one view. 171 return [nuke.OutputContext().viewname(1)]
172
173 - def addToPane(self):
175
176 - def run(self):
177 frame_ranges = nuke.FrameRanges(self._frameRange.value().split(',')) 178 views = self._selectedViews() 179 rootProxyMode = nuke.root().proxy() 180 try: 181 nuke.Undo().disable() 182 nuke.root().setProxy(self._useProxy.value()) 183 nuke.executeMultiple(self._nodeSelection, frame_ranges, views, continueOnError = self._continueOnError.value()) 184 except RuntimeError, e: 185 if self._exceptOnError or e.args[0][0:9] != "Cancelled": # TO DO: change this to an exception type 186 raise 187 finally: 188 nuke.root().setProxy(rootProxyMode) 189 nuke.Undo().enable()
190
191 -class RenderDialog(ExecuteDialog):
192 deprecatedWarningShown = False 193 shouldShowBGRender = False 194 ShowWarning = True
195 - def _titleString(self):
196 return "Render"
197 198
199 - def _idString(self):
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
211 - def _addPreKnobs( self ):
212 if self.isTimelineWrite(): 213 self._timelineRender = nuke.Boolean_Knob("timeline_render", "Render to timeline" ) 214 self._state.setKnob( self._timelineRender, True) 215 self._timelineRender.setFlag(nuke.STARTLINE) 216 self.addKnob(self._timelineRender)
217 218
220 if not RenderDialog.deprecatedWarningShown: 221 RenderDialog.deprecatedWarningShown = True; 222 nuke.tprint("Render in Background option functionality has been deprecated and replaced with the Frame Server.")
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
250 - def _getBackgroundLimits(self):
251 #Deprecated 252 return { 253 "maxThreads": self._numThreads.value(), 254 "maxCache": self._maxMem.value() }
255
256 - def knobChanged( self, knob ):
257 ExecuteDialog.knobChanged(self, knob) 258 259 timelineRender = False 260 try: 261 timelineRender = self._timelineRender.value() 262 except: 263 pass 264 265 bgRenderVisible = not timelineRender and self.backgroundRenderAvailable() 266 showBGRenderOptions = bgRenderVisible and self.isBackgrounded() 267 self._bgRender.setVisible(bgRenderVisible) 268 self._numThreads.setVisible(showBGRenderOptions) 269 self._maxMem.setVisible(showBGRenderOptions) 270 #if knob changed is the bgRender, set it visible 271 if knob is self._bgRender: 272 self._showDeprecatedWarningMessage() 273 274 275 if timelineRender and self.isTimelineWrite(): 276 self._rangeEnum.setValue( "global" ) 277 self._setFrameRangeFromSource(self._rangeEnum.value()) 278 self._rangeEnum.setEnabled( False ) 279 self._frameRange.setEnabled( False ) 280 self._frameserverRender.setVisible(False) 281 self._useProxy.setVisible( False ) 282 self._continueOnError.setVisible( False ) 283 else: 284 self._rangeEnum.setEnabled( True ) 285 self._frameRange.setEnabled( True ) 286 if self._allowFrameServer: 287 self._frameserverRender.setVisible(self.frameserverRenderAvailable()) 288 self._useProxy.setVisible( True ) 289 self._continueOnError.setVisible( True )
290 291
292 - def isBackgrounded(self):
293 """Return whether the background rendering option is enabled.""" 294 #Deprecated 295 return self._bgRender.value() and self.backgroundRenderAvailable()
296 297
298 - def isFrameserverEnabled(self):
299 """Return whether the frame server option is enabled.""" 300 return self._frameserverRender.value() and self.frameserverRenderAvailable()
301 302
303 - def backgroundRenderAvailable(self):
304 """Return whether background rendering should be allowed for this render""" 305 return RenderDialog.shouldShowBGRender or self.renderContainsMovs()
306 307
308 - def frameserverRenderAvailable(self):
309 return self._allowFrameServer and not self.renderContainsMovs()
310 311
312 - def extractMovNodes(self, nodeSelection):
313 movWriteNodes = [] 314 nonMovWriteNodes = [] 315 #extract mov nodes from the rest 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
327 - def isTimelineWrite(self):
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 ## must be a render of all 'write' nodes, this is tricky as there may be other executable nodes apart from write nodes 339 ## lets assume the write nodes are write, writegeo, particlecache, and diskcache 340 341 # there is a bug here however as there may be groups they are executable which will be skipped right now 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 ## double check that this script is actually in a timeline 369 try: 370 from hiero.ui import isInAnyProject 371 return isInAnyProject( nuke.scriptName() ) 372 except: 373 pass 374 375 return False
376
377 - def saveFileToRender(self, prefix, forceSaveNew):
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 #if file is unsaved 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 #if file is saved but not up to date 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 #if file is saved 413 else: 414 filePath = originalPath 415 return (filePath, wasSaved)
416
417 - def getNodeSelection(self):
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
426 - def renderContainsMovs(self):
427 (_, movNodesToRender) = self.extractMovNodes(self.getNodeSelection()) 428 if movNodesToRender: 429 return True 430 return False
431
432 - def renderToFrameServer(self,frame_ranges, views):
433 nodeSelection = self.getNodeSelection() 434 (nodesToRender, movNodesToRender) = self.extractMovNodes(nodeSelection) 435 436 #check views and frame pading 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 #there's no way to find if writer can write multiple views per file in python 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 #if there's any mov to be rendered, show a warning, render synchronously later 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
488 - def run(self):
489 ##this will render the full framerange of the script 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": # TO DO: change this to an exception type 510 raise 511 finally: 512 nuke.root().setProxy(rootProxyMode) 513 nuke.Undo().enable()
514
515 -class FlipbookDialog( RenderDialog ):
516 - def _titleString( self ):
517 return "Flipbook"
518
519 - def _idString( self ):
520 return "uk.co.thefoundry.FlipbookDialog"
521
522 - def __init__(self, dialogState, groupContext, node, takeNodeSettings):
523 # Init attributes 524 self._node = node 525 self._takeNodeSettings = takeNodeSettings 526 self._customKnobs = [] 527 528 # init super 529 RenderDialog.__init__(self, dialogState, groupContext) 530 531 # Override the initial frame range value 532 self._state.setKnob(self._rangeEnum, "input") 533 self._setFrameRangeFromSource(self._rangeEnum.value()) 534 535 if self._takeNodeSettings: 536 self._viewerForSettings.setValue(node.name()) 537 self.knobChanged(self._viewerForSettings)
538
539 - def _addPreKnobs( self ):
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 # Region of Interest knobs 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 # Channel knobs 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 # Misc knobs 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 # LUT knobs 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 # Audio knobs 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
599 - def _addTrailingKnobs(self):
600 self.flipbookKnobs()
601
602 - def flipbookKnobs(self):
603 try: 604 beforeKnobs = self.knobs() 605 flipbookToRun = flipbooking.gFlipbookFactory.getApplication(self._flipbookEnum.value()) 606 flipbookToRun.dialogKnobs(self) 607 afterKnobs = self.knobs() 608 self._customKnobs = list(set(beforeKnobs.values()) ^ set(afterKnobs.values())) 609 except NotImplementedError: 610 pass
611
612 - def _getDefaultViews(self):
613 return nuke.activeViewer().view()
614
615 - def _addViewKnob(self):
616 oc = nuke.OutputContext() 617 self._views = [oc.viewname(i) for i in xrange(1, oc.viewcount())] 618 if (oc.viewcount() > 2): 619 supportedViews = self._selectedFlipbook().capabilities()["maximumViews"] 620 if (int(supportedViews) > 1): 621 self._viewSelection = nuke.MultiView_Knob("views", "Views") 622 else: 623 self._viewSelection = nuke.OneView_Knob("views", "View", self._views) 624 activeView = nuke.activeViewer().view() 625 if activeView == "": 626 activeView = self._views[0] 627 628 # Retrieve previous view selection or default to selecting all available views 629 previousViews = self._state.getValue(self._viewSelection.name(), " ".join(self._views)).split() 630 # Get the intersection of the previous selection and the available views 631 viewsToRestore = set(self._views).intersection(previousViews) 632 if viewsToRestore: 633 self._viewSelection.setValue(" ".join(viewsToRestore)) 634 else: 635 self._viewSelection.setValue(activeView) 636 self.addKnob(self._viewSelection) 637 self._viewSelection.clearFlag(nuke.NO_MULTIVIEW)
638
639 - def _selectedFlipbook(self):
640 return flipbooking.gFlipbookFactory.getApplication(self._flipbookEnum.value())
641
642 - def _lutFromViewer(self, viewerName = ""):
643 try: 644 if viewerName == "": 645 return nuke.ViewerProcess.node().knob("current").value() 646 else: 647 return nuke.ViewerProcess.node(viewer=viewerName).knob("current").value() 648 except AttributeError: 649 return "None"
650
651 - def _isViewerSettingKnob(self, knob):
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
654 - def _setKnobAndStore(self, knob, val):
655 knob.setValue(val) 656 self._state.save(knob)
657
658 - def knobChanged(self, knob):
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
717 - def _deleteTemporaries(self):
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
736 - def _getIntermediateFileType(self):
737 return _gFlipbookDialogState.getValue('intermediateFormat', 'exr')
738
739 - def _getIntermediatePath(self):
740 """Get the path for the temporary files. May be filled in using printf syntax.""" 741 flipbooktmp="" 742 if flipbooktmp == "": 743 try: 744 flipbooktmp = self._selectedFlipbook().cacheDir() 745 except: 746 try: 747 flipbooktmp = os.environ["NUKE_DISK_CACHE"] 748 except: 749 flipbooktmp = nuke.value("preferences.DiskCachePath") 750 751 if len(self._selectedViews()) > 1: 752 flipbookFileNameTemp = "nuke_tmp_flip.%04d.%V." + self._getIntermediateFileType() 753 else: 754 flipbookFileNameTemp = "nuke_tmp_flip.%04d." + self._getIntermediateFileType() 755 flipbooktmpdir = os.path.join(flipbooktmp, "flipbook") 756 if not os.path.exists(flipbooktmpdir): 757 os.mkdir(flipbooktmpdir) 758 759 if not os.path.isdir(flipbooktmpdir): 760 raise RuntimeError("%s already exists and is not a directory, please delete before flipbooking again" % flipbooktmpdir) 761 flipbooktmp = os.path.join(flipbooktmpdir, flipbookFileNameTemp) 762 763 if nuke.env['WIN32']: 764 flipbooktmp = re.sub(r"\\", "/", str(flipbooktmp)) 765 return flipbooktmp
766
767 - def _requireIntermediateNode(self, nodeToTest):
768 if nodeToTest.Class() == "Read" or nodeToTest.Class() == "Write": 769 flipbookToRun = flipbooking.gFlipbookFactory.getApplication(self._flipbookEnum.value()) 770 flipbookCapabilities = flipbookToRun.capabilities() 771 772 # Check if we can read it in directly.. 773 filePath = nuke.filename(nodeToTest) 774 # There might be a prefix that overrides the extension, if so, this will 775 # confuse the flipbook probably, so just create a render. 776 if ':' in filePath: 777 readerPrefix = filePath.split(':')[0] 778 if len(readerPrefix) > 1: # 1 is a drive letter 779 return True 780 fileExtension = os.path.splitext(filePath)[1].lower()[1:] 781 flipbookSupportsFileType = fileExtension in flipbookCapabilities.get("fileTypes", []) 782 if not flipbookSupportsFileType: 783 return True 784 785 # Not all flipbooks can handle weird channels 786 flipbookSupportsArbitraryChannels = flipbookCapabilities.get("arbitraryChannels", False) 787 if self._channels.value() not in set(["rgb", "rgba", "alpha"]) and not flipbookSupportsArbitraryChannels: 788 return True 789 channelKnob = nodeToTest.knob("channels") 790 if channelKnob != None and channelKnob.value() != self._channels.value(): 791 return True 792 793 ## if flipbook doesn't support roi and the roi option is on we need the intermediate node because we're going to insert a crop 794 if self._useRoi.value() and self._useRoi.enabled(): 795 if not flipbookCapabilities.get("roi", False): 796 return True 797 798 if self._burnInLUT.value() and self._burnInLUT.enabled(): 799 return True 800 801 # Separate view files 802 # TODO Temporary workaround to deal with MediaSource not being aware of 803 # the %V notation for specifiying separate view files. 804 if "%V" in filePath or "%v" in filePath: 805 return True 806 807 return False 808 else: 809 return True
810
811 - def _getBurninWriteColorspace(self):
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 # Determine whether we're using original Nuke, rather than OCIO, color management. 820 usingNukeColorspaces = False 821 rootNode = nuke.root() 822 if rootNode: 823 colorManagementKnob = rootNode.knob("colorManagement") 824 if colorManagementKnob: 825 usingNukeColorspaces = (colorManagementKnob.value() == "Nuke") # Note: Must use value() rather than getValue(). 826 827 if usingNukeColorspaces: 828 829 # We're using Nuke colorspace management so the our lut knob will correspond to the appropriate 830 # colorspace name, except for rec1886, which we need to map to Gamma2.4. 831 # If the lut is the special case of None then don't set writeColorspace (to match the original behaviour). 832 # (The rec1886 -> Gamma2.4 mapping matches what's done in register_default_viewer_processes, in 833 # NukeScripts/src/ViewerProcess.py - I suppose we could consider adding something into 834 # nuke/src/Python/PythonObjects/ViewerProcess.cpp to allow the python code to look-up into ViewerProcessMap 835 # so we could parse the registered args to obtain the colorspace but that seems like overkill and not much 836 # less fragile.) 837 if lut == "rec1886": 838 writeColorspace = "Gamma2.4" 839 elif lut != "None": 840 writeColorspace = lut 841 842 else: 843 844 # We're using OCIO color management so we expect our lut string to contain a view name 845 # followed by a single space and a display name in parantheses. 846 # For example, the aces 1.0.1 config has lots of views but all for the display called ACES, 847 # hence here we might get lut set to, for example, 848 # DCDM P3 gamut clip (ACES) 849 # As another example, the spi-vfx config has views for two displays, DCIP3 and sRGB, so we might get 850 # Film (DCIP3) 851 # or 852 # Film (sRGB) 853 # etc. 854 # The nuke-default config has a single display called 'default' so we get lut set to 855 # None (default) 856 # or 857 # sRGB (default) 858 # etc. The nuke-default case is a bit confusing because the _view_ names almost match colorspace names 859 # and also correspond to legacy Nuke colorspace managament options. 860 # 861 # Anyway, what we actually need to return is the actual colorspace the particualr combination of 862 # view and display name map to, as defined in the relevant OCIO config file. 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
880 - def _createIntermediateNode(self):
881 """Create a write node to render out the current node so that output may be used for flipbooking.""" 882 flipbooktmp = self._getIntermediatePath() 883 884 fieldname = "file" 885 if self._useProxy.value(): 886 fieldname = "proxy" 887 888 fixup = nuke.createNode("Group", "tile_color 0xff000000", inpanel = False) 889 with fixup: 890 fixup.setName("Flipbook") 891 inputNode = nuke.createNode("Input", inpanel = False) 892 shuffle = nuke.createNode("Shuffle", inpanel = False) 893 shuffle.knob("in").setValue(self._channels.value()) 894 if self._useRoi.value(): 895 crop = nuke.createNode( "Crop", inpanel = False ) 896 crop['box'].fromScript( self._roi.toScript() ) 897 write = nuke.createNode("Write", fieldname+" {"+flipbooktmp+"}", inpanel = False) 898 write.knob('file_type').setValue(self._getIntermediateFileType()) 899 selectedViews = self._selectedViews() 900 write.knob('views').fromScript(" ".join(selectedViews)) 901 902 if self._getIntermediateFileType() == "exr": 903 write.knob('compression').setValue("B44") 904 # Set the 'heroview' to be the first of the selected views. If we don't 905 # do this then then 'heroview' is by default set to be 1 which may not 906 # be a valid view for this clip. The 'heroview' is used as a fallback if 907 # no view has been set on the reader. This assumes the user has selected 908 # sensible views if they haven't then the write may still fail. 909 if len(selectedViews) > 0: 910 firstView = nuke.OutputContext().viewFromName(selectedViews[0]) 911 write.knob('heroview').setValue(firstView) 912 913 writeColorspace = "" 914 915 if self._burnInLUT.value(): 916 # The user has chosen to burn the viewer transform into the intermedate render so set the colorspace 917 # on the Write node appropriately. 918 writeColorspace = self._getBurninWriteColorspace() 919 else: 920 # The viewer transform is not being burnt into the intermediate render, set the Write node's colorspace 921 # to whatever the current working space is - when reading the file back in the flipbook we'll ssume 922 # the media was written out in the working space. 923 rootNode = nuke.root() 924 if rootNode: 925 workingSpaceKnob = rootNode.knob("workingSpaceLUT") 926 if workingSpaceKnob: 927 writeColorspace = workingSpaceKnob.value() 928 929 if writeColorspace: 930 write.knob('colorspace').setValue(writeColorspace) 931 932 outputNode = nuke.createNode("Output", inpanel = False) 933 #If called on a Viewer connect fixup node to the one immediately above if exists. 934 if self._node.Class() == "Viewer": 935 fixup.setInput(0, self._node.input(int(nuke.knob(self._node.fullName()+".input_number")))) 936 else: 937 fixup.setInput(0, self._node) 938 939 return (fixup, write)
940
941 - def _getLUT(self):
942 return self._luts.value()
943
944 - def _getAudio(self):
945 nukeNode = nuke.toNode( self._audioSource.value() ) 946 ret = "" 947 if nukeNode != None: 948 ret = nukeNode["file"].getEvaluatedValue() 949 950 return ret
951
952 - def _getOptions(self, nodeToFlipbook):
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 # LUT 968 if not self._burnInLUT.value(): 969 inputColourspace = "linear" 970 outputColourspace = "linear" 971 # Check if we have a different than linear input 972 if self._node.Class() == "Read" or self._node.Class() == "Write": 973 lut = self._node.knob("colorspace").value() 974 # Might be in the format of "default (foo)", if so, get at "foo". 975 if lut[:7] == "default": 976 lut = lut[9:-1] 977 inputColourspace = lut 978 979 # Check our output 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 # AUDIO 989 audioTrack = self._getAudio() 990 if audioTrack != "": 991 options["audio"] = audioTrack 992 993 # ROI 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
1002 - def run(self):
1003 flipbookToRun = flipbooking.gFlipbookFactory.getApplication(self._flipbookEnum.value()) 1004 if (flipbookToRun): 1005 if not os.access(flipbookToRun.path(), os.X_OK): 1006 raise RuntimeError("%s cannot be executed (%s)." % (flipbookToRun.name(), flipbookToRun.path(),) ) 1007 1008 nodeToFlipbook = None 1009 rootProxyMode = nuke.root().proxy() 1010 try: 1011 # Need this to prevent Bug 5295 1012 nuke.Undo().disable() 1013 nuke.root().setProxy(self._useProxy.value()) 1014 1015 if self._cleanup.value(): 1016 self._deleteTemporaries() 1017 1018 calledOnNode = self._node 1019 if self._node.Class() == "Viewer": 1020 self._node = self._node.input(int(self._node.knob("input_number").value())) 1021 1022 renderer = getFlipbookRenderer(self, flipbookToRun) 1023 renderer.doFlipbook() 1024 1025 except Exception, e: 1026 import traceback 1027 traceback.print_exc(file=sys.stdout) 1028 print "exception ",e 1029 finally: 1030 #if an intermediate node was created, delete it 1031 if self._node != renderer._nodeToFlipbook: 1032 nuke.delete(renderer._nodeToFlipbook) 1033 nuke.root().setProxy(rootProxyMode) 1034 nuke.Undo().enable() 1035 else: 1036 raise RuntimeError("No flipbook called " + self._flipbookEnum.value() + " found. Was it deregistered while the dialog was open?")
1037
1038 -class ViewerCaptureDialog( FlipbookDialog ):
1039 - def _titleString( self ):
1040 return "Capture"
1041
1042 - def _idString( self ):
1043 return "uk.co.thefoundry.ViewerCaptureDialog"
1044
1045 - def __init__(self, dialogState, groupContext, node):
1046 1047 # init super 1048 FlipbookDialog.__init__(self, dialogState, groupContext, node, True) 1049 self._frameserverRender.setVisible(False) 1050 self._bgRender.setVisible(False) 1051 self._useProxy.setVisible(False) 1052 self._continueOnError.setVisible(False) 1053 1054 self._viewerForSettings.setVisible(False) 1055 self._defaultValues.setVisible(False) 1056 1057 self._useRoi.setVisible(False) 1058 self._roi.setVisible(False) 1059 1060 self._channels.setVisible(False) 1061 self._luts.setVisible(False) 1062 1063 self._audioSource.setVisible(False) 1064 self._burnInLUT.setVisible(False) 1065 1066 try: 1067 ## this may not exist, just ignore if not present 1068 self._viewSelection.setVisible(False) 1069 except: 1070 pass 1071 1072 customWriteActive = node['file'].getValue() != self._getIntermediatePath() and node['file'].getValue() != '' 1073 self._customWrite = nuke.Boolean_Knob( 'custom', 'Customise write path' ) 1074 self._customWrite.setValue( customWriteActive ) 1075 self.addKnob( self._customWrite ) 1076 1077 self._noFlipbook = nuke.Boolean_Knob( 'write', 'No flipbook' ) 1078 self._noFlipbook.setFlag( nuke.STARTLINE ) 1079 self._noFlipbook.setVisible( customWriteActive ) 1080 self._noFlipbook.setValue( self._state.get( self._noFlipbook, False ) ) 1081 self.addKnob( self._noFlipbook ) 1082 1083 self._file = nuke.File_Knob( 'file', 'Write path' ) 1084 defaultPath = self._node['file'].value() 1085 if defaultPath == self._getIntermediatePath(): 1086 defaultPath = '' 1087 self._file.setValue ( self._state.get(self._file, defaultPath ) ) 1088 self._file.setVisible( customWriteActive ) 1089 1090 self.addKnob( self._file )
1091
1092 - def knobChanged( self, knob ):
1093 try: 1094 FlipbookDialog.knobChanged(self, knob) 1095 self._frameserverRender.setVisible(False) 1096 if (knob == self._customWrite): 1097 self._noFlipbook.setVisible( self._customWrite.value() ) 1098 self._file.setVisible( self._customWrite.value() ) 1099 if ( not self._customWrite.value() ): 1100 self._node['file'].setValue( self._getIntermediatePath() ) 1101 elif( knob == self._noFlipbook ): 1102 self._flipbookEnum.setEnabled( not self._noFlipbook.value() ) 1103 except: 1104 pass
1105
1106 - def _getIntermediateFileType(self):
1107 return 'jpg'
1108
1109 - def captureViewer(self):
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 # build up the args 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
1127 -def _showDialog( dialog ):
1128 """Shows the dialog if dialog.showModalDialog() == True""" 1129 if (dialog.showModalDialog() == True): 1130 dialog.run()
1131
1132 -def showExecuteDialog(nodesToExecute, exceptOnError = True):
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):
1139 """Present a dialog that renders the given list of nodes.""" 1140 groupContext = nuke.root() 1141 d = RenderDialog(_gRenderDialogState, groupContext, nodesToRender, exceptOnError, allowFrameServer) 1142 _showDialog(d)
1143
1144 -def _getFlipbookDialog(node, takeNodeSettings = False):
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
1160 -def showFlipbookDialog(node, takeNodeSettings = False):
1161 """Present a dialog that flipbooks the given node.""" 1162 e = _getFlipbookDialog( node, takeNodeSettings ) 1163 _showDialog(e)
1164 1165 # because the capture button could have been triggered via a progress bar in update_handles 1166 # we defer the capture run until the main event loop is pumped
1167 -class ViewerCaptureDialogThread(Thread):
1168 - def __init__(self, captureViewer):
1169 Thread.__init__(self) 1170 self.captureViewer = captureViewer
1171
1172 - def run(self):
1173 ## only runs the dialog capture function when we are outside update_handles 1174 utils.executeInMainThreadWithResult( self.captureViewer, )
1175
1176 -def showViewerCaptureDialog(node):
1177 if node is None: 1178 raise RuntimeError("Can't launch flipbook, requires a viewer node."); 1179 if node.Class() != "Viewer" and node.inputs() == 0: 1180 raise RuntimeError("Can't launch flipbook, this is not a viewer node."); 1181 1182 if not (nuke.canCreateNode("Write")): 1183 nuke.message("Viewer capture is not permitted in Nuke Assist") 1184 return 1185 1186 groupContext = nuke.root() 1187 e = ViewerCaptureDialog(_gViewerCaptureDialogState, groupContext, node) 1188 if (e.showModalDialog() == True): 1189 # Bug 38516 - Be careful about what gets passed to the ViewerCaptureDialogThread, since anything 1190 # created here will be destroyed in a different thread. This can cause problems, such as crashes, 1191 # if a Qt widget gets passed. 1192 captureViewer = e.captureViewer() 1193 thread = ViewerCaptureDialogThread(captureViewer) 1194 thread.start()
1195
1196 -def showFlipbookDialogForSelected():
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
1203 -def bboxToTopLeft(height, roi):
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
1216 -def setRenderDialogDefaultOption(name, value):
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
1220 -def setFlipbookDefaultOption(name, value):
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