nukescripts.renderdialog

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  #import traceback
     13
     14  ##############################################################################
     15  # Dialogs
     16  ##############################################################################
     17
     18 -class DialogState:
     19 -  def __init__(self):
     20      self._state = {}
     21
     22 -  def get(self, knob, defaultValue = None):
     23      """Return the given knob's stored last state value.
     24      If none exists, defaultValue is returned.
     25      Values are stored in a dict referenced by knob name, so names must be unique!"""
     26      return self.getValue(knob.name(), defaultValue)
     27
     28 -  def save(self, knob):
     29      """Store the knob's current value as the 'last state' for the next time the dialog is opened.
     30      Values are stored in a dict referenced by knob name, so names must be unique!"""
     31      self.saveValue(knob.name(), knob.value())
     32
     33 -  def setKnob(self, knob, defaultValue = None):
     34      """Convenience method for setting a value straight on a knob."""
     35      knob.setValue(self.get(knob, defaultValue))
     36 -  def saveValue(self, id, value):
     37      """Stores the value with the given id."""
     38      self._state[id] = value
     39 -  def getValue(self, id, defaultValue = None):
     40      """Recalls the value. If it was not set before, it will return the defaultValue."""
     41      return self._state.get(id, defaultValue)
     42
     43  _gRenderDialogState = DialogState()
     44  _gFlipbookDialogState = DialogState()
     45
     46 -class ExecuteDialog(nukescripts.PythonPanel):
     47 -  def _titleString(self):
     48      return "Execute"
     49
     50 -  def _idString(self):
     51      return "uk.co.thefoundry.ExecuteDialog"
     52
     53 -  def _addPreKnobs(self):
     54      """Add knobs that must appear before the render knobs."""
     55      return
     56
     57 -  def _addPostKnobs(self):
     58      """Add knobs that must appear after the render knobs."""
     59      return
     60
     61 -  def _getDefaultViews(self):
     62      oc = nuke.OutputContext()
     63      allViews = [oc.viewname(i) for i in xrange(1, oc.viewcount())]
     64      return " ".join(allViews)
     65
     66 -  def _addViewKnob(self):
     67      """Add knobs for view selection."""
     68      oc = nuke.OutputContext()
     69      if (oc.viewcount() > 2):
     70        self._viewSelection = nuke.MultiView_Knob("multi_view", "Views")
     71        self._viewSelection.fromScript(self._state.get(self._viewSelection, self._getDefaultViews()))
     72        self.addKnob(self._viewSelection)
     73        self._viewSelection.clearFlag(nuke.NO_MULTIVIEW)
     74
     75 -  def addKnob(self, knob):
     76      """Add the knob and make sure it cannot be animated."""
     77      knob.setFlag(nuke.NO_ANIMATION | nuke.NO_MULTIVIEW)
     78      super(ExecuteDialog, self).addKnob(knob)
     79
     80 -  def __init__(self, dialogState, groupContext, nodeSelection = [], exceptOnError = True):
     81      self._state = dialogState
     82      self._nodeSelection = nodeSelection
     83      self._exceptOnError = exceptOnError
     84
     85      nukescripts.PythonPanel.__init__(self, self._titleString(), self._idString(), False)
     86
     87      self._viewers = {}
     88      for n in nuke.allNodes("Viewer", groupContext):
     89        self._viewers[n.name()] = n
     90      self._specialRanges = ["input", "global", "custom"]
     91
     92      self._addPreKnobs()
     93
     94      # Frame range knobs
     95      self._rangeEnum = nuke.Enumeration_Knob( "frame_range", "Frame range", self._specialRanges + self._viewers.keys() )
     96      self._state.setKnob(self._rangeEnum, "input")
     97      self.addKnob( self._rangeEnum )
     98      self._frameRange = nuke.String_Knob( "frame_range_string", "")
     99      self._frameRange.clearFlag(nuke.STARTLINE)
    100      if self._rangeEnum.value() == "custom":
    101        self._state.setKnob(self._frameRange, str(nuke.root().frameRange()))
    102      else:
    103        self._setFrameRangeFromSource(self._rangeEnum.value())
    104
    105      self.addKnob(self._frameRange)
    106
    107      # Proxy
    108      self._useProxy = nuke.Boolean_Knob("use_proxy", "Use proxy")
    109      self._useProxy.setFlag(nuke.STARTLINE)
    110      self._state.setKnob(self._useProxy, nuke.root().proxy())
    111      self.addKnob(self._useProxy)
    112
    113      self._addPostKnobs()
    114
    115      self._continueOnError = nuke.Boolean_Knob("continue", "Continue on error")
    116      self._state.setKnob(self._continueOnError, True)
    117      self._continueOnError.setFlag(nuke.STARTLINE)
    118      self.addKnob(self._continueOnError)
    119
    120      self._addViewKnob()
    121
    122 -  def knobChanged( self, knob ):
    123      self._state.save(knob)
    124      if (knob == self._frameRange):
    125        self._rangeEnum.setValue("custom")
    126        self._state.save(self._rangeEnum)
    127      if (knob == self._rangeEnum):
    128        self._setFrameRangeFromSource(knob.value())
    129        self._state.save(self._frameRange)
    130
    131 -  def _setFrameRangeFromSource(self, source):
    132      if (source == "input"):
    133        try:
    134          activeInput = nuke.activeViewer().activeInput()
    135          self._frameRange.setValue(str(nuke.activeViewer().node().input(activeInput).frameRange()))
    136        except:
    137          self._frameRange.setValue(str(nuke.root().frameRange()))
    138      elif (source == "global"):
    139        self._frameRange.setValue(str(nuke.root().frameRange()))
    140      elif (source == "custom"):
    141        pass
    142      else:
    143        self._frameRangeFromViewer(source);
    144
    145 -  def _frameRangeFromViewer( self, viewer ):
    146      """"Set the framerange knob to have the framerange from the given viewer."""
    147      viewerRange = str(self._viewers[viewer].knob("frame_range").value())
    148      if viewerRange == "":
    149        viewerRange = str(self._viewers[viewer].playbackRange())
    150      self._frameRange.setValue(viewerRange)
    151
    152 -  def _selectedViews(self):
    153      try:
    154        return self._viewSelection.value().split()
    155      except AttributeError:
    156        # If we didn't add the view selection knob, there should be just the one view.
    157        return [nuke.OutputContext().viewname(1)]
    158
    159 -  def addToPane(self):
    160      nukescripts.PythonPanel.addToPane(self, pane = nuke.thisPane())
    161
    162 -  def run(self):
    163      frame_ranges = nuke.FrameRanges(self._frameRange.value().split(','))
    164      views = self._selectedViews()
    165      rootProxyMode = nuke.root().proxy()
    166      try:
    167        nuke.Undo().disable()
    168        nuke.root().setProxy(self._useProxy.value())
    169        nuke.executeMultiple(self._nodeSelection, frame_ranges, views, continueOnError = self._continueOnError.value())
    170      except RuntimeError, e:
    171        if self._exceptOnError or e.args[0][0:9] != "Cancelled":   # TO DO: change this to an exception type
    172          raise
    173      finally:
    174        nuke.root().setProxy(rootProxyMode)
    175        nuke.Undo().enable()
    176
    177 -class RenderDialog(ExecuteDialog):
    178 -  def _titleString(self):
    179      return "Render"
    180
    181 -  def _idString(self):
    182      return "uk.co.thefoundry.RenderDialog"
    183
    184 -  def __init__(self, dialogState, groupContext, nodeSelection = [], exceptOnError = True):
    185      ExecuteDialog.__init__(self, dialogState, groupContext, nodeSelection, exceptOnError)
    186
    187 -  def _addPostKnobs(self):
    188      # Background render stuff
    189      self._bgRender = nuke.Boolean_Knob("bg_render", "Render in background")
    190      self._state.setKnob(self._bgRender, False)
    191      self._bgRender.setFlag(nuke.STARTLINE)
    192      self.addKnob(self._bgRender)
    193      self._numThreads = nuke.Int_Knob("num_threads", "Thread limit")
    194      self._numThreads.setVisible(self._bgRender.value())
    195      self._state.setKnob(self._numThreads, max(nuke.NUM_CPUS / 2, 1))
    196      self.addKnob(self._numThreads)
    197      self._maxMem = nuke.String_Knob("max_memory", "Memory limit")
    198      self._state.setKnob(self._maxMem, str(max(nuke.memory("max_usage") / 2097152, 16)) + "M")
    199      self._maxMem.setVisible(self._bgRender.value())
    200      self.addKnob(self._maxMem)
    201
    202
    203 -  def _getBackgroundLimits(self):
    204      return {
    205        "maxThreads": self._numThreads.value(),
    206        "maxCache": self._maxMem.value() }
    207
    208 -  def knobChanged( self, knob ):
    209      ExecuteDialog.knobChanged(self, knob)
    210
    211      if (knob == self._bgRender):
    212        self._numThreads.setVisible(self._bgRender.value())
    213        self._maxMem.setVisible(self._bgRender.value())
    214
    215 -  def isBackgrounded(self):
    216      """Return whether the backgroun rendering option is enabled."""
    217      return self._bgRender.value()
    218
    219 -  def run(self):
    220      frame_ranges = nuke.FrameRanges(self._frameRange.value().split(','))
    221      views = self._selectedViews()
    222      rootProxyMode = nuke.root().proxy()
    223      try:
    224        nuke.Undo().disable()
    225        nuke.root().setProxy(self._useProxy.value())
    226        if (self.isBackgrounded()):
    227          nuke.executeBackgroundNuke(nuke.EXE_PATH, self._nodeSelection,
    228            frame_ranges, views, self._getBackgroundLimits(), continueOnError = self._continueOnError.value())
    229        else:
    230          nuke.executeMultiple(self._nodeSelection, frame_ranges, views, continueOnError = self._continueOnError.value())
    231      except RuntimeError, e:
    232        if self._exceptOnError or e.args[0][0:9] != "Cancelled":   # TO DO: change this to an exception type
    233          raise
    234      finally:
    235        nuke.root().setProxy(rootProxyMode)
    236        nuke.Undo().enable()
    237
    238 -class FlipbookDialog( RenderDialog ):
    239 -  def _titleString( self ):
    240      return "Flipbook"
    241
    242 -  def _idString( self ):
    243      return "uk.co.thefoundry.FlipbookDialog"
    244
    245 -  def __init__(self, dialogState, groupContext, node, takeNodeSettings):
    246      # Init attributes
    247      self._node = node
    248      self._takeNodeSettings = takeNodeSettings
    249
    250      # init super
    251      RenderDialog.__init__(self, dialogState, groupContext)
    252
    253      # Override the initial frame range value
    254      self._state.setKnob(self._rangeEnum, "input")
    255      self._setFrameRangeFromSource(self._rangeEnum.value())
    256
    257      if self._takeNodeSettings:
    258        self._viewerForSettings.setValue(node.name())
    259        self.knobChanged(self._viewerForSettings)
    260
    261 -  def _addPreKnobs( self ):
    262      self._flipbookEnum = nuke.Enumeration_Knob( "flipbook", "Flipbook", flipbooking.gFlipbookFactory.getNames() )
    263      self._state.setKnob(self._flipbookEnum, "FrameCycler")
    264      self.addKnob( self._flipbookEnum )
    265      self._viewerForSettings = nuke.Enumeration_Knob("viewer_settings", "Take settings from", ["-"] + self._viewers.keys())
    266      if not self._takeNodeSettings:
    267        self._viewerForSettings.setValue("-")
    268      self.addKnob(self._viewerForSettings)
    269
    270      self._defaultValues = nuke.PyScript_Knob("default", "Defaults")
    271      self.addKnob(self._defaultValues)
    272
    273      # Region of Interest knobs
    274      self._useRoi = nuke.Boolean_Knob("use_roi", "Enable ROI")
    275      self._useRoi.setFlag(nuke.STARTLINE)
    276      self._state.setKnob(self._useRoi, False)
    277      self.addKnob(self._useRoi)
    278      self._roi = nuke.BBox_Knob("roi", "Region of Interest")
    279      self._state.setKnob(self._roi, (0, 0, 0, 0))
    280      self.addKnob(self._roi)
    281      self._roi.setVisible(self._useRoi.value())
    282
    283      # Channel knobs
    284      self._channels = nuke.Channel_Knob( "channels_knob", "Channels")
    285      if self._node.Class() == "Write":
    286        self._channels.setValue(self._node.knob("channels").value())
    287      else:
    288        self._state.setKnob(self._channels, "rgba")
    289      self._channels.setFlag(nuke.STARTLINE | nuke.NO_CHECKMARKS)
    290      self.addKnob( self._channels )
    291
    292 -  def _addPostKnobs( self ):
    293      super(FlipbookDialog, self)._addPostKnobs()
    294      # Misc knobs
    295      self._cleanup = nuke.Boolean_Knob("cleanup", "Delete existing temporary files")
    296      self._cleanup.setFlag(nuke.STARTLINE)
    297      self._state.setKnob(self._cleanup, True)
    298      self.addKnob(self._cleanup)
    299
    300      # LUT knobs
    301      self._luts = nuke.Enumeration_Knob("lut", "LUT", nuke.ViewerProcess.registeredNames())
    302      if self._takeNodeSettings:
    303        self._state.setKnob(self._luts, self._lutFromViewer(self._viewerForSettings.value()))
    304      else:
    305        self._state.setKnob(self._luts, self._lutFromViewer())
    306      self.addKnob(self._luts)
    307
    308      self._burnInLUT = nuke.Boolean_Knob("burnin", "Burn in the LUT")
    309      self._state.setKnob(self._burnInLUT, False)
    310      self.addKnob(self._burnInLUT)
    311
    312      # Audio knobs
    313      audioList = []
    314      audioList.append( "None" )
    315      for node in nuke.allNodes("AudioRead"):
    316        audioList.append( node.name() )
    317      self._audioSource = nuke.Enumeration_Knob( "audio", "Audio", audioList )
    318      self.addKnob( self._audioSource )
    319
    320
    321 -  def _getDefaultViews(self):
    322      return nuke.activeViewer().view()
    323
    324 -  def _addViewKnob(self):
    325      oc = nuke.OutputContext()
    326      self._views = [oc.viewname(i) for i in xrange(1, oc.viewcount())]
    327      if (oc.viewcount() > 2):
    328        supportedViews = self._selectedFlipbook().capabilities()["maximumViews"]
    329        if (int(supportedViews) > 1):
    330          self._viewSelection = nuke.MultiView_Knob("views", "Views")
    331        else:
    332          self._viewSelection = nuke.OneView_Knob("views", "View", self._views)
    333        activeView = nuke.activeViewer().view()
    334        if activeView == "":
    335          activeView = self._views[0]
    336        self._state.setKnob(self._viewSelection, activeView)
    337        self.addKnob(self._viewSelection)
    338        self._viewSelection.clearFlag(nuke.NO_MULTIVIEW)
    339
    340 -  def _selectedFlipbook(self):
    341      return flipbooking.gFlipbookFactory.getApplication(self._flipbookEnum.value())
    342
    343 -  def _lutFromViewer(self, viewerName = ""):
    344      try:
    345        if viewerName == "":
    346          return nuke.ViewerProcess.node().knob("current").value()
    347        else:
    348          return nuke.ViewerProcess.node(viewer=viewerName).knob("current").value()
    349      except AttributeError:
    350        return "None"
    351
    352 -  def _isViewerSettingKnob(self, knob):
    353      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
    354
    355 -  def _setKnobAndStore(self, knob, val):
    356      knob.setValue(val)
    357      self._state.save(knob)
    358
    359 -  def knobChanged(self, knob):
    360      RenderDialog.knobChanged(self, knob)
    361      if knob == self._defaultValues:
    362        self._setKnobAndStore(self._useRoi, False)
    363        self._setKnobAndStore(self._roi, (0, 0, 0, 0))
    364        self._roi.setVisible(False)
    365        self._maxMem.setVisible(False)
    366        self._numThreads.setVisible(False)
    367        self._setKnobAndStore(self._viewerForSettings, "-")
    368        self._setKnobAndStore(self._channels, "rgba")
    369        self._setKnobAndStore(self._useProxy, False)
    370        self._setKnobAndStore(self._frameRange, str(nuke.root().frameRange()))
    371        self._setKnobAndStore(self._rangeEnum, "input")
    372        self._setKnobAndStore(self._continueOnError, True)
    373        self._setKnobAndStore(self._bgRender, False)
    374        self._setKnobAndStore(self._luts, "sRGB")
    375        self._setKnobAndStore(self._burnInLUT, False)
    376        self._setKnobAndStore(self._cleanup, True)
    377        self._setKnobAndStore(self._maxMem, str(max(nuke.memory("max_usage") / 2097152, 16)) + "M")
    378        self._setKnobAndStore(self._numThreads, max(nuke.NUM_CPUS / 2, 1))
    379      elif (knob == self._viewerForSettings):
    380        if self._viewerForSettings.value() != "-":
    381          viewer = self._viewers[self._viewerForSettings.value()]
    382          self._setKnobAndStore(self._useRoi, viewer.roiEnabled())
    383          roi = viewer.roi()
    384          if roi != None:
    385            self._roi.fromDict(roi)
    386            self._state.save(self._roi)
    387          self._channels.fromScript(viewer.knob("channels").toScript())
    388          self._state.save(self._channels)
    389          self._setKnobAndStore(self._useProxy, nuke.root().proxy())
    390          self._frameRangeFromViewer(viewer.name())
    391          self._state.save(self._frameRange)
    392          self._setKnobAndStore(self._rangeEnum, viewer.name())
    393          self._roi.setVisible(self._useRoi.value())
    394          self._setKnobAndStore(self._luts, self._lutFromViewer(viewer.name()))
    395      elif (knob == self._useRoi):
    396        self._roi.setVisible(self._useRoi.value())
    397      elif self._isViewerSettingKnob(knob):
    398        self._viewerForSettings.setValue("-")
    399        self._state.save(self._viewerForSettings)
    400      elif knob == self._luts:
    401        self._burnInLUT.setEnabled(self._luts.value() != "None")
    402
    403 -  def _deleteTemporaries(self):
    404      """Delete all the files in the range to be rendered."""
    405      temporariesPath = self._getIntermediatePath()
    406      temporariesPath = temporariesPath .replace("%V", "%s")
    407      for r in nuke.FrameRanges(self._frameRange.value().split(',')):
    408        deleteRange = xrange(r.minFrame(), r.maxFrame() + 1)
    409
    410        for v in self._selectedViews():
    411          for i in deleteRange:
    412            if len(self._selectedViews()) > 1:
    413              f = temporariesPath % (i, v,)
    414            else:
    415              f = temporariesPath % i
    416
    417            if os.access(f, os.F_OK):
    418              os.remove(f)
    419
    420 -  def _getIntermediateFileType(self):
    421      return _gFlipbookDialogState.getValue('intermediateFormat', 'exr')
    422
    423 -  def _getIntermediatePath(self):
    424      """Get the path for the temporary files. May be filled in using printf syntax."""
    425      flipbooktmp=""
    426      if flipbooktmp == "":
    427        try:
    428          flipbooktmp = self._selectedFlipbook().cacheDir()
    429        except:
    430          try:
    431            flipbooktmp = os.environ["NUKE_DISK_CACHE"]
    432          except:
    433            flipbooktmp = nuke.value("preferences.DiskCachePath")
    434
    435      if len(self._selectedViews()) > 1:
    436        flipbookFileNameTemp = "nuke_tmp_flip.%04d.%V." + self._getIntermediateFileType()
    437      else:
    438        flipbookFileNameTemp = "nuke_tmp_flip.%04d." + self._getIntermediateFileType()
    439      flipbooktmpdir = os.path.join(flipbooktmp, "flipbook")
    440      if not os.path.exists(flipbooktmpdir):
    441        os.mkdir(flipbooktmpdir)
    442
    443      if not os.path.isdir(flipbooktmpdir):
    444        raise RuntimeError("%s already exists and is not a directory, please delete before flipbooking again" % flipbooktmpdir)
    445      flipbooktmp = os.path.join(flipbooktmpdir, flipbookFileNameTemp)
    446
    447      if nuke.env['WIN32']:
    448        flipbooktmp = re.sub(r"\\", "/", str(flipbooktmp))
    449      return flipbooktmp
    450
    451 -  def _requireIntermediateNode(self):
    452      if self._node.Class() == "Read" or self._node.Class() == "Write":
    453        flipbookToRun = flipbooking.gFlipbookFactory.getApplication(self._flipbookEnum.value())
    454        flipbookCapabilities = flipbookToRun.capabilities()
    455
    456        # Check if we can read it in directly..
    457        filePath = nuke.filename(self._node)
    458        # There might be a prefix that overrides the extension, if so, this will
    459        # confuse the flipbook probably, so just create a render.
    460        if ':' in filePath:
    461          readerPrefix = filePath.split(':')[0]
    462          if len(readerPrefix) > 1: # 1 is a drive letter
    463            return True
    464        fileExtension = os.path.splitext(filePath)[1].lower()[1:]
    465        flipbookSupportsFileType = fileExtension in flipbookCapabilities.get("fileTypes", [])
    466        if not flipbookSupportsFileType:
    467          return True
    468
    469        # Not all flipbooks can handle weird channels
    470        flipbookSupportsArbitraryChannels = flipbookCapabilities.get("arbitraryChannels", False)
    471        if self._channels.value() not in set(["rgb", "rgba", "alpha"]) and not flipbookSupportsArbitraryChannels:
    472          return True
    473        channelKnob = self._node.knob("channels")
    474        if channelKnob != None and channelKnob.value() != self._channels.value():
    475          return True
    476
    477        if self._burnInLUT.value() and self._burnInLUT.enabled():
    478          return True
    479
    480        return False
    481      else:
    482        return True
    483
    484 -  def _createIntermediateNode(self):
    485      """Create a write node to render out the current node so that output may be used for flipbooking."""
    486      flipbooktmp = self._getIntermediatePath()
    487
    488      fieldname = "file"
    489      if self._useProxy.value():
    490        fieldname = "proxy"
    491
    492      fixup = nuke.createNode("Group", "tile_color 0xff000000", inpanel = False)
    493      with fixup:
    494        fixup.setName("Flipbook")
    495        inputNode = nuke.createNode("Input", inpanel = False)
    496        shuffle = nuke.createNode("Shuffle", inpanel = False)
    497        shuffle.knob("in").setValue(self._channels.value())
    498        write = nuke.createNode("Write", fieldname+" {"+flipbooktmp+"}", inpanel = False)
    499        write.knob('file_type').setValue(self._getIntermediateFileType())
    500        write.knob('views').fromScript(" ".join(self._selectedViews()))
    501
    502        if self._getIntermediateFileType() == "exr":
    503          write.knob('compression').setValue("B44")
    504        if self._burnInLUT.value():
    505          lut = self._getLUT()
    506          if lut != "None":
    507            write.knob('colorspace').setValue(lut)
    508        outputNode = nuke.createNode("Output", inpanel = False)
    509      #If called on a Viewer connect fixup node to the one immediately above if exists.
    510      if self._node.Class() == "Viewer":
    511        fixup.setInput(0, self._node.input(int(nuke.knob(self._node.fullName()+".input_number"))))
    512      else:
    513        fixup.setInput(0, self._node)
    514
    515      try:
    516        # Throws exception on render failure
    517        if (self.isBackgrounded()):
    518          nuke.executeBackgroundNuke(nuke.EXE_PATH, [write],
    519            nuke.FrameRanges(self._frameRange.value().split(',')), self._selectedViews(),
    520            self._getBackgroundLimits(), self._continueOnError.value(),
    521            self._flipbookEnum.value(), self._getOptions(write))
    522        else:
    523          nuke.executeMultiple((write,),
    524              nuke.FrameRanges(self._frameRange.value().split(',')), self._selectedViews(),
    525              self._continueOnError.value())
    526      except RuntimeError, msg:
    527        if msg.args[0][0:9] == "Cancelled":
    528          splitMsg = string.split(msg.args[0])
    529
    530          msg = """Render did not complete, do you want to show the completed range?
    531  Frame range %s contains %s frames but only %s finished.""" % (self._frameRange.value(), splitMsg[3], splitMsg[1])
    532          if nuke.ask(msg) == False:
    533            nuke.delete(fixup)
    534            fixup = None
    535        else:
    536          nuke.delete(fixup)
    537          fixup = None
    538          nuke.message("Flipbook render failed:\n%s" % (msg.args[0],))
    539  #    except BaseException, be:
    540  #      print be.__class__.__name__
    541  #      traceback.print_exc()
    542      finally:
    543        return fixup
    544
    545 -  def _getLUT(self):
    546      return self._luts.value()
    547
    548 -  def _getAudio(self):
    549      nukeNode = nuke.toNode( self._audioSource.value() )
    550      ret = ""
    551      if nukeNode != None:
    552        ret = nukeNode["file"].getEvaluatedValue()
    553
    554      # if the platform is not windows and we have spaces in the audio name then we cant use audio
    555      # as framecycler does not support spaces in the name
    556      if nuke.env['WIN32'] == False:
    557        if ret != "":
    558          # check if we have spaces
    559          if (' ' in ret) == True :
    560            nuke.message("Framecycler does not currently support spaces in the audio file name, to enable audio please ensure the audio file contains no spaces.")
    561            ret = ""
    562      return ret
    563
    564 -  def _getOptions(self, nodeToFlipbook):
    565      options = {
    566      }
    567
    568      try:
    569        options['pixelAspect'] = float(nuke.value(nodeToFlipbook.name()+".pixel_aspect"))
    570      except:
    571        pass
    572
    573      try:
    574        f = nodeToFlipbook.format()
    575        options['dimensions'] = { 'width' : f.width(), 'height' : f.height() }
    576      except:
    577        pass
    578
    579      # LUT
    580      if not self._burnInLUT.value():
    581        inputColourspace = "linear"
    582        outputColourspace = "linear"
    583        # Check if we have a different than linear input
    584        if self._node.Class() == "Read" or self._node.Class() == "Write":
    585          lut = self._node.knob("colorspace").value()
    586          # Might be in the format of "default (foo)", if so, get at "foo".
    587          if lut[:7] == "default":
    588            lut = lut[9:-1]
    589          inputColourspace = lut
    590
    591        # Check our output
    592        lut = self._getLUT()
    593        if lut != "None":
    594          outputColourspace = lut
    595
    596        if inputColourspace == outputColourspace:
    597          options["lut"] = inputColourspace
    598        else:
    599          options["lut"] = inputColourspace + "-" + outputColourspace
    600      # AUDIO
    601      audioTrack = self._getAudio()
    602      if audioTrack != "":
    603        options["audio"] = audioTrack
    604
    605      # ROI
    606      if self._useRoi.value():
    607        roi = self._roi.toDict()
    608        if (roi["r"] - roi["x"] > 0) and (roi["t"] - roi["y"] > 0):
    609          options["roi"] = bboxToTopLeft(int(nuke.value(nodeToFlipbook.name()+".actual_format.height")), roi)
    610
    611      return options
    612
    613 -  def run(self):
    614      flipbookToRun = flipbooking.gFlipbookFactory.getApplication(self._flipbookEnum.value())
    615      if (flipbookToRun):
    616        if not os.access(flipbookToRun.path(), os.X_OK):
    617          raise RuntimeError("%s cannot be executed (%s)." % (flipbookToRun.name(), flipbookToRun.path(),) )
    618
    619        nodeToFlipbook = None
    620        rootProxyMode = nuke.root().proxy()
    621        try:
    622          # Need this to prevent Bug 5295
    623          nuke.Undo().disable()
    624          nuke.root().setProxy(self._useProxy.value())
    625
    626          if self._cleanup.value():
    627            self._deleteTemporaries()
    628          calledOnNode = self._node
    629          if self._node.Class() == "Viewer":
    630            self._node = self._node.input(int(self._node.knob("input_number").value()))
    631
    632          runFlipbook = False
    633          # In many cases we need to create a temporary node and render that.
    634          if not self._requireIntermediateNode():
    635            nodeToFlipbook = self._node
    636            runFlipbook = True
    637          else:
    638            nodeToFlipbook = self._createIntermediateNode()
    639            runFlipbook = not self._bgRender.value()
    640
    641          if nodeToFlipbook and runFlipbook:
    642            filename = nuke.filename(nodeToFlipbook)
    643            if filename is None or filename == "":
    644              raise RuntimeError("Cannot run a flipbook on '%s', expected to find a filename and there was none." % (nodeToFlipbook.fullName(),))
    645            flipbookToRun.run(filename,
    646                nuke.FrameRanges(self._frameRange.value().split(',')),
    647                self._selectedViews(), self._getOptions(nodeToFlipbook))
    648        finally:
    649          if self._node != nodeToFlipbook:
    650            nuke.delete(nodeToFlipbook)
    651          nuke.root().setProxy(rootProxyMode)
    652          nuke.Undo().enable()
    653      else:
    654          raise RuntimeError("No flipbook called " + self._flipbookEnum.value() + " found. Was it deregistered while the dialog was open?")
    655
    656 -def showExecuteDialog(nodesToExecute, exceptOnError = True):
    657    """Present a dialog that executes the given list of nodes."""
    658    groupContext = nuke.root()
    659    d = ExecuteDialog(_gRenderDialogState, groupContext, nodesToExecute, exceptOnError)
    660    if d.showModalDialog() == True:
    661      d.run()
    662
    663 -def showRenderDialog(nodesToRender, exceptOnError = True):
    664    """Present a dialog that renders the given list of nodes."""
    665    groupContext = nuke.root()
    666    d = RenderDialog(_gRenderDialogState, groupContext, nodesToRender, exceptOnError)
    667    if d.showModalDialog() == True:
    668      d.run()
    669
    670 -def showFlipbookDialog(node, takeNodeSettings = False):
    671    """Present a dialog that flipbooks the given node."""
    672    if node is None:
    673      raise RuntimeError("Can't launch flipbook, require a node.");
    674    if node.Class() == "Viewer" and node.inputs() == 0:
    675      raise RuntimeError("Can't launch flipbook, there is nothing connected to the viewed input.");
    676
    677    groupContext = nuke.root()
    678
    679    e = FlipbookDialog(_gFlipbookDialogState, groupContext, node, takeNodeSettings)
    680    if (e.showModalDialog() == True):
    681      e.run()
    682
    683 -def showFlipbookDialogForSelected():
    684    """Present a dialog that flipbooks the currently selected node."""
    685    try:
    686      showFlipbookDialog(nuke.selectedNode())
    687    except ValueError, ve:
    688      raise RuntimeError("Can't launch flipbook, %s." % (ve.args[0]))
    689
    690 -def bboxToTopLeft(height, roi):
    691    """Convert the roi passed from a origin at the bottom left to the top left.
    692       Also replaces the r and t keys with w and h keys.
    693       @param height: the height used to determine the top.
    694       @param roi: the roi with a bottom left origin, must have x, y, r & t keys.
    695       @result dict with x, y, w & h keys"""
    696    topLeftRoi = {
    697        "x": roi["x"],
    698        "y": height - roi["y"] - (roi["t"] - roi["y"]),
    699        "w": roi["r"] - roi["x"],
    700        "h": roi["t"] - roi["y"] }
    701    return topLeftRoi
    702
    703 -def setRenderDialogDefaultOption(name, value):
    704    """ 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."""
    705    _gRenderDialogState.saveValue(name, value)
    706
    707 -def setFlipbookDefaultOption(name, value):
    708    """ 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."""
    709    _gFlipbookDialogState.saveValue(name, value)
    710