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   
 16  #import traceback 
 17   
 18  ############################################################################## 
 19  # Dialogs 
 20  ############################################################################## 
 21   
22 -class DialogState:
23 - def __init__(self):
24 self._state = {}
25
26 - def get(self, knob, defaultValue = None):
27 """Return the given knob's stored last state value. 28 If none exists, defaultValue is returned. 29 Values are stored in a dict referenced by knob name, so names must be unique!""" 30 return self.getValue(knob.name(), defaultValue)
31
32 - def save(self, knob):
33 """Store the knob's current value as the 'last state' for the next time the dialog is opened. 34 Values are stored in a dict referenced by knob name, so names must be unique!""" 35 self.saveValue(knob.name(), knob.value())
36
37 - def setKnob(self, knob, defaultValue = None):
38 """Convenience method for setting a value straight on a knob.""" 39 knob.setValue(self.get(knob, defaultValue))
40 - def saveValue(self, id, value):
41 """Stores the value with the given id.""" 42 self._state[id] = value
43 - def getValue(self, id, defaultValue = None):
44 """Recalls the value. If it was not set before, it will return the defaultValue.""" 45 return self._state.get(id, defaultValue)
46 47 _gRenderDialogState = DialogState() 48 _gFlipbookDialogState = DialogState() 49 _gViewerCaptureDialogState = DialogState() 50
51 -class ExecuteDialog(nukescripts.PythonPanel):
52 - def _titleString(self):
53 return "Execute"
54
55 - def _idString(self):
56 return "uk.co.thefoundry.ExecuteDialog"
57
58 - def _addPreKnobs(self):
59 """Add knobs that must appear before the render knobs.""" 60 return
61
62 - def _addPostKnobs(self):
63 """Add knobs that must appear after the render knobs.""" 64 return
65
66 - def _addTrailingKnobs(self):
67 """Add knobs that must appear at the very end.""" 68 return
69
70 - def _getDefaultViews(self):
71 oc = nuke.OutputContext() 72 allViews = [oc.viewname(i) for i in xrange(1, oc.viewcount())] 73 return " ".join(allViews)
74
75 - def _addViewKnob(self):
76 """Add knobs for view selection.""" 77 oc = nuke.OutputContext() 78 if (oc.viewcount() > 2): 79 self._viewSelection = nuke.MultiView_Knob("multi_view", "Views") 80 self._viewSelection.fromScript(self._state.get(self._viewSelection, self._getDefaultViews())) 81 self.addKnob(self._viewSelection) 82 self._viewSelection.clearFlag(nuke.NO_MULTIVIEW)
83
84 - def addKnob(self, knob):
85 """Add the knob and make sure it cannot be animated.""" 86 knob.setFlag(nuke.NO_ANIMATION | nuke.NO_MULTIVIEW) 87 super(ExecuteDialog, self).addKnob(knob)
88
89 - def __init__(self, dialogState, groupContext, nodeSelection = [], exceptOnError = True):
90 self._state = dialogState 91 self._nodeSelection = nodeSelection 92 self._exceptOnError = exceptOnError 93 94 nukescripts.PythonPanel.__init__(self, self._titleString(), self._idString(), False) 95 96 self._viewers = {} 97 for n in nuke.allNodes("Viewer", groupContext): 98 self._viewers[n.name()] = n 99 self._specialRanges = ["input", "global", "custom"] 100 101 self._addPreKnobs() 102 103 # Frame range knobs 104 self._rangeEnum = nuke.Enumeration_Knob( "frame_range", "Frame range", self._specialRanges + self._viewers.keys() ) 105 self._state.setKnob(self._rangeEnum, "input") 106 self.addKnob( self._rangeEnum ) 107 108 self._frameRange = nuke.String_Knob( "frame_range_string", "") 109 self._frameRange.clearFlag(nuke.STARTLINE) 110 if self._rangeEnum.value() == "custom": 111 self._state.setKnob(self._frameRange, str(nuke.root().frameRange())) 112 else: 113 self._setFrameRangeFromSource(self._rangeEnum.value()) 114 115 self.addKnob(self._frameRange) 116 117 # Proxy 118 self._useProxy = nuke.Boolean_Knob("use_proxy", "Use proxy") 119 self._useProxy.setFlag(nuke.STARTLINE) 120 self._state.setKnob(self._useProxy, nuke.root().proxy()) 121 self.addKnob(self._useProxy) 122 123 self._addPostKnobs() 124 125 self._continueOnError = nuke.Boolean_Knob("continue", "Continue on error") 126 self._state.setKnob(self._continueOnError, True) 127 self._continueOnError.setFlag(nuke.STARTLINE) 128 self.addKnob(self._continueOnError) 129 130 self._addViewKnob() 131 132 self._addTrailingKnobs()
133
134 - def knobChanged( self, knob ):
135 self._state.save(knob) 136 if (knob == self._frameRange): 137 self._rangeEnum.setValue("custom") 138 self._state.save(self._rangeEnum) 139 if (knob == self._rangeEnum): 140 self._setFrameRangeFromSource(knob.value()) 141 self._state.save(self._frameRange)
142
143 - def _setFrameRangeFromSource(self, source):
144 if (source == "input"): 145 try: 146 activeInput = nuke.activeViewer().activeInput() 147 self._frameRange.setValue(str(nuke.activeViewer().node().input(activeInput).frameRange())) 148 except: 149 self._frameRange.setValue(str(nuke.root().frameRange())) 150 elif (source == "global"): 151 self._frameRange.setValue(str(nuke.root().frameRange())) 152 elif (source == "custom"): 153 pass 154 else: 155 self._frameRangeFromViewer(source);
156
157 - def _frameRangeFromViewer( self, viewer ):
158 """"Set the framerange knob to have the framerange from the given viewer.""" 159 viewerRange = str(self._viewers[viewer].knob("frame_range").value()) 160 if viewerRange == "": 161 viewerRange = str(self._viewers[viewer].playbackRange()) 162 self._frameRange.setValue(viewerRange)
163
164 - def _selectedViews(self):
165 try: 166 return self._viewSelection.value().split() 167 except AttributeError: 168 # If we didn't add the view selection knob, there should be just the one view. 169 return [nuke.OutputContext().viewname(1)]
170
171 - def addToPane(self):
173
174 - def run(self):
175 frame_ranges = nuke.FrameRanges(self._frameRange.value().split(',')) 176 views = self._selectedViews() 177 rootProxyMode = nuke.root().proxy() 178 try: 179 nuke.Undo().disable() 180 nuke.root().setProxy(self._useProxy.value()) 181 nuke.executeMultiple(self._nodeSelection, frame_ranges, views, continueOnError = self._continueOnError.value()) 182 except RuntimeError, e: 183 if self._exceptOnError or e.args[0][0:9] != "Cancelled": # TO DO: change this to an exception type 184 raise 185 finally: 186 nuke.root().setProxy(rootProxyMode) 187 nuke.Undo().enable()
188
189 -class RenderDialog(ExecuteDialog):
190 - def _titleString(self):
191 return "Render"
192
193 - def _idString(self):
194 return "uk.co.thefoundry.RenderDialog"
195
196 - def __init__(self, dialogState, groupContext, nodeSelection = [], exceptOnError = True):
197 ExecuteDialog.__init__(self, dialogState, groupContext, nodeSelection, exceptOnError)
198
199 - def _addPreKnobs( self ):
200 if self.isTimelineWrite(): 201 self._frameServerRender = nuke.Boolean_Knob("frameserver_render", "Render to timeline") 202 self._state.setKnob( self._frameServerRender, True) 203 self._frameServerRender.setFlag(nuke.STARTLINE) 204 self.addKnob(self._frameServerRender)
205
206 - def _addPostKnobs(self):
207 # Background render stuff 208 self._bgRender = nuke.Boolean_Knob("bg_render", "Render in background") 209 self._state.setKnob(self._bgRender, False) 210 self._bgRender.setFlag(nuke.STARTLINE) 211 self.addKnob(self._bgRender) 212 self._numThreads = nuke.Int_Knob("num_threads", "Thread limit") 213 self._numThreads.setVisible(self._bgRender.value()) 214 self._state.setKnob(self._numThreads, max(nuke.NUM_CPUS / 2, 1)) 215 self.addKnob(self._numThreads) 216 self._maxMem = nuke.String_Knob("max_memory", "Memory limit") 217 self._state.setKnob(self._maxMem, str(max(nuke.memory("max_usage") / 2097152, 16)) + "M") 218 self._maxMem.setVisible(self._bgRender.value()) 219 self.addKnob(self._maxMem)
220 221
222 - def _getBackgroundLimits(self):
223 return { 224 "maxThreads": self._numThreads.value(), 225 "maxCache": self._maxMem.value() }
226
227 - def knobChanged( self, knob ):
228 ExecuteDialog.knobChanged(self, knob) 229 230 bgRenderVisible = False 231 frameServerRender = False 232 try: 233 frameServerRender = self._frameServerRender.value() 234 except: 235 pass 236 237 if not frameServerRender and self._bgRender.value(): 238 bgRenderVisible = True 239 240 self._bgRender.setVisible( not frameServerRender ) 241 self._numThreads.setVisible(bgRenderVisible) 242 self._maxMem.setVisible(bgRenderVisible) 243 244 if frameServerRender: 245 self._rangeEnum.setValue( "global" ) 246 self._setFrameRangeFromSource(self._rangeEnum.value()) 247 self._rangeEnum.setEnabled( False ) 248 self._frameRange.setEnabled( False ) 249 self._useProxy.setVisible( False ) 250 self._continueOnError.setVisible( False ) 251 else: 252 self._rangeEnum.setEnabled( True ) 253 self._frameRange.setEnabled( True ) 254 self._useProxy.setVisible( True ) 255 self._continueOnError.setVisible( True )
256 257
258 - def isBackgrounded(self):
259 """Return whether the background rendering option is enabled.""" 260 return self._bgRender.value()
261
262 - def isTimelineWrite(self):
263 264 if not nuke.env['studio']: 265 return False 266 267 if len(self._nodeSelection) > 1 or len(self._nodeSelection) < 1: 268 return False 269 270 write = self._nodeSelection[0] 271 272 if write == nuke.root(): 273 ## must be a render of all 'write' nodes, this is tricky as there may be other executable nodes apart from write nodes 274 ## lets assume the write nodes are write, writegeo, particlecache, and diskcache 275 276 # there is a bug here however as there may be groups they are executable which will be skipped right now 277 278 writeNodes = nuke.allNodes( 'Write' ) 279 writeNodes.extend( nuke.allNodes( 'WriteGeo') ) 280 writeNodes.extend( nuke.allNodes( 'ParticleCache') ) 281 writeNodes.extend( nuke.allNodes( 'DiskCache') ) 282 283 if len(writeNodes) > 1: 284 return False 285 286 if len(writeNodes) > 0: 287 write = writeNodes[0] 288 289 timelineWriteNode = None 290 291 try: 292 from foundry.frameserver.nuke.workerapplication import GetWriteNode 293 timelineWriteNode = GetWriteNode() 294 except: 295 pass 296 297 if not timelineWriteNode: 298 return False 299 300 if timelineWriteNode.name() != write.name(): 301 return False 302 303 ## double check that this script is actually in a timeline 304 try: 305 from hiero.ui import isInAnyProject 306 return isInAnyProject( nuke.scriptName() ) 307 except: 308 pass 309 310 return False
311
312 - def run(self):
313 if self.isTimelineWrite() and self._frameServerRender.value(): 314 from hiero.ui.nuke_bridge.nukestudio import scriptSaveAndReRender 315 scriptSaveAndReRender() 316 return 317 318 frame_ranges = nuke.FrameRanges(self._frameRange.value().split(',')) 319 views = self._selectedViews() 320 rootProxyMode = nuke.root().proxy() 321 try: 322 nuke.Undo().disable() 323 nuke.root().setProxy(self._useProxy.value()) 324 if (self.isBackgrounded()): 325 nuke.executeBackgroundNuke(nuke.EXE_PATH, self._nodeSelection, 326 frame_ranges, views, self._getBackgroundLimits(), continueOnError = self._continueOnError.value()) 327 else: 328 nuke.executeMultiple(self._nodeSelection, frame_ranges, views, continueOnError = self._continueOnError.value()) 329 except RuntimeError, e: 330 if self._exceptOnError or e.args[0][0:9] != "Cancelled": # TO DO: change this to an exception type 331 raise 332 finally: 333 nuke.root().setProxy(rootProxyMode) 334 nuke.Undo().enable()
335
336 -class FlipbookDialog( RenderDialog ):
337 - def _titleString( self ):
338 return "Flipbook"
339
340 - def _idString( self ):
341 return "uk.co.thefoundry.FlipbookDialog"
342
343 - def __init__(self, dialogState, groupContext, node, takeNodeSettings):
344 # Init attributes 345 self._node = node 346 self._takeNodeSettings = takeNodeSettings 347 self._customKnobs = [] 348 349 # init super 350 RenderDialog.__init__(self, dialogState, groupContext) 351 352 # Override the initial frame range value 353 self._state.setKnob(self._rangeEnum, "input") 354 self._setFrameRangeFromSource(self._rangeEnum.value()) 355 356 if self._takeNodeSettings: 357 self._viewerForSettings.setValue(node.name()) 358 self.knobChanged(self._viewerForSettings)
359
360 - def _addPreKnobs( self ):
361 self._flipbookEnum = nuke.Enumeration_Knob( "flipbook", "Flipbook", flipbooking.gFlipbookFactory.getNames() ) 362 self._state.setKnob(self._flipbookEnum, "Default") 363 self.addKnob( self._flipbookEnum ) 364 self._viewerForSettings = nuke.Enumeration_Knob("viewer_settings", "Take settings from", ["-"] + self._viewers.keys()) 365 if not self._takeNodeSettings: 366 self._viewerForSettings.setValue("-") 367 self.addKnob(self._viewerForSettings) 368 369 self._defaultValues = nuke.PyScript_Knob("default", "Defaults") 370 self.addKnob(self._defaultValues) 371 372 # Region of Interest knobs 373 self._useRoi = nuke.Boolean_Knob("use_roi", "Enable ROI") 374 self._useRoi.setFlag(nuke.STARTLINE) 375 self._state.setKnob(self._useRoi, False) 376 self.addKnob(self._useRoi) 377 self._roi = nuke.BBox_Knob("roi", "Region of Interest") 378 self._state.setKnob(self._roi, (0, 0, 0, 0)) 379 self.addKnob(self._roi) 380 self._roi.setVisible(self._useRoi.value()) 381 382 # Channel knobs 383 self._channels = nuke.Channel_Knob( "channels_knob", "Channels") 384 if self._node.Class() == "Write": 385 self._channels.setValue(self._node.knob("channels").value()) 386 else: 387 self._state.setKnob(self._channels, "rgba") 388 self._channels.setFlag(nuke.STARTLINE | nuke.NO_CHECKMARKS) 389 self.addKnob( self._channels )
390
391 - def _addPostKnobs( self ):
392 super(FlipbookDialog, self)._addPostKnobs() 393 # Misc knobs 394 self._cleanup = nuke.Boolean_Knob("cleanup", "Delete existing temporary files") 395 self._cleanup.setFlag(nuke.STARTLINE) 396 self._state.setKnob(self._cleanup, True) 397 self.addKnob(self._cleanup) 398 399 # LUT knobs 400 self._luts = nuke.Enumeration_Knob("lut", "LUT", nuke.ViewerProcess.registeredNames()) 401 if self._takeNodeSettings: 402 self._state.setKnob(self._luts, self._lutFromViewer(self._viewerForSettings.value())) 403 else: 404 self._state.setKnob(self._luts, self._lutFromViewer()) 405 self.addKnob(self._luts) 406 407 self._burnInLUT = nuke.Boolean_Knob("burnin", "Burn in the LUT") 408 self._state.setKnob(self._burnInLUT, False) 409 self.addKnob(self._burnInLUT) 410 411 # Audio knobs 412 audioList = [] 413 audioList.append( "None" ) 414 for node in nuke.allNodes("AudioRead"): 415 audioList.append( node.name() ) 416 self._audioSource = nuke.Enumeration_Knob( "audio", "Audio", audioList ) 417 self._state.setKnob(self._audioSource, audioList[0] ) 418 self.addKnob( self._audioSource )
419
420 - def _addTrailingKnobs(self):
421 self.flipbookKnobs()
422
423 - def flipbookKnobs(self):
424 try: 425 beforeKnobs = self.knobs() 426 flipbookToRun = flipbooking.gFlipbookFactory.getApplication(self._flipbookEnum.value()) 427 flipbookToRun.dialogKnobs(self) 428 afterKnobs = self.knobs() 429 self._customKnobs = list(set(beforeKnobs.values()) ^ set(afterKnobs.values())) 430 except NotImplementedError: 431 pass
432
433 - def _getDefaultViews(self):
434 return nuke.activeViewer().view()
435
436 - def _addViewKnob(self):
437 oc = nuke.OutputContext() 438 self._views = [oc.viewname(i) for i in xrange(1, oc.viewcount())] 439 if (oc.viewcount() > 2): 440 supportedViews = self._selectedFlipbook().capabilities()["maximumViews"] 441 if (int(supportedViews) > 1): 442 self._viewSelection = nuke.MultiView_Knob("views", "Views") 443 else: 444 self._viewSelection = nuke.OneView_Knob("views", "View", self._views) 445 activeView = nuke.activeViewer().view() 446 if activeView == "": 447 activeView = self._views[0] 448 self._state.setKnob(self._viewSelection, activeView) 449 self.addKnob(self._viewSelection) 450 self._viewSelection.clearFlag(nuke.NO_MULTIVIEW)
451
452 - def _selectedFlipbook(self):
453 return flipbooking.gFlipbookFactory.getApplication(self._flipbookEnum.value())
454
455 - def _lutFromViewer(self, viewerName = ""):
456 try: 457 if viewerName == "": 458 return nuke.ViewerProcess.node().knob("current").value() 459 else: 460 return nuke.ViewerProcess.node(viewer=viewerName).knob("current").value() 461 except AttributeError: 462 return "None"
463
464 - def _isViewerSettingKnob(self, knob):
465 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
466
467 - def _setKnobAndStore(self, knob, val):
468 knob.setValue(val) 469 self._state.save(knob)
470
471 - def knobChanged(self, knob):
472 RenderDialog.knobChanged(self, knob) 473 if knob == self._defaultValues: 474 self._setKnobAndStore(self._useRoi, False) 475 self._setKnobAndStore(self._roi, (0, 0, 0, 0)) 476 self._roi.setVisible(False) 477 self._maxMem.setVisible(False) 478 self._numThreads.setVisible(False) 479 self._setKnobAndStore(self._viewerForSettings, "-") 480 self._setKnobAndStore(self._channels, "rgba") 481 self._setKnobAndStore(self._useProxy, False) 482 self._setKnobAndStore(self._frameRange, str(nuke.root().frameRange())) 483 self._setKnobAndStore(self._rangeEnum, "input") 484 self._setKnobAndStore(self._continueOnError, True) 485 self._setKnobAndStore(self._bgRender, False) 486 self._setKnobAndStore(self._luts, "sRGB") 487 self._setKnobAndStore(self._burnInLUT, False) 488 self._setKnobAndStore(self._cleanup, True) 489 self._setKnobAndStore(self._maxMem, str(max(nuke.memory("max_usage") / 2097152, 16)) + "M") 490 self._setKnobAndStore(self._numThreads, max(nuke.NUM_CPUS / 2, 1)) 491 elif (knob == self._viewerForSettings): 492 if self._viewerForSettings.value() != "-": 493 viewer = self._viewers[self._viewerForSettings.value()] 494 self._setKnobAndStore(self._useRoi, viewer.roiEnabled()) 495 roi = viewer.roi() 496 if roi != None: 497 self._roi.fromDict(roi) 498 self._state.save(self._roi) 499 self._channels.fromScript(viewer.knob("channels").toScript()) 500 self._state.save(self._channels) 501 self._setKnobAndStore(self._useProxy, nuke.root().proxy()) 502 self._frameRangeFromViewer(viewer.name()) 503 self._state.save(self._frameRange) 504 self._setKnobAndStore(self._rangeEnum, viewer.name()) 505 self._roi.setVisible(self._useRoi.value()) 506 self._setKnobAndStore(self._luts, self._lutFromViewer(viewer.name())) 507 elif (knob == self._useRoi): 508 self._roi.setVisible(self._useRoi.value()) 509 elif self._isViewerSettingKnob(knob): 510 self._viewerForSettings.setValue("-") 511 self._state.save(self._viewerForSettings) 512 elif knob == self._luts: 513 self._burnInLUT.setEnabled(self._luts.value() != "None") 514 515 if knob == self._flipbookEnum: 516 for k in self._customKnobs: 517 self.removeKnob(k) 518 self.removeKnob(self.okButton) 519 self.removeKnob(self.cancelButton) 520 self._customKnobs = [] 521 self.flipbookKnobs() 522 self._makeOkCancelButton() 523 elif knob in self._customKnobs: 524 try: 525 flipbookToRun = flipbooking.gFlipbookFactory.getApplication(self._flipbookEnum.value()) 526 flipbookToRun.dialogKnobChanged(self, knob) 527 except NotImplementedError: 528 pass
529
530 - def _deleteTemporaries(self):
531 """Delete all the files in the range to be rendered.""" 532 temporariesPath = self._getIntermediatePath() 533 temporariesPath = temporariesPath .replace("%V", "%s") 534 for r in nuke.FrameRanges(self._frameRange.value().split(',')): 535 deleteRange = xrange(r.minFrame(), r.maxFrame() + 1) 536 537 for v in self._selectedViews(): 538 for i in deleteRange: 539 if len(self._selectedViews()) > 1: 540 f = temporariesPath % (i, v,) 541 else: 542 f = temporariesPath % i 543 544 if os.access(f, os.F_OK): 545 os.remove(f)
546
547 - def _getIntermediateFileType(self):
548 return _gFlipbookDialogState.getValue('intermediateFormat', 'exr')
549
550 - def _getIntermediatePath(self):
551 """Get the path for the temporary files. May be filled in using printf syntax.""" 552 flipbooktmp="" 553 if flipbooktmp == "": 554 try: 555 flipbooktmp = self._selectedFlipbook().cacheDir() 556 except: 557 try: 558 flipbooktmp = os.environ["NUKE_DISK_CACHE"] 559 except: 560 flipbooktmp = nuke.value("preferences.DiskCachePath") 561 562 if len(self._selectedViews()) > 1: 563 flipbookFileNameTemp = "nuke_tmp_flip.%04d.%V." + self._getIntermediateFileType() 564 else: 565 flipbookFileNameTemp = "nuke_tmp_flip.%04d." + self._getIntermediateFileType() 566 flipbooktmpdir = os.path.join(flipbooktmp, "flipbook") 567 if not os.path.exists(flipbooktmpdir): 568 os.mkdir(flipbooktmpdir) 569 570 if not os.path.isdir(flipbooktmpdir): 571 raise RuntimeError("%s already exists and is not a directory, please delete before flipbooking again" % flipbooktmpdir) 572 flipbooktmp = os.path.join(flipbooktmpdir, flipbookFileNameTemp) 573 574 if nuke.env['WIN32']: 575 flipbooktmp = re.sub(r"\\", "/", str(flipbooktmp)) 576 return flipbooktmp
577
578 - def _requireIntermediateNode(self, nodeToTest):
579 if nodeToTest.Class() == "Read" or nodeToTest.Class() == "Write": 580 flipbookToRun = flipbooking.gFlipbookFactory.getApplication(self._flipbookEnum.value()) 581 flipbookCapabilities = flipbookToRun.capabilities() 582 583 # Check if we can read it in directly.. 584 filePath = nuke.filename(nodeToTest) 585 # There might be a prefix that overrides the extension, if so, this will 586 # confuse the flipbook probably, so just create a render. 587 if ':' in filePath: 588 readerPrefix = filePath.split(':')[0] 589 if len(readerPrefix) > 1: # 1 is a drive letter 590 return True 591 fileExtension = os.path.splitext(filePath)[1].lower()[1:] 592 flipbookSupportsFileType = fileExtension in flipbookCapabilities.get("fileTypes", []) 593 if not flipbookSupportsFileType: 594 return True 595 596 # Not all flipbooks can handle weird channels 597 flipbookSupportsArbitraryChannels = flipbookCapabilities.get("arbitraryChannels", False) 598 if self._channels.value() not in set(["rgb", "rgba", "alpha"]) and not flipbookSupportsArbitraryChannels: 599 return True 600 channelKnob = nodeToTest.knob("channels") 601 if channelKnob != None and channelKnob.value() != self._channels.value(): 602 return True 603 604 ## 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 605 if self._useRoi.value() and self._useRoi.enabled(): 606 if not flipbookCapabilities.get("roi", False): 607 return True 608 609 if self._burnInLUT.value() and self._burnInLUT.enabled(): 610 return True 611 612 return False 613 else: 614 return True
615
616 - def _createIntermediateNode(self):
617 """Create a write node to render out the current node so that output may be used for flipbooking.""" 618 flipbooktmp = self._getIntermediatePath() 619 620 fieldname = "file" 621 if self._useProxy.value(): 622 fieldname = "proxy" 623 624 fixup = nuke.createNode("Group", "tile_color 0xff000000", inpanel = False) 625 with fixup: 626 fixup.setName("Flipbook") 627 inputNode = nuke.createNode("Input", inpanel = False) 628 shuffle = nuke.createNode("Shuffle", inpanel = False) 629 shuffle.knob("in").setValue(self._channels.value()) 630 if self._useRoi.value(): 631 crop = nuke.createNode( "Crop", inpanel = False ) 632 crop['box'].fromScript( self._roi.toScript() ) 633 write = nuke.createNode("Write", fieldname+" {"+flipbooktmp+"}", inpanel = False) 634 write.knob('file_type').setValue(self._getIntermediateFileType()) 635 write.knob('views').fromScript(" ".join(self._selectedViews())) 636 637 if self._getIntermediateFileType() == "exr": 638 write.knob('compression').setValue("B44") 639 if self._burnInLUT.value(): 640 lut = self._getLUT() 641 if lut != "None": 642 write.knob('colorspace').setValue(lut) 643 outputNode = nuke.createNode("Output", inpanel = False) 644 #If called on a Viewer connect fixup node to the one immediately above if exists. 645 if self._node.Class() == "Viewer": 646 fixup.setInput(0, self._node.input(int(nuke.knob(self._node.fullName()+".input_number")))) 647 else: 648 fixup.setInput(0, self._node) 649 650 try: 651 # Throws exception on render failure 652 if (self.isBackgrounded()): 653 nuke.executeBackgroundNuke(nuke.EXE_PATH, [write], 654 nuke.FrameRanges(self._frameRange.value().split(',')), self._selectedViews(), 655 self._getBackgroundLimits(), self._continueOnError.value(), 656 self._flipbookEnum.value(), self._getOptions(write)) 657 else: 658 nuke.executeMultiple((write,), 659 nuke.FrameRanges(self._frameRange.value().split(',')), self._selectedViews(), 660 self._continueOnError.value()) 661 except RuntimeError, msg: 662 if msg.args[0][0:9] == "Cancelled": 663 splitMsg = string.split(msg.args[0]) 664 665 msg = """Render did not complete, do you want to show the completed range? 666 Frame range %s contains %s frames but only %s finished.""" % (self._frameRange.value(), splitMsg[3], splitMsg[1]) 667 if nuke.ask(msg) == False: 668 nuke.delete(fixup) 669 fixup = None 670 else: 671 nuke.delete(fixup) 672 fixup = None 673 nuke.message("Flipbook render failed:\n%s" % (msg.args[0],)) 674 # except BaseException, be: 675 # print be.__class__.__name__ 676 # traceback.print_exc() 677 finally: 678 return fixup
679
680 - def _getLUT(self):
681 return self._luts.value()
682
683 - def _getAudio(self):
684 nukeNode = nuke.toNode( self._audioSource.value() ) 685 ret = "" 686 if nukeNode != None: 687 ret = nukeNode["file"].getEvaluatedValue() 688 689 return ret
690
691 - def _getOptions(self, nodeToFlipbook):
692 options = { 693 } 694 695 try: 696 options['pixelAspect'] = float(nuke.value(nodeToFlipbook.name()+".pixel_aspect")) 697 except: 698 pass 699 700 try: 701 f = nodeToFlipbook.format() 702 options['dimensions'] = { 'width' : f.width(), 'height' : f.height() } 703 except: 704 pass 705 706 # LUT 707 if not self._burnInLUT.value(): 708 inputColourspace = "linear" 709 outputColourspace = "linear" 710 # Check if we have a different than linear input 711 if self._node.Class() == "Read" or self._node.Class() == "Write": 712 lut = self._node.knob("colorspace").value() 713 # Might be in the format of "default (foo)", if so, get at "foo". 714 if lut[:7] == "default": 715 lut = lut[9:-1] 716 inputColourspace = lut 717 718 # Check our output 719 lut = self._getLUT() 720 if lut != "None": 721 outputColourspace = lut 722 723 if inputColourspace == outputColourspace: 724 options["lut"] = inputColourspace 725 else: 726 options["lut"] = inputColourspace + "-" + outputColourspace 727 # AUDIO 728 audioTrack = self._getAudio() 729 if audioTrack != "": 730 options["audio"] = audioTrack 731 732 # ROI 733 if self._useRoi.value(): 734 roi = self._roi.toDict() 735 if (roi["r"] - roi["x"] > 0) and (roi["t"] - roi["y"] > 0): 736 options["roi"] = bboxToTopLeft(int(nuke.value(nodeToFlipbook.name()+".actual_format.height")), roi) 737 738 739 return options
740
741 - def run(self):
742 flipbookToRun = flipbooking.gFlipbookFactory.getApplication(self._flipbookEnum.value()) 743 if (flipbookToRun): 744 if not os.access(flipbookToRun.path(), os.X_OK): 745 raise RuntimeError("%s cannot be executed (%s)." % (flipbookToRun.name(), flipbookToRun.path(),) ) 746 747 nodeToFlipbook = None 748 rootProxyMode = nuke.root().proxy() 749 try: 750 # Need this to prevent Bug 5295 751 nuke.Undo().disable() 752 nuke.root().setProxy(self._useProxy.value()) 753 754 if self._cleanup.value(): 755 self._deleteTemporaries() 756 757 calledOnNode = self._node 758 if self._node.Class() == "Viewer": 759 self._node = self._node.input(int(self._node.knob("input_number").value())) 760 761 runFlipbook = False 762 # In many cases we need to create a temporary node and render that. 763 if not self._requireIntermediateNode( self._node ): 764 nodeToFlipbook = self._node 765 runFlipbook = True 766 else: 767 nodeToFlipbook = self._createIntermediateNode() 768 runFlipbook = not self._bgRender.value() 769 770 if nodeToFlipbook and runFlipbook: 771 # get default options and extra options needed by the selected 772 # Flipbook application 773 options = self._getOptions( nodeToFlipbook ) 774 extraOptions = flipbookToRun.getExtraOptions( self, nodeToFlipbook ) 775 options.update( extraOptions ) 776 777 flipbookToRun.runFromNode(nodeToFlipbook, 778 nuke.FrameRanges(self._frameRange.value().split(',')), 779 self._selectedViews(), options ) 780 781 finally: 782 if self._node != nodeToFlipbook: 783 nuke.delete(nodeToFlipbook) 784 nuke.root().setProxy(rootProxyMode) 785 nuke.Undo().enable() 786 else: 787 raise RuntimeError("No flipbook called " + self._flipbookEnum.value() + " found. Was it deregistered while the dialog was open?")
788
789 -class ViewerCaptureDialog( FlipbookDialog ):
790 - def _titleString( self ):
791 return "Capture"
792
793 - def _idString( self ):
794 return "uk.co.thefoundry.ViewerCaptureDialog"
795
796 - def __init__(self, dialogState, groupContext, node):
797 798 # init super 799 FlipbookDialog.__init__(self, dialogState, groupContext, node, True) 800 801 self._bgRender.setVisible(False) 802 self._useProxy.setVisible(False) 803 self._continueOnError.setVisible(False) 804 805 self._viewerForSettings.setVisible(False) 806 self._defaultValues.setVisible(False) 807 808 self._useRoi.setVisible(False) 809 self._roi.setVisible(False) 810 811 self._channels.setVisible(False) 812 self._luts.setVisible(False) 813 814 self._audioSource.setVisible(False) 815 self._burnInLUT.setVisible(False) 816 817 try: 818 ## this may not exist, just ignore if not present 819 self._viewSelection.setVisible(False) 820 except: 821 pass 822 823 customWriteActive = node['file'].getValue() != self._getIntermediatePath() and node['file'].getValue() != '' 824 self._customWrite = nuke.Boolean_Knob( 'custom', 'Customise write path' ) 825 self._customWrite.setValue( customWriteActive ) 826 self.addKnob( self._customWrite ) 827 828 self._noFlipbook = nuke.Boolean_Knob( 'write', 'No flipbook' ) 829 self._noFlipbook.setFlag( nuke.STARTLINE ) 830 self._noFlipbook.setVisible( customWriteActive ) 831 self._noFlipbook.setValue( self._state.get( self._noFlipbook, False ) ) 832 self.addKnob( self._noFlipbook ) 833 834 self._file = nuke.File_Knob( 'file', 'Write path' ) 835 defaultPath = self._node['file'].value() 836 if defaultPath == self._getIntermediatePath(): 837 defaultPath = '' 838 self._file.setValue ( self._state.get(self._file, defaultPath ) ) 839 self._file.setVisible( customWriteActive ) 840 841 self.addKnob( self._file )
842
843 - def knobChanged( self, knob ):
844 try: 845 FlipbookDialog.knobChanged(self, knob) 846 if (knob == self._customWrite): 847 self._noFlipbook.setVisible( self._customWrite.value() ) 848 self._file.setVisible( self._customWrite.value() ) 849 if ( not self._customWrite.value() ): 850 self._node['file'].setValue( self._getIntermediatePath() ) 851 elif( knob == self._noFlipbook ): 852 self._flipbookEnum.setEnabled( not self._noFlipbook.value() ) 853 except: 854 pass
855
856 - def _getIntermediateFileType(self):
857 return 'jpg'
858
859 - def captureViewer(self):
860 """ Return an instance of a CaptureViewer class, which when executed captures the viewer. 861 """ 862 flipbook = flipbooking.gFlipbookFactory.getApplication(self._flipbookEnum.value()) 863 if flipbook and not os.access(flipbook.path(), os.X_OK): 864 raise RuntimeError("%s cannot be executed (%s)." % (flipbook.name(), flipbook.path(),) ) 865 866 # build up the args 867 frameRange = self._frameRange.value() 868 viewer = self._node 869 selectedViews = self._selectedViews() 870 defaultWritePath = self._getIntermediatePath() 871 customWritePath = self._file.value() if self._customWrite.value() else "" 872 doFlipbook = not self._noFlipbook.value() 873 doCleanup = self._cleanup.value() 874 875 return captureViewer.CaptureViewer(flipbook, frameRange, viewer, selectedViews, defaultWritePath, customWritePath, doFlipbook, doCleanup)
876
877 -def showExecuteDialog(nodesToExecute, exceptOnError = True):
878 """Present a dialog that executes the given list of nodes.""" 879 groupContext = nuke.root() 880 d = ExecuteDialog(_gRenderDialogState, groupContext, nodesToExecute, exceptOnError) 881 if d.showModalDialog() == True: 882 d.run() 883
884 -def showRenderDialog(nodesToRender, exceptOnError = True):
885 """Present a dialog that renders the given list of nodes.""" 886 groupContext = nuke.root() 887 d = RenderDialog(_gRenderDialogState, groupContext, nodesToRender, exceptOnError) 888 if d.showModalDialog() == True: 889 d.run() 890
891 -def showFlipbookDialog(node, takeNodeSettings = False):
892 """Present a dialog that flipbooks the given node.""" 893 if node is None: 894 raise RuntimeError("Can't launch flipbook, require a node."); 895 if node.Class() == "Viewer" and node.inputs() == 0: 896 raise RuntimeError("Can't launch flipbook, there is nothing connected to the viewed input."); 897 898 if not (nuke.canCreateNode("Write")): 899 nuke.message("Flipbooking is not permitted in Nuke Assist") 900 return 901 902 groupContext = nuke.root() 903 904 e = FlipbookDialog(_gFlipbookDialogState, groupContext, node, takeNodeSettings) 905 if (e.showModalDialog() == True): 906 e.run()
907 908 # because the capture button could have been triggered via a progress bar in update_handles 909 # we defer the capture run until the main event loop is pumped
910 -class ViewerCaptureDialogThread(Thread):
911 - def __init__(self, captureViewer):
914
915 - def run(self):
916 ## only runs the dialog capture function when we are outside update_handles 917 utils.executeInMainThreadWithResult( self.captureViewer, )
918
919 -def showViewerCaptureDialog(node):
920 if node is None: 921 raise RuntimeError("Can't launch flipbook, requires a viewer node."); 922 if node.Class() != "Viewer" and node.inputs() == 0: 923 raise RuntimeError("Can't launch flipbook, this is not a viewer node."); 924 925 if not (nuke.canCreateNode("Write")): 926 nuke.message("Viewer capture is not permitted in Nuke Assist") 927 return 928 929 groupContext = nuke.root() 930 e = ViewerCaptureDialog(_gViewerCaptureDialogState, groupContext, node) 931 if (e.showModalDialog() == True): 932 # Bug 38516 - Be careful about what gets passed to the ViewerCaptureDialogThread, since anything 933 # created here will be destroyed in a different thread. This can cause problems, such as crashes, 934 # if a Qt widget gets passed. 935 captureViewer = e.captureViewer() 936 thread = ViewerCaptureDialogThread(captureViewer) 937 thread.start()
938
939 -def showFlipbookDialogForSelected():
940 """Present a dialog that flipbooks the currently selected node.""" 941 try: 942 showFlipbookDialog(nuke.selectedNode()) 943 except ValueError, ve: 944 raise RuntimeError("Can't launch flipbook, %s." % (ve.args[0]))
945
946 -def bboxToTopLeft(height, roi):
947 """Convert the roi passed from a origin at the bottom left to the top left. 948 Also replaces the r and t keys with w and h keys. 949 @param height: the height used to determine the top. 950 @param roi: the roi with a bottom left origin, must have x, y, r & t keys. 951 @result dict with x, y, w & h keys""" 952 topLeftRoi = { 953 "x": roi["x"], 954 "y": height - roi["y"] - (roi["t"] - roi["y"]), 955 "w": roi["r"] - roi["x"], 956 "h": roi["t"] - roi["y"] } 957 return topLeftRoi
958
959 -def setRenderDialogDefaultOption(name, value):
960 """ 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.""" 961 _gRenderDialogState.saveValue(name, value)
962
963 -def setFlipbookDefaultOption(name, value):
964 """ 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.""" 965 _gFlipbookDialogState.saveValue(name, value)
966