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