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 #Show the knob if it has been set before, or it has been set to True 227 bgRenderValue = self._state.getValue("bg_render",False) 228 RenderDialog.shouldShowBGRender = RenderDialog.shouldShowBGRender or bgRenderValue 229 230 # Background render stuff 231 self._frameserverRender = nuke.Boolean_Knob("frameserver_render", "Render using frame server") 232 if self._allowFrameServer: 233 #if default bgRender has been set after dialog has been shown for the first time, 234 #frameserver and bgrender might be set to true, make sure if bgRender is True, 235 #we set frameserver to false 236 if bgRenderValue: 237 self._frameserverRender.setValue(False) 238 self._state.save(self._frameserverRender) 239 else: 240 self._state.setKnob(self._frameserverRender,True) 241 self._frameserverRender.setVisible(True) 242 else: 243 self._frameserverRender.setValue(False) 244 self._frameserverRender.setVisible(False) 245 self._frameserverRender.setFlag(nuke.STARTLINE) 246 self.addKnob(self._frameserverRender) 247 248 #Deprecated 249 self._bgRender = nuke.Boolean_Knob("bg_render", "Render in background") 250 self._state.setKnob(self._bgRender, bgRenderValue) 251 self._bgRender.setVisible(RenderDialog.shouldShowBGRender) 252 self._bgRender.setFlag(nuke.STARTLINE) 253 self.addKnob(self._bgRender) 254 255 self._numThreads = nuke.Int_Knob("num_threads", "Thread limit") 256 self._numThreads.setVisible(self.isBackgrounded()) 257 self._state.setKnob(self._numThreads, max(nuke.NUM_CPUS / 2, 1)) 258 self.addKnob(self._numThreads) 259 self._maxMem = nuke.String_Knob("max_memory", "Memory limit") 260 self._state.setKnob(self._maxMem, str(max(nuke.memory("max_usage") / 2097152, 16)) + "M") 261 self._maxMem.setVisible(self.isBackgrounded()) 262 self.addKnob(self._maxMem) 263 if self.isBackgrounded(): 264 self._showDeprecatedWarningMessage()
265
266 - def _getBackgroundLimits(self):
267 #Deprecated 268 return { 269 "maxThreads": self._numThreads.value(), 270 "maxCache": self._maxMem.value() }
271
272 - def knobChanged( self, knob ):
273 ExecuteDialog.knobChanged(self, knob) 274 if knob == self._bgRender and self.isBackgrounded(): 275 self._frameserverRender.setValue(False) 276 self._state.save(self._frameserverRender) 277 elif knob == self._frameserverRender and self._frameserverRender.value(): 278 self._bgRender.setValue(False) 279 self._state.save(self._bgRender) 280 281 timelineRender = False 282 try: 283 timelineRender = self._timelineRender.value() 284 except: 285 pass 286 287 bgRenderVisible = not timelineRender and RenderDialog.shouldShowBGRender 288 showBGRenderOptions = bgRenderVisible and self.isBackgrounded() 289 self._bgRender.setVisible(bgRenderVisible) 290 self._numThreads.setVisible(showBGRenderOptions) 291 self._maxMem.setVisible(showBGRenderOptions) 292 #if knob changed is the bgRender, set it visible 293 if knob is self._bgRender: 294 self._showDeprecatedWarningMessage() 295 296 297 if timelineRender and self.isTimelineWrite(): 298 self._rangeEnum.setValue( "global" ) 299 self._setFrameRangeFromSource(self._rangeEnum.value()) 300 self._rangeEnum.setEnabled( False ) 301 self._frameRange.setEnabled( False ) 302 self._frameserverRender.setVisible(False) 303 self._useProxy.setVisible( False ) 304 self._continueOnError.setVisible( False ) 305 else: 306 self._rangeEnum.setEnabled( True ) 307 self._frameRange.setEnabled( True ) 308 if self._allowFrameServer: 309 self._frameserverRender.setVisible(True) 310 self._useProxy.setVisible( True ) 311 self._continueOnError.setVisible( True )
312 313
314 - def isBackgrounded(self):
315 """Return whether the background rendering option is enabled.""" 316 #Deprecated 317 return self._bgRender.value()
318 319
320 - def extractMovNodes(self, nodeSelection):
321 movWriteNodes = [] 322 nonMovWriteNodes = [] 323 #extract mov nodes from the rest 324 for node in nodeSelection: 325 knob = node.knobs().get("file_type",None) 326 if knob: 327 fileType = knob.value() 328 if fileType in ("mov", "mov32", "mov64", "ffmpeg"): 329 movWriteNodes.append(node) 330 else: 331 nonMovWriteNodes.append(node) 332 return (nonMovWriteNodes, movWriteNodes)
333 334
335 - def isTimelineWrite(self):
336 337 if not nuke.env['studio']: 338 return False 339 340 if len(self._nodeSelection) > 1 or len(self._nodeSelection) < 1: 341 return False 342 343 write = self._nodeSelection[0] 344 345 if write == nuke.root(): 346 ## must be a render of all 'write' nodes, this is tricky as there may be other executable nodes apart from write nodes 347 ## lets assume the write nodes are write, writegeo, particlecache, and diskcache 348 349 # there is a bug here however as there may be groups they are executable which will be skipped right now 350 351 writeNodes = nuke.allNodes( 'Write' ) 352 writeNodes.extend( nuke.allNodes( 'WriteGeo') ) 353 writeNodes.extend( nuke.allNodes( 'ParticleCache') ) 354 writeNodes.extend( nuke.allNodes( 'DiskCache') ) 355 356 if len(writeNodes) > 1: 357 return False 358 359 if len(writeNodes) > 0: 360 write = writeNodes[0] 361 362 timelineWriteNode = None 363 364 try: 365 from foundry.frameserver.nuke.workerapplication import GetWriteNode 366 timelineWriteNode = GetWriteNode() 367 except: 368 pass 369 370 if not timelineWriteNode: 371 return False 372 373 if timelineWriteNode.name() != write.name(): 374 return False 375 376 ## double check that this script is actually in a timeline 377 try: 378 from hiero.ui import isInAnyProject 379 return isInAnyProject( nuke.scriptName() ) 380 except: 381 pass 382 383 return False
384
385 - def saveFileToRender(self, prefix, forceSaveNew):
386 import datetime 387 now = datetime.datetime.now() 388 timestamp = now.strftime('%H%M_%S.%f_%d-%m-%Y') 389 390 if nuke.env['nc']: 391 nukeExt = ".nknc" 392 else: 393 nukeExt = ".nk" 394 395 wasSaved = False 396 #if file is unsaved 397 if nuke.Root().name() == 'Root': 398 fileName = "%s.%s%s"%(prefix,timestamp, nukeExt) 399 filePath = "/".join([os.environ["NUKE_TEMP_DIR"], fileName]) 400 nuke.scriptSaveToTemp(filePath) 401 wasSaved = True 402 else: 403 originalPath = nuke.scriptName() 404 #if file is saved but not up to date 405 if nuke.Root().modified() == True or forceSaveNew: 406 extensionIndex = originalPath.rfind(nukeExt) 407 noExt = originalPath[:extensionIndex] if extensionIndex>=0 else originalPath 408 head, tail = os.path.split(noExt) 409 fileName = "%s.%s.%s%s"%(prefix,tail,timestamp,nukeExt) 410 filePath = "/".join([head,fileName]) 411 try: 412 nuke.scriptSaveToTemp(filePath) 413 wasSaved = True 414 except RuntimeError: 415 nuke.tprint("Exception when saving script to: %s. Saving to NUKE_TEMP_DIR"%filePath) 416 fileName = originalPath.split()[-1] 417 filePath = "/".join([os.environ["NUKE_TEMP_DIR"], fileName]) 418 nuke.scriptSaveToTemp(filePath) 419 wasSaved = True 420 #if file is saved 421 else: 422 filePath = originalPath 423 return (filePath, wasSaved)
424 425
426 - def renderToFrameServer(self,frame_ranges, views):
427 #extract mov/non mov nodes 428 nodeSelection = self._nodeSelection 429 nodesToRenderCount= len(nodeSelection) 430 if nodesToRenderCount==1: 431 if nodeSelection[0] == nuke.root(): 432 nodeSelection = nuke.allNodes("Write") 433 nodeSelection.extend(nuke.allNodes("DeepWrite")) 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.name()) 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.name()) 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.name(), 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 ##RENDER USING THE FRAMESERVER 490 ##this will render the full framerange of the script 491 if self.isTimelineWrite() and self._timelineRender.value(): 492 from hiero.ui.nuke_bridge.nukestudio import scriptSaveAndReRender 493 scriptSaveAndReRender() 494 return 495 496 frame_ranges = nuke.FrameRanges(self._frameRange.value().split(',')) 497 views = self._selectedViews() 498 rootProxyMode = nuke.root().proxy() 499 try: 500 nuke.Undo().disable() 501 nuke.root().setProxy(self._useProxy.value()) 502 if self._frameserverRender.value(): 503 self.renderToFrameServer(frame_ranges,views) 504 #Begin deprecated 505 elif (self.isBackgrounded()): 506 nuke.executeBackgroundNuke(nuke.EXE_PATH, self._nodeSelection, 507 frame_ranges, views, self._getBackgroundLimits(), continueOnError = self._continueOnError.value()) 508 #End deprecated 509 else: 510 ###LOCAL RENDER### 511 nuke.executeMultiple(self._nodeSelection, frame_ranges, views, continueOnError = self._continueOnError.value()) 512 except RuntimeError, e: 513 if self._exceptOnError or e.args[0][0:9] != "Cancelled": # TO DO: change this to an exception type 514 raise 515 finally: 516 nuke.root().setProxy(rootProxyMode) 517 nuke.Undo().enable()
518
519 -class FlipbookDialog( RenderDialog ):
520 - def _titleString( self ):
521 return "Flipbook"
522
523 - def _idString( self ):
524 return "uk.co.thefoundry.FlipbookDialog"
525
526 - def __init__(self, dialogState, groupContext, node, takeNodeSettings):
527 # Init attributes 528 self._node = node 529 self._takeNodeSettings = takeNodeSettings 530 self._customKnobs = [] 531 532 # init super 533 RenderDialog.__init__(self, dialogState, groupContext) 534 535 # Override the initial frame range value 536 self._state.setKnob(self._rangeEnum, "input") 537 self._setFrameRangeFromSource(self._rangeEnum.value()) 538 539 if self._takeNodeSettings: 540 self._viewerForSettings.setValue(node.name()) 541 self.knobChanged(self._viewerForSettings)
542
543 - def _addPreKnobs( self ):
544 self._flipbookEnum = nuke.Enumeration_Knob( "flipbook", "Flipbook", flipbooking.gFlipbookFactory.getNames() ) 545 self._state.setKnob(self._flipbookEnum, "Default") 546 self.addKnob( self._flipbookEnum ) 547 self._viewerForSettings = nuke.Enumeration_Knob("viewer_settings", "Take settings from", ["-"] + self._viewers.keys()) 548 if not self._takeNodeSettings: 549 self._viewerForSettings.setValue("-") 550 self.addKnob(self._viewerForSettings) 551 552 self._defaultValues = nuke.PyScript_Knob("default", "Defaults") 553 self.addKnob(self._defaultValues) 554 555 # Region of Interest knobs 556 self._useRoi = nuke.Boolean_Knob("use_roi", "Enable ROI") 557 self._useRoi.setFlag(nuke.STARTLINE) 558 self._state.setKnob(self._useRoi, False) 559 self.addKnob(self._useRoi) 560 self._roi = nuke.BBox_Knob("roi", "Region of Interest") 561 self._state.setKnob(self._roi, (0, 0, 0, 0)) 562 self.addKnob(self._roi) 563 self._roi.setVisible(self._useRoi.value()) 564 565 # Channel knobs 566 self._channels = nuke.Channel_Knob( "channels_knob", "Channels") 567 if self._node.Class() == "Write": 568 self._channels.setValue(self._node.knob("channels").value()) 569 else: 570 self._state.setKnob(self._channels, "rgba") 571 self._channels.setFlag(nuke.STARTLINE | nuke.NO_CHECKMARKS) 572 self.addKnob( self._channels )
573
574 - def _addPostKnobs( self ):
575 super(FlipbookDialog, self)._addPostKnobs() 576 # Misc knobs 577 self._cleanup = nuke.Boolean_Knob("cleanup", "Delete existing temporary files") 578 self._cleanup.setFlag(nuke.STARTLINE) 579 self._state.setKnob(self._cleanup, True) 580 self.addKnob(self._cleanup) 581 582 # LUT knobs 583 self._luts = nuke.Enumeration_Knob("lut", "LUT", nuke.ViewerProcess.registeredNames()) 584 if self._takeNodeSettings: 585 self._state.setKnob(self._luts, self._lutFromViewer(self._viewerForSettings.value())) 586 else: 587 self._state.setKnob(self._luts, self._lutFromViewer()) 588 self.addKnob(self._luts) 589 590 self._burnInLUT = nuke.Boolean_Knob("burnin", "Burn in the LUT") 591 self._state.setKnob(self._burnInLUT, False) 592 self.addKnob(self._burnInLUT) 593 594 # Audio knobs 595 audioList = [] 596 audioList.append( "None" ) 597 for node in nuke.allNodes("AudioRead"): 598 audioList.append( node.name() ) 599 self._audioSource = nuke.Enumeration_Knob( "audio", "Audio", audioList ) 600 self._state.setKnob(self._audioSource, audioList[0] ) 601 self.addKnob( self._audioSource )
602
603 - def _addTrailingKnobs(self):
604 self.flipbookKnobs()
605
606 - def flipbookKnobs(self):
607 try: 608 beforeKnobs = self.knobs() 609 flipbookToRun = flipbooking.gFlipbookFactory.getApplication(self._flipbookEnum.value()) 610 flipbookToRun.dialogKnobs(self) 611 afterKnobs = self.knobs() 612 self._customKnobs = list(set(beforeKnobs.values()) ^ set(afterKnobs.values())) 613 except NotImplementedError: 614 pass
615
616 - def _getDefaultViews(self):
617 return nuke.activeViewer().view()
618
619 - def _addViewKnob(self):
620 oc = nuke.OutputContext() 621 self._views = [oc.viewname(i) for i in xrange(1, oc.viewcount())] 622 if (oc.viewcount() > 2): 623 supportedViews = self._selectedFlipbook().capabilities()["maximumViews"] 624 if (int(supportedViews) > 1): 625 self._viewSelection = nuke.MultiView_Knob("views", "Views") 626 else: 627 self._viewSelection = nuke.OneView_Knob("views", "View", self._views) 628 activeView = nuke.activeViewer().view() 629 if activeView == "": 630 activeView = self._views[0] 631 632 # Retrieve previous view selection or default to selecting all available views 633 previousViews = self._state.getValue(self._viewSelection.name(), " ".join(self._views)).split() 634 # Get the intersection of the previous selection and the available views 635 viewsToRestore = set(self._views).intersection(previousViews) 636 if viewsToRestore: 637 self._viewSelection.setValue(" ".join(viewsToRestore)) 638 else: 639 self._viewSelection.setValue(activeView) 640 self.addKnob(self._viewSelection) 641 self._viewSelection.clearFlag(nuke.NO_MULTIVIEW)
642
643 - def _selectedFlipbook(self):
644 return flipbooking.gFlipbookFactory.getApplication(self._flipbookEnum.value())
645
646 - def _lutFromViewer(self, viewerName = ""):
647 try: 648 if viewerName == "": 649 return nuke.ViewerProcess.node().knob("current").value() 650 else: 651 return nuke.ViewerProcess.node(viewer=viewerName).knob("current").value() 652 except AttributeError: 653 return "None"
654
655 - def _isViewerSettingKnob(self, knob):
656 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
657
658 - def _setKnobAndStore(self, knob, val):
659 knob.setValue(val) 660 self._state.save(knob)
661
662 - def knobChanged(self, knob):
663 RenderDialog.knobChanged(self, knob) 664 if knob == self._defaultValues: 665 self._setKnobAndStore(self._useRoi, False) 666 self._setKnobAndStore(self._roi, (0, 0, 0, 0)) 667 self._roi.setVisible(False) 668 self._maxMem.setVisible(False) 669 self._numThreads.setVisible(False) 670 self._setKnobAndStore(self._viewerForSettings, "-") 671 self._setKnobAndStore(self._channels, "rgba") 672 self._setKnobAndStore(self._useProxy, False) 673 self._setKnobAndStore(self._frameRange, str(nuke.root().frameRange())) 674 self._setKnobAndStore(self._rangeEnum, "input") 675 self._setKnobAndStore(self._continueOnError, True) 676 self._setKnobAndStore(self._bgRender, False) 677 self._setKnobAndStore(self._luts, "sRGB") 678 self._setKnobAndStore(self._burnInLUT, False) 679 self._setKnobAndStore(self._cleanup, True) 680 self._setKnobAndStore(self._maxMem, str(max(nuke.memory("max_usage") / 2097152, 16)) + "M") 681 self._setKnobAndStore(self._numThreads, max(nuke.NUM_CPUS / 2, 1)) 682 elif (knob == self._viewerForSettings): 683 if self._viewerForSettings.value() != "-": 684 viewer = self._viewers[self._viewerForSettings.value()] 685 self._setKnobAndStore(self._useRoi, viewer.roiEnabled()) 686 roi = viewer.roi() 687 if roi != None: 688 self._roi.fromDict(roi) 689 self._state.save(self._roi) 690 self._channels.fromScript(viewer.knob("channels").toScript()) 691 self._state.save(self._channels) 692 self._setKnobAndStore(self._useProxy, nuke.root().proxy()) 693 self._frameRangeFromViewer(viewer.name()) 694 self._state.save(self._frameRange) 695 self._setKnobAndStore(self._rangeEnum, viewer.name()) 696 self._roi.setVisible(self._useRoi.value()) 697 self._setKnobAndStore(self._luts, self._lutFromViewer(viewer.name())) 698 elif (knob == self._useRoi): 699 self._roi.setVisible(self._useRoi.value()) 700 elif self._isViewerSettingKnob(knob): 701 self._viewerForSettings.setValue("-") 702 self._state.save(self._viewerForSettings) 703 elif knob == self._luts: 704 self._burnInLUT.setEnabled(self._luts.value() != "None") 705 706 if knob == self._flipbookEnum: 707 for k in self._customKnobs: 708 self.removeKnob(k) 709 self.removeKnob(self.okButton) 710 self.removeKnob(self.cancelButton) 711 self._customKnobs = [] 712 self.flipbookKnobs() 713 self._makeOkCancelButton() 714 elif knob in self._customKnobs: 715 try: 716 flipbookToRun = flipbooking.gFlipbookFactory.getApplication(self._flipbookEnum.value()) 717 flipbookToRun.dialogKnobChanged(self, knob) 718 except NotImplementedError: 719 pass
720
721 - def _deleteTemporaries(self):
722 """Delete all the files in the range to be rendered.""" 723 temporariesPath = self._getIntermediatePath() 724 isFilePerView = temporariesPath.find("%V") != -1 725 selectedViews = self._selectedViews() 726 temporariesPath = temporariesPath .replace("%V", "%s") 727 for r in nuke.FrameRanges(self._frameRange.value().split(',')): 728 deleteRange = xrange(r.minFrame(), r.maxFrame() + 1) 729 730 for v in selectedViews: 731 for i in deleteRange: 732 if isFilePerView and (len(selectedViews) > 1): 733 f = temporariesPath % (i, v,) 734 else: 735 f = temporariesPath % i 736 737 if os.access(f, os.F_OK): 738 os.remove(f)
739
740 - def _getIntermediateFileType(self):
741 return _gFlipbookDialogState.getValue('intermediateFormat', 'exr')
742
743 - def _getIntermediatePath(self):
744 """Get the path for the temporary files. May be filled in using printf syntax.""" 745 flipbooktmp="" 746 if flipbooktmp == "": 747 try: 748 flipbooktmp = self._selectedFlipbook().cacheDir() 749 except: 750 try: 751 flipbooktmp = os.environ["NUKE_DISK_CACHE"] 752 except: 753 flipbooktmp = nuke.value("preferences.DiskCachePath") 754 755 if len(self._selectedViews()) > 1: 756 flipbookFileNameTemp = "nuke_tmp_flip.%04d.%V." + self._getIntermediateFileType() 757 else: 758 flipbookFileNameTemp = "nuke_tmp_flip.%04d." + self._getIntermediateFileType() 759 flipbooktmpdir = os.path.join(flipbooktmp, "flipbook") 760 if not os.path.exists(flipbooktmpdir): 761 os.mkdir(flipbooktmpdir) 762 763 if not os.path.isdir(flipbooktmpdir): 764 raise RuntimeError("%s already exists and is not a directory, please delete before flipbooking again" % flipbooktmpdir) 765 flipbooktmp = os.path.join(flipbooktmpdir, flipbookFileNameTemp) 766 767 if nuke.env['WIN32']: 768 flipbooktmp = re.sub(r"\\", "/", str(flipbooktmp)) 769 return flipbooktmp
770
771 - def _requireIntermediateNode(self, nodeToTest):
772 if nodeToTest.Class() == "Read" or nodeToTest.Class() == "Write": 773 flipbookToRun = flipbooking.gFlipbookFactory.getApplication(self._flipbookEnum.value()) 774 flipbookCapabilities = flipbookToRun.capabilities() 775 776 # Check if we can read it in directly.. 777 filePath = nuke.filename(nodeToTest) 778 # There might be a prefix that overrides the extension, if so, this will 779 # confuse the flipbook probably, so just create a render. 780 if ':' in filePath: 781 readerPrefix = filePath.split(':')[0] 782 if len(readerPrefix) > 1: # 1 is a drive letter 783 return True 784 fileExtension = os.path.splitext(filePath)[1].lower()[1:] 785 flipbookSupportsFileType = fileExtension in flipbookCapabilities.get("fileTypes", []) 786 if not flipbookSupportsFileType: 787 return True 788 789 # Not all flipbooks can handle weird channels 790 flipbookSupportsArbitraryChannels = flipbookCapabilities.get("arbitraryChannels", False) 791 if self._channels.value() not in set(["rgb", "rgba", "alpha"]) and not flipbookSupportsArbitraryChannels: 792 return True 793 channelKnob = nodeToTest.knob("channels") 794 if channelKnob != None and channelKnob.value() != self._channels.value(): 795 return True 796 797 ## 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 798 if self._useRoi.value() and self._useRoi.enabled(): 799 if not flipbookCapabilities.get("roi", False): 800 return True 801 802 if self._burnInLUT.value() and self._burnInLUT.enabled(): 803 return True 804 805 # Separate view files 806 # TODO Temporary workaround to deal with MediaSource not being aware of 807 # the %V notation for specifiying separate view files. 808 if "%V" in filePath or "%v" in filePath: 809 return True 810 811 return False 812 else: 813 return True
814
815 - def _getBurninWriteColorspace(self):
816 """Helper function to get the appropriate colorspace to set on the Write node when burning the 817 viewer color transform into the render.""" 818 819 writeColorspace = None 820 821 lut = self._getLUT() 822 823 # Determine whether we're using original Nuke, rather than OCIO, color management. 824 usingNukeColorspaces = False 825 rootNode = nuke.root() 826 if rootNode: 827 colorManagementKnob = rootNode.knob("colorManagement") 828 if colorManagementKnob: 829 usingNukeColorspaces = (colorManagementKnob.value() == "Nuke") # Note: Must use value() rather than getValue(). 830 831 if usingNukeColorspaces: 832 833 # We're using Nuke colorspace management so the our lut knob will correspond to the appropriate 834 # colorspace name, except for rec1886, which we need to map to Gamma2.4. 835 # If the lut is the special case of None then don't set writeColorspace (to match the original behaviour). 836 # (The rec1886 -> Gamma2.4 mapping matches what's done in register_default_viewer_processes, in 837 # NukeScripts/src/ViewerProcess.py - I suppose we could consider adding something into 838 # nuke/src/Python/PythonObjects/ViewerProcess.cpp to allow the python code to look-up into ViewerProcessMap 839 # so we could parse the registered args to obtain the colorspace but that seems like overkill and not much 840 # less fragile.) 841 if lut == "rec1886": 842 writeColorspace = "Gamma2.4" 843 elif lut != "None": 844 writeColorspace = lut 845 846 else: 847 848 # We're using OCIO color management so we expect our lut string to contain a view name 849 # followed by a single space and a display name in parantheses. 850 # For example, the aces 1.0.1 config has lots of views but all for the display called ACES, 851 # hence here we might get lut set to, for example, 852 # DCDM P3 gamut clip (ACES) 853 # As another example, the spi-vfx config has views for two displays, DCIP3 and sRGB, so we might get 854 # Film (DCIP3) 855 # or 856 # Film (sRGB) 857 # etc. 858 # The nuke-default config has a single display called 'default' so we get lut set to 859 # None (default) 860 # or 861 # sRGB (default) 862 # etc. The nuke-default case is a bit confusing because the _view_ names almost match colorspace names 863 # and also correspond to legacy Nuke colorspace managament options. 864 # 865 # Anyway, what we actually need to return is the actual colorspace the particualr combination of 866 # view and display name map to, as defined in the relevant OCIO config file. 867 868 displayName = "" 869 viewName = "" 870 871 NUKE_LUT_FORMAT = '(.*) \((.*)\)$' 872 matchResult = re.match( NUKE_LUT_FORMAT , lut) 873 if matchResult is not None: 874 875 viewName = matchResult.group(1) 876 displayName = matchResult.group(2) 877 878 if rootNode: 879 writeColorspace = rootNode.getOCIOColorspaceFromViewTransform(displayName, viewName) 880 881 return writeColorspace
882 883
884 - def _createIntermediateNode(self):
885 """Create a write node to render out the current node so that output may be used for flipbooking.""" 886 flipbooktmp = self._getIntermediatePath() 887 888 fieldname = "file" 889 if self._useProxy.value(): 890 fieldname = "proxy" 891 892 fixup = nuke.createNode("Group", "tile_color 0xff000000", inpanel = False) 893 with fixup: 894 fixup.setName("Flipbook") 895 inputNode = nuke.createNode("Input", inpanel = False) 896 shuffle = nuke.createNode("Shuffle", inpanel = False) 897 shuffle.knob("in").setValue(self._channels.value()) 898 if self._useRoi.value(): 899 crop = nuke.createNode( "Crop", inpanel = False ) 900 crop['box'].fromScript( self._roi.toScript() ) 901 write = nuke.createNode("Write", fieldname+" {"+flipbooktmp+"}", inpanel = False) 902 write.knob('file_type').setValue(self._getIntermediateFileType()) 903 selectedViews = self._selectedViews() 904 write.knob('views').fromScript(" ".join(selectedViews)) 905 906 if self._getIntermediateFileType() == "exr": 907 write.knob('compression').setValue("B44") 908 # Set the 'heroview' to be the first of the selected views. If we don't 909 # do this then then 'heroview' is by default set to be 1 which may not 910 # be a valid view for this clip. The 'heroview' is used as a fallback if 911 # no view has been set on the reader. This assumes the user has selected 912 # sensible views if they haven't then the write may still fail. 913 if len(selectedViews) > 0: 914 firstView = nuke.OutputContext().viewFromName(selectedViews[0]) 915 write.knob('heroview').setValue(firstView) 916 917 writeColorspace = "" 918 919 if self._burnInLUT.value(): 920 # The user has chosen to burn the viewer transform into the intermedate render so set the colorspace 921 # on the Write node appropriately. 922 writeColorspace = self._getBurninWriteColorspace() 923 else: 924 # The viewer transform is not being burnt into the intermediate render, set the Write node's colorspace 925 # to whatever the current working space is - when reading the file back in the flipbook we'll ssume 926 # the media was written out in the working space. 927 rootNode = nuke.root() 928 if rootNode: 929 workingSpaceKnob = rootNode.knob("workingSpaceLUT") 930 if workingSpaceKnob: 931 writeColorspace = workingSpaceKnob.value() 932 933 if writeColorspace: 934 write.knob('colorspace').setValue(writeColorspace) 935 936 outputNode = nuke.createNode("Output", inpanel = False) 937 #If called on a Viewer connect fixup node to the one immediately above if exists. 938 if self._node.Class() == "Viewer": 939 fixup.setInput(0, self._node.input(int(nuke.knob(self._node.fullName()+".input_number")))) 940 else: 941 fixup.setInput(0, self._node) 942 943 return (fixup, write)
944
945 - def _getLUT(self):
946 return self._luts.value()
947
948 - def _getAudio(self):
949 nukeNode = nuke.toNode( self._audioSource.value() ) 950 ret = "" 951 if nukeNode != None: 952 ret = nukeNode["file"].getEvaluatedValue() 953 954 return ret
955
956 - def _getOptions(self, nodeToFlipbook):
957 options = { 958 } 959 960 try: 961 options['pixelAspect'] = float(nuke.value(nodeToFlipbook.name()+".pixel_aspect")) 962 except: 963 pass 964 965 try: 966 f = nodeToFlipbook.format() 967 options['dimensions'] = { 'width' : f.width(), 'height' : f.height() } 968 except: 969 pass 970 971 # LUT 972 if not self._burnInLUT.value(): 973 inputColourspace = "linear" 974 outputColourspace = "linear" 975 # Check if we have a different than linear input 976 if self._node.Class() == "Read" or self._node.Class() == "Write": 977 lut = self._node.knob("colorspace").value() 978 # Might be in the format of "default (foo)", if so, get at "foo". 979 if lut[:7] == "default": 980 lut = lut[9:-1] 981 inputColourspace = lut 982 983 # Check our output 984 lut = self._getLUT() 985 if lut != "None": 986 outputColourspace = lut 987 988 if inputColourspace == outputColourspace: 989 options["lut"] = inputColourspace 990 else: 991 options["lut"] = inputColourspace + "-" + outputColourspace 992 # AUDIO 993 audioTrack = self._getAudio() 994 if audioTrack != "": 995 options["audio"] = audioTrack 996 997 # ROI 998 if self._useRoi.value(): 999 roi = self._roi.toDict() 1000 if (roi["r"] - roi["x"] > 0) and (roi["t"] - roi["y"] > 0): 1001 options["roi"] = bboxToTopLeft(int(nuke.value(nodeToFlipbook.name()+".actual_format.height")), roi) 1002 1003 1004 return options
1005
1006 - def run(self):
1007 flipbookToRun = flipbooking.gFlipbookFactory.getApplication(self._flipbookEnum.value()) 1008 if (flipbookToRun): 1009 if not os.access(flipbookToRun.path(), os.X_OK): 1010 raise RuntimeError("%s cannot be executed (%s)." % (flipbookToRun.name(), flipbookToRun.path(),) ) 1011 1012 nodeToFlipbook = None 1013 rootProxyMode = nuke.root().proxy() 1014 try: 1015 # Need this to prevent Bug 5295 1016 nuke.Undo().disable() 1017 nuke.root().setProxy(self._useProxy.value()) 1018 1019 if self._cleanup.value(): 1020 self._deleteTemporaries() 1021 1022 calledOnNode = self._node 1023 if self._node.Class() == "Viewer": 1024 self._node = self._node.input(int(self._node.knob("input_number").value())) 1025 1026 renderer = getFlipbookRenderer(self, flipbookToRun) 1027 renderer.doFlipbook() 1028 1029 except Exception, e: 1030 import traceback 1031 traceback.print_exc(file=sys.stdout) 1032 print "exception ",e 1033 finally: 1034 #if an intermediate node was created, delete it 1035 if self._node != renderer._nodeToFlipbook: 1036 nuke.delete(renderer._nodeToFlipbook) 1037 nuke.root().setProxy(rootProxyMode) 1038 nuke.Undo().enable() 1039 else: 1040 raise RuntimeError("No flipbook called " + self._flipbookEnum.value() + " found. Was it deregistered while the dialog was open?")
1041
1042 -class ViewerCaptureDialog( FlipbookDialog ):
1043 - def _titleString( self ):
1044 return "Capture"
1045
1046 - def _idString( self ):
1047 return "uk.co.thefoundry.ViewerCaptureDialog"
1048
1049 - def __init__(self, dialogState, groupContext, node):
1050 1051 # init super 1052 FlipbookDialog.__init__(self, dialogState, groupContext, node, True) 1053 self._frameserverRender.setVisible(False) 1054 self._bgRender.setVisible(False) 1055 self._useProxy.setVisible(False) 1056 self._continueOnError.setVisible(False) 1057 1058 self._viewerForSettings.setVisible(False) 1059 self._defaultValues.setVisible(False) 1060 1061 self._useRoi.setVisible(False) 1062 self._roi.setVisible(False) 1063 1064 self._channels.setVisible(False) 1065 self._luts.setVisible(False) 1066 1067 self._audioSource.setVisible(False) 1068 self._burnInLUT.setVisible(False) 1069 1070 try: 1071 ## this may not exist, just ignore if not present 1072 self._viewSelection.setVisible(False) 1073 except: 1074 pass 1075 1076 customWriteActive = node['file'].getValue() != self._getIntermediatePath() and node['file'].getValue() != '' 1077 self._customWrite = nuke.Boolean_Knob( 'custom', 'Customise write path' ) 1078 self._customWrite.setValue( customWriteActive ) 1079 self.addKnob( self._customWrite ) 1080 1081 self._noFlipbook = nuke.Boolean_Knob( 'write', 'No flipbook' ) 1082 self._noFlipbook.setFlag( nuke.STARTLINE ) 1083 self._noFlipbook.setVisible( customWriteActive ) 1084 self._noFlipbook.setValue( self._state.get( self._noFlipbook, False ) ) 1085 self.addKnob( self._noFlipbook ) 1086 1087 self._file = nuke.File_Knob( 'file', 'Write path' ) 1088 defaultPath = self._node['file'].value() 1089 if defaultPath == self._getIntermediatePath(): 1090 defaultPath = '' 1091 self._file.setValue ( self._state.get(self._file, defaultPath ) ) 1092 self._file.setVisible( customWriteActive ) 1093 1094 self.addKnob( self._file )
1095
1096 - def knobChanged( self, knob ):
1097 try: 1098 FlipbookDialog.knobChanged(self, knob) 1099 self._frameserverRender.setVisible(False) 1100 if (knob == self._customWrite): 1101 self._noFlipbook.setVisible( self._customWrite.value() ) 1102 self._file.setVisible( self._customWrite.value() ) 1103 if ( not self._customWrite.value() ): 1104 self._node['file'].setValue( self._getIntermediatePath() ) 1105 elif( knob == self._noFlipbook ): 1106 self._flipbookEnum.setEnabled( not self._noFlipbook.value() ) 1107 except: 1108 pass
1109
1110 - def _getIntermediateFileType(self):
1111 return 'jpg'
1112
1113 - def captureViewer(self):
1114 """ Return an instance of a CaptureViewer class, which when executed captures the viewer. 1115 """ 1116 flipbook = flipbooking.gFlipbookFactory.getApplication(self._flipbookEnum.value()) 1117 if flipbook and not os.access(flipbook.path(), os.X_OK): 1118 raise RuntimeError("%s cannot be executed (%s)." % (flipbook.name(), flipbook.path(),) ) 1119 1120 # build up the args 1121 frameRange = self._frameRange.value() 1122 viewer = self._node 1123 selectedViews = self._selectedViews() 1124 defaultWritePath = self._getIntermediatePath() 1125 customWritePath = self._file.value() if self._customWrite.value() else "" 1126 doFlipbook = not self._noFlipbook.value() 1127 doCleanup = self._cleanup.value() 1128 1129 return captureViewer.CaptureViewer(flipbook, frameRange, viewer, selectedViews, defaultWritePath, customWritePath, doFlipbook, doCleanup)
1130
1131 -def _showDialog( dialog ):
1132 """Shows the dialog if dialog.showModalDialog() == True""" 1133 if (dialog.showModalDialog() == True): 1134 dialog.run()
1135
1136 -def showExecuteDialog(nodesToExecute, exceptOnError = True):
1137 """Present a dialog that executes the given list of nodes.""" 1138 groupContext = nuke.root() 1139 d = ExecuteDialog(_gRenderDialogState, groupContext, nodesToExecute, exceptOnError) 1140 _showDialog(d) 1141
1142 -def showRenderDialog(nodesToRender, exceptOnError = True, allowFrameServer = True):
1143 """Present a dialog that renders the given list of nodes.""" 1144 groupContext = nuke.root() 1145 d = RenderDialog(_gRenderDialogState, groupContext, nodesToRender, exceptOnError, allowFrameServer) 1146 _showDialog(d)
1147
1148 -def _getFlipbookDialog(node, takeNodeSettings = False):
1149 """Returns the flipbook dialog object created when flipbooking node""" 1150 if node is None: 1151 raise RuntimeError("Can't launch flipbook, require a node."); 1152 if node.Class() == "Viewer" and node.inputs() == 0: 1153 raise RuntimeError("Can't launch flipbook, there is nothing connected to the viewed input."); 1154 1155 if not (nuke.canCreateNode("Write")): 1156 nuke.message("Flipbooking is not permitted in Nuke Assist") 1157 return 1158 1159 groupContext = nuke.root() 1160 1161 e = FlipbookDialog(_gFlipbookDialogState, groupContext, node, takeNodeSettings) 1162 return e
1163
1164 -def showFlipbookDialog(node, takeNodeSettings = False):
1165 """Present a dialog that flipbooks the given node.""" 1166 e = _getFlipbookDialog( node, takeNodeSettings ) 1167 _showDialog(e)
1168 1169 # because the capture button could have been triggered via a progress bar in update_handles 1170 # we defer the capture run until the main event loop is pumped
1171 -class ViewerCaptureDialogThread(Thread):
1172 - def __init__(self, captureViewer):
1173 Thread.__init__(self) 1174 self.captureViewer = captureViewer
1175
1176 - def run(self):
1177 ## only runs the dialog capture function when we are outside update_handles 1178 utils.executeInMainThreadWithResult( self.captureViewer, )
1179
1180 -def showViewerCaptureDialog(node):
1181 if node is None: 1182 raise RuntimeError("Can't launch flipbook, requires a viewer node."); 1183 if node.Class() != "Viewer" and node.inputs() == 0: 1184 raise RuntimeError("Can't launch flipbook, this is not a viewer node."); 1185 1186 if not (nuke.canCreateNode("Write")): 1187 nuke.message("Viewer capture is not permitted in Nuke Assist") 1188 return 1189 1190 groupContext = nuke.root() 1191 e = ViewerCaptureDialog(_gViewerCaptureDialogState, groupContext, node) 1192 if (e.showModalDialog() == True): 1193 # Bug 38516 - Be careful about what gets passed to the ViewerCaptureDialogThread, since anything 1194 # created here will be destroyed in a different thread. This can cause problems, such as crashes, 1195 # if a Qt widget gets passed. 1196 captureViewer = e.captureViewer() 1197 thread = ViewerCaptureDialogThread(captureViewer) 1198 thread.start()
1199
1200 -def showFlipbookDialogForSelected():
1201 """Present a dialog that flipbooks the currently selected node.""" 1202 try: 1203 showFlipbookDialog(nuke.selectedNode()) 1204 except ValueError, ve: 1205 raise RuntimeError("Can't launch flipbook, %s." % (ve.args[0]))
1206
1207 -def bboxToTopLeft(height, roi):
1208 """Convert the roi passed from a origin at the bottom left to the top left. 1209 Also replaces the r and t keys with w and h keys. 1210 @param height: the height used to determine the top. 1211 @param roi: the roi with a bottom left origin, must have x, y, r & t keys. 1212 @result dict with x, y, w & h keys""" 1213 topLeftRoi = { 1214 "x": roi["x"], 1215 "y": height - roi["y"] - (roi["t"] - roi["y"]), 1216 "w": roi["r"] - roi["x"], 1217 "h": roi["t"] - roi["y"] } 1218 return topLeftRoi
1219
1220 -def setRenderDialogDefaultOption(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 _gRenderDialogState.saveValue(name, value)
1223
1224 -def setFlipbookDefaultOption(name, value):
1225 """ 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.""" 1226 _gFlipbookDialogState.saveValue(name, value)
1227