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