def _add_callbacks(self): """ Add callbacks to watch for certain events: """ nuke.addOnUserCreate(self._setOCIOColorspaceContext, nodeClass="OCIOColorSpace") nuke.addOnCreate(self._setOCIODisplayContext, nodeClass="OCIODisplay") nuke.addOnCreate(self._warningNoCameraColorspace, nodeClass='Root')
def addCallbackOnUserCreate(self, func, nodeClass=None, group=None): """Executed whenever a node is created by the user. Not called when loading existing scripts, pasting nodes, or undoing a delete. """ self._addNukeCallbackGroup(group) self.windowInstance()['callback'][group]['onUserCreate'][func].add(nodeClass) if not self.__windowHidden: if nodeClass is None: nuke.addOnUserCreate(func) else: nuke.addOnUserCreate(func, nodeClass=nodeClass)
toolbar = nuke.menu('Nodes') myMenu = toolbar.addMenu('cTz', icon='script_folder.png') myMenu.addCommand('V_EdgeMatte', 'nuke.createNode("V_EdgeMatte")', icon='V_EdgeMatte.png') myMenu.addCommand('SliceTool', 'nuke.createNode("SliceTool")') myMenu.addCommand('akromatism_stRub', 'nuke.createNode("akromatism_stRub")') myMenu.addCommand('lenskernelFFT', 'nuke.createNode("LensKernelFFT_v01")') myMenu.addCommand('DespillMadness', 'nuke.createNode("DespillMadness")') myMenu.addCommand('glass', 'nuke.createNode("Glass")') myMenu.addCommand('P_Matte', 'nuke.createNode("P_Matte")') myMenu.addCommand('SynthEyesLensDistortion', 'nuke.createNode("SynthEyesLensDistortion")') myMenu.addCommand('Coc Depth of Field', 'nuke.createNode("CoCDoF")') myMenu.addCommand('ColExpand', 'nuke.createNode("ColExpand")') myMenu.addCommand('BadPixels', 'nuke.createNode("badpixels")') ### Run a command everytime a node of a specific type is created # set the value of a new framehold node to the currentframe. nuke.addOnUserCreate( lambda: nuke.thisNode()['first_frame'].setValue(nuke.frame()), nodeClass='FrameHold') # set the label of a new camera to show the current focal length nuke.addOnUserCreate( lambda: nuke.thisNode()['label'].setValue('F[expr round([value focal])]'), nodeClass='Camera') nuke.addOnUserCreate( lambda: nuke.thisNode()['label'].setValue('F[expr round([value focal])]'), nodeClass='Camera2')
def instance_toggled(instance, new_value, old_value): instance.data["instanceToggled"](instance, new_value) api.register_callback("instanceToggled", instance_toggled) # Check frame range is locked when reading, since startup locking doesn't work def modify_read_node(): if not nuke.root()["lock_range"].getValue(): print "Locking frame range." nuke.root()["lock_range"].setValue(True) nuke.addOnUserCreate(modify_read_node, nodeClass="Read") # Nuke callback for modifying the Write nodes on creation def modify_write_node(): # Setting the file path file_path = ("[python {nuke.script_directory()}]/workspace/[python " "{nuke.thisNode().name()}]/[python {os.path.splitext(" "os.path.basename(nuke.scriptName()))[0]}]/[python {" "os.path.splitext(os.path.basename(nuke.scriptName()))[0]}]_" "[python {nuke.thisNode().name()}].%04d.exr") nuke.thisNode()["file"].setValue(file_path) # Setting the file type
nnp.addNotificationPanel( ) # This adds the notification panel in the default location (main menu) # nnp.addNotificationPanel(custom_menu=nuke.menu('Nodes')) # This would add it in another Nuke menu. # Alternatively, one could decide not to add the notification panel in a menu and implement their own panel. # Create an example callback to trigger notifications # Do not keep this in your code, it is only meant as an example implementation, def node_created_callback(): node = nuke.thisNode() if node.Class() == "Grade": nnp.info("Grade Node Created", "Congratulations, you created a Grade node.") elif node.Class() == "ColorCorrect": nnp.warning( "CC Node Created", "Congratulations, you created a Color Correct node.", "If you can see this notification, it probably means that you did not make your own implementation " "of the Nuke notification panel. This tools needs to be customized for your needs." ) elif node.Class() == "Blur": nnp.error( "Blur Node Created", "Congratulations, you created a Blur node.", "If you can see this notification, it probably means that you did not make your own implementation " "of the Nuke notification panel. This tools needs to be customized for your needs." ) nuke.addOnUserCreate(node_created_callback)
def start(): nuke.addOnUserCreate(lambda: on_backdrop_user_created(nuke.thisNode()), nodeClass='BackdropNode') nuke.addOnCreate(lambda: on_backdrop_created(nuke.thisNode()), nodeClass='BackdropNode')
nuke.knobDefault('Merge2.label',"Mix: [value mix]") nuke.knobDefault('VectorBlur2.label', "Channels: [value channels]\nUV: [value uv]\nmotionblur: [value scale]") nuke.knobDefault('Roto.cliptype','no clip') nuke.knobDefault('RotoPaint.cliptype','no clip') nuke.knobDefault('Tracker4.adjust_for_luminance_changes','1') nuke.knobDefault('Transform.shutteroffset', "centered") nuke.knobDefault('Crop.crop','0') nuke.knobDefault('VectorBlur2.uv', "motion") nuke.knobDefault('ScanlineRender.shutteroffset', "centered") nuke.knobDefault('CornerPin2D.shutteroffset', "centered") nuke.knobDefault('TimeBlur.shutteroffset', "centered") nuke.knobDefault('TransformMasked.shutteroffset', "centered") nuke.knobDefault('MotionBlur2D.shutteroffset', "centered") nuke.knobDefault('MotionBlur3D.shutteroffset', "centered") nuke.knobDefault('Card3D.shutteroffset', "centered") nuke.addOnUserCreate(lambda:nuke.thisNode()['reference_frame'].setValue(nuke.frame()), nodeClass='Tracker4') nuke.addOnUserCreate(lambda:nuke.thisNode()['first_frame'].setValue(nuke.frame()), nodeClass='FrameHold') # -------------------------------------------------------------- # KEYBOARD SHORTCUTS ::::::::::::::::::::::::::::::::::::::::: # -------------------------------------------------------------- nuke.menu('Nodes').addCommand("Transform/Tracker", "nuke.createNode('Tracker4')", "ctrl+shift+t", icon="Tracker.png", shortcutContext=2) nuke.menu('Nodes').addCommand("Other/Backdrop", "nuke.createNode('BackdropNode')", "shift+b", icon="Backdrop.png", shortcutContext=2) nuke.menu('Nodes').addCommand("Time/FrameHold", "nuke.createNode('FrameHold')", "ctrl+shift+f", icon="FrameHold.png", shortcutContext=2) nuke.menu('Nodes').addCommand("Merge/Premult", "nuke.createNode('Premult')", "shift+p", icon="Premult.png", shortcutContext=2) nuke.menu('Nuke').addCommand('Edit/Paste', 'paste_selected.paste_selected()', 'ctrl+v') # -------------------------------------------------------------- # PYTHON SCRIPTS :::::::::::::::::::::::::::::::::::::::::::::: # --------------------------------------------------------------
## Add Favorites directories nuke.addFavoriteDir('Job Server', jobServer ) if job: nuke.addFavoriteDir('Job', job) if seq: nuke.addFavoriteDir('Sequence', seq) if shot: nuke.addFavoriteDir('Shot', shot) toolbar = nuke.toolbar("Nodes") ### END FAVORITES SETUP ### ### BEGIN DEFAULTS SETUP ### nuke.addOnUserCreate(firstFrameEval, nodeClass = 'FrameHold') nuke.addOnUserCreate(guiOn, nodeClass = 'DiskCache') shuffleLabel = "<b>[value in]" nuke.knobDefault( 'Shuffle.label', shuffleLabel ) nuke.knobDefault( 'EXPTool.mode', 'Stops' ) # Node Colors nuke.knobDefault( 'Transform.tile_color', '1278560767.0' ) ### END DEFAULTS SETUP ### ### BEGIN LUMA GIZMO SETUP ### ## LUMA Pictures gizmo collector if __name__ == '__main__': # Just in case they didn't use the supplied init.py gizManager = globals().get('gizManager', None)
def addNukeCallBacks(self): try: nuke.addOnUserCreate(self.refreshListOfNodes) nuke.addOnDestroy(self.refreshListOfNodes) except: pass
file_path += "[python {os.path.splitext(os.path.basename(" file_path += "nuke.scriptName()))[0]}].%04d.exr" nuke.thisNode()["file"].setValue(file_path) # Setting the file type nuke.thisNode()["file_type"].setValue("exr") # Setting metadata nuke.thisNode()["metadata"].setValue("all metadata") # Enable create directories if it exists. # Older version of Nuke does not have this option. if "create_directories" in nuke.thisNode().knobs(): nuke.thisNode()["create_directories"].setValue(True) nuke.addOnUserCreate(modify_write_node, nodeClass="Write") # Adding ftrack assets if import is available. try: imp.find_module("ftrack_connect") imp.find_module("ftrack_connect_nuke") import ftrack_assets ftrack_assets.register_assets() import ftrack_init ftrack_init.init() except ImportError as error: print "Could not find ftrack modules: " + str(error)
elif nClass == "NoOp": n['note_font'].setValue("Bebas Neue") n['note_font_size'].setValue(18) elif nClass == "CameraTracker1_0": n['analysisRange'].setValue("Analysis Range") n['analysisStart'].setValue(nuke.root().firstFrame()) n['analysisStop'].setValue(nuke.root().lastFrame()) n['lensDistortionType'].setValue("Unknown Lens") elif nClass == "Read": n['label'].setValue("[file dirname [knob file]]") else: pass nuke.addOnUserCreate(customizeNodeOnUserCreate) def uniquifyWrites(): """ Make sure Write names are not of the generic form "Write1","Write2", etc. BUG: We hook this function to addUpdateUI because customizing node name at addOnUserCreate crashes Nuke! """ n = nuke.thisNode() if re.search(r"^Write\d+$",n.name()): suffix = "".join(sample(string.ascii_letters,6)).upper() n['name'].setValue("Write_"+suffix) nuke.addOnCreate(uniquifyWrites,(),{},"Write") def writeReadingStatus():
Mask.knob('operation').setValue('divide') nuke.menu("Nodes").addCommand("Merge/Merges/Divide", "Divide()", icon="Merge.png") ###################################################################################################################################################### ###################################################################################################################################################### def FrameHoldCurFrame(): fh = nuke.thisNode() fh.knob('first_frame').setValue(nuke.frame()) nuke.addOnUserCreate(FrameHoldCurFrame, nodeClass='FrameHold') ###################################################################################################################################################### ###################################################################################################################################################### ###################################################################################################################################################### ###################################################################################################################################################### def updateFrameRange(): for readNodes in nuke.selectedNodes(): name = readNodes.knob('file').getValue().split('/')[-1].split('%')[0] fileType = readNodes.knob('file').getValue().split('/')[-1].split( '.')[-1] path = '/'.join(
'''Run the panel script and add it as a tab into the pane it is called from''' myPanel = SearchReplacePanel.SearchReplacePanel() return myPanel.addToPane() #THIS LINE WILL ADD THE NEW ENTRY TO THE PANE MENU nuke.menu('Pane').addCommand('SearchReplace', addSRPanel) # CREATE A READ NODE AND OPEN THE "DB" TAB def customRead(): n = nuke.createNode( 'Read' ) n['PipeL'].setFlag( 0 ) return n nuke.menu( 'Nodes' ).addCommand( 'Image/ReadPipel', customRead, 'Shift+r' ) nuke.addOnUserCreate( nuk.general.read.createVersionKnobs, nodeClass='Read' ) nuke.addKnobChanged( nuk.general.read.updateVersionKnob, nodeClass='Read' ) nuke.addOnScriptLoad( nuk.general.read.checkVersions ) nuke.addOnScriptSave( nuk.general.read.checkVersions ) """ import nuk.general.read import nuke nuke.removeOnScriptLoad(nuk.general.read.checkVersions) nuke.removeOnScriptSave(nuk.general.read.checkVersions) """ def mergeColor(): n = nuke.thisNode() k = nuke.thisKnob() if k.name() == "mix":
if nuke.selectedNodes(): sNode = nuke.selectedNodes()[0] n.setInput(0, sNode) last_position = (sNode.xpos(), sNode.ypos()) def afterCreate(): time.sleep(0.05) if last_position: n.setXYpos(last_position[0], last_position[1] + 80) constant.setXYpos(n.xpos() - 132, n.ypos()) n.setInput(3, None) threading.Thread(None, afterCreate).start() nuke.addOnUserCreate(addRotoConstant, (), {}, "RotoPaint") def nk_get_roto_from_color(): """ Reveal Roto SS Shift+v """ def colorSum(node): color = node["color"].value() if isinstance(color, list): return sum([int(i * 10000) for i in color][0:3]) else: return color
####################################################################################### ####################################################################################### def plusFramehold(): This = nuke.thisNode() tab = nuke.Tab_Knob('plus', 'plus') button = nuke.PyScript_Knob("STCF", "Set To Current Frame") This.addKnob(tab) This.addKnob(button) This.knob('first_frame').setValue(nuke.frame()) This['STCF'].setCommand( "pfh = nuke.thisNode().knob('first_frame').setValue(nuke.frame())") nuke.addOnUserCreate(plusFramehold, nodeClass="FrameHold") ####################################################################################### ####################################################################################### def plusRead(): This = nuke.thisNode() tab = nuke.Tab_Knob('plus', 'plus') line1 = nuke.Text_Knob("PlusControl", "Plus Control") PRXbutton = nuke.PyScript_Knob("PRX", "Create proxy") OPNbutton = nuke.PyScript_Knob("OPN", "Open Directory") OPNbutton.setFlag(nuke.STARTLINE) rltvbutton = nuke.PyScript_Knob("rltv", "Relative") rltvbutton.setFlag(nuke.STARTLINE) obslbutton = nuke.PyScript_Knob("obsl", "obsolute")
def start(): nuke.addOnUserCreate(auto_label_helper)
import nuke import os from nodes.DA_WriteMovieSlices import DA_WriteMovieSlices gizmos = '%s/gizmos' % os.path.dirname(os.path.abspath(__file__)) toolbar = nuke.toolbar('Nodes') da_menu = toolbar.addMenu('DataArena') da_menu.addCommand('DA_WriteMovieSlices', 'nuke.createNode("%s/DA_WriteMovieSlices")' % gizmos) nuke.addOnUserCreate(DA_WriteMovieSlices.onUserCreateCallback)
def populate_ftrack(self): import nuke import legacy from nukescripts import panels from ftrack_connect_nuke.connector import Connector # Check if QtWebKit or QWebEngine is avaliable. from FnAssetAPI.ui.toolkit import is_webwidget_supported has_webwidgets = is_webwidget_supported() Connector.registerAssets() # wrappers for initializing the widgets with # the correct connector object def wrapImportAssetDialog(*args, **kwargs): from ftrack_connect.ui.widget.import_asset import FtrackImportAssetDialog return FtrackImportAssetDialog(connector=Connector()) def wrapAssetManagerDialog(*args, **kwargs): from ftrack_connect.ui.widget.asset_manager import FtrackAssetManagerDialog return FtrackAssetManagerDialog(connector=Connector()) # Populate the ui nukeMenu = nuke.menu("Nuke") ftrackMenu = nukeMenu.addMenu("&ftrack") ftrackMenu.addSeparator() # add ftrack publish node to the menu ftrackMenu.addCommand('Create Publish Node', lambda: legacy.createFtrackPublish()) ftrackMenu.addSeparator() globals()['ftrackImportAssetClass'] = wrapImportAssetDialog panels.registerWidgetAsPanel( '{0}.{1}'.format(__name__, 'ftrackImportAssetClass'), 'ftrackImportAsset', 'ftrackDialogs.ftrackImportAssetDialog') ftrackMenu.addSeparator() ftrackMenu.addCommand( 'Import Asset', 'pane = nuke.getPaneFor("Properties.1");' 'panel = nukescripts.restorePanel("ftrackDialogs.ftrackImportAssetDialog");' 'panel.addToPane(pane)') globals()['ftrackAssetManagerDialogClass'] = wrapAssetManagerDialog # Create the asset manager dialog entry in the menu panels.registerWidgetAsPanel( '{0}.{1}'.format(__name__, 'ftrackAssetManagerDialogClass'), 'ftrackAssetManager', 'ftrackDialogs.ftrackAssetManagerDialog') ftrackMenu.addCommand( 'Asset Manager', 'pane = nuke.getPaneFor("Properties.1");' 'panel = nukescripts.restorePanel("ftrackDialogs.ftrackAssetManagerDialog");' 'panel.addToPane(pane)') if has_webwidgets: def wrapAssetInfoDialog(*args, **kwargs): from ftrack_connect_nuke.ui.widget.info_view import AssetInfoView return AssetInfoView(bridge=self._bridge) globals()['ftrackAssetInfoDialogClass'] = wrapAssetInfoDialog # Create the crew dialog entry in the menu panels.registerWidgetAsPanel( '{0}.{1}'.format(__name__, 'ftrackAssetInfoDialogClass'), 'ftrackAssetInfo', 'ftrackDialogs.ftrackAssetInfoDialog') ftrackMenu.addCommand( 'Asset Info', 'pane = nuke.getPaneFor("Properties.1");' 'panel = nukescripts.restorePanel("ftrackDialogs.ftrackAssetInfoDialog");' 'panel.addToPane(pane)') ftrackMenu.addSeparator() if has_webwidgets: from ftrack_connect_foundry.ui.info_view import WorkingTaskInfoView as _WorkingTaskInfoView from ftrack_connect_foundry.ui.tasks_view import TasksView as _TasksView # Add Web Views located in the ftrack_connect_foundry package to the # menu for easier access. for widget in [_TasksView, _WorkingTaskInfoView]: ftrackMenu.addCommand( widget.getDisplayName(), 'pane = nuke.getPaneFor("Properties.1");' 'panel = nukescripts.restorePanel("{identifier}");' 'panel.addToPane(pane)'.format( identifier=widget.getIdentifier())) ftrackMenu.addSeparator() # Add new entries in the ftrack menu. ftrackMenu.addSeparator() if has_webwidgets: from ftrack_connect_nuke.ui.widget.publish_gizmo import GizmoPublisherDialog ftrackMenu.addCommand('Publish gizmo', GizmoPublisherDialog) # Add ftrack publish node toolbar = nuke.toolbar("Nodes") ftrackNodesMenu = toolbar.addMenu("ftrack", icon="ftrack_logo.png") ftrackNodesMenu.addCommand('ftrackPublish', lambda: legacy.createFtrackPublish()) # Set calbacks def asset_info_menu_switch(): '''Enable and disable asset info depending on selection.''' this_node = nuke.thisNode() # Do not continue if selection is not node. if not isinstance(this_node, nuke.Node): return try: is_ftrack = this_node.knob('assetVersionId') except ValueError: is_ftrack = False nuke_menu = nuke.menu('Nuke') menu_item = nuke_menu.findItem('&ftrack') asset_info_menu = menu_item.findItem('Asset Info') if has_webwidgets and asset_info_menu: if is_ftrack: asset_info_menu.setEnabled(True) else: asset_info_menu.setEnabled(False) nuke.addKnobChanged(asset_info_menu_switch) # other callbacks nuke.addOnScriptLoad(legacy.refAssetManager) nuke.addOnScriptLoad(legacy.scan_for_new_assets) nuke.addOnUserCreate(legacy.addFtrackComponentField, nodeClass='Write') nuke.addOnUserCreate(legacy.addFtrackComponentField, nodeClass='WriteGeo') nuke.addOnUserCreate(legacy.addFtrackComponentField, nodeClass='Read') nuke.addKnobChanged(legacy.ftrackPublishKnobChanged, nodeClass="Group") nuke.addOnCreate(legacy.ftrackPublishHieroInit) # Set default values from environments. start_frame = os.environ.get('FS', 0) end_frame = os.environ.get('FE', 100) FnAssetAPI.logging.debug( 'Setting start frame : {}'.format(start_frame)) nuke.knob('root.first_frame', str(start_frame)) FnAssetAPI.logging.debug('Setting end frame : {}'.format(end_frame)) nuke.knob('root.last_frame', str(end_frame))
def coreFunction(self): coreLayout = QtWidgets.QVBoxLayout() tertiary_layout = QtWidgets.QHBoxLayout() label_cwd = QtWidgets.QLabel() label_cwd.setText("Current Working Directory =") tertiary_layout.addWidget(label_cwd) field_cwd = QtWidgets.QLineEdit(label_cwd) field_cwd.setText(self.SCRIPT_DIR) field_cwd.setFont(self.italicFont) field_cwd.setStyleSheet('color: rgb(128, 128, 128)') field_cwd.setReadOnly(1) tertiary_layout.addWidget(field_cwd) coreLayout.addLayout(tertiary_layout) #--- BEGIN TABS ---# #initializes the 'tabBar' 'QtWidgets.QTabWidget()' object, and adds both 'tab_readNodes' and 'tab_writeNodes' to it as 'QtWidgets.QWidget()' objects tabBar = QtWidgets.QTabWidget() self.tab_readNodes = QtWidgets.QWidget() self.tab_writeNodes = QtWidgets.QWidget() tabBar.addTab(self.tab_readNodes, 'Read Nodes') tabBar.addTab(self.tab_writeNodes, 'Write Nodes') #creates 2 instances of 'QtWidgets.QVBoxLayout' within both the 'tab_readNodes' and 'tab_writeNodes' objects based on the current working Nuke script self.tab_readNodes.layout = QtWidgets.QVBoxLayout() self.tab_writeNodes.layout = QtWidgets.QVBoxLayout() #--- BEGIN READ NODES TABLE ---# #initializes the 'table_readNodes' 'QtWidgets.QTableWidget()' object with default parameters self.table_readNodes = QtWidgets.QTableWidget() self.table_readNodes.setColumnCount(3) self.table_readNodes.setHorizontalHeaderLabels( ['Absolute Pathnames', 'Relative Pathnames', 'Disable/Enable']) self.table_readNodes.setWordWrap(0) self.table_readNodes.setRowCount(0) #calls the 'generateReadsData()' private method that will populate the 'table_readNodes' object based on the current Nuke working script self.generateTable_reads() #try: # #removes the custom 'nuke.onCreate()' callback that was created within th the 'pathConverter_window' class. This prevents duplicate callbacks from being created. # nuke.removeOnUserCreate(self.generateTable_reads) # print "TRY statement was succesful." #except: # print "TRY statement has encountered an error. Now printing the EXCEPTION." # nuke.addOnUserCreate(self.generateTable_reads) #if self.generateTable_reads in nuke.onUserCreates.values(): # nuke.removeOnUserCreate(self.generateTable_reads) #nuke.addOnUserCreate(self.generateTable_reads) nuke.removeOnUserCreate(self.myFunction, nodeClass='Read') nuke.removeOnDestroy(self.myFunction, nodeClass='Read') nuke.addOnUserCreate(self.myFunction, nodeClass='Read') nuke.addOnDestroy(self.myFunction, nodeClass='Read') #if self.myFunction in nuke.onUserCreates['Read']: # print str(self.myFunction) + ' exists within the nuke.addOnUserCreates dictionary.' # nuke.removeOnUserCreate(self.myFunction) #----------------------- self.tab_readNodes.layout.addWidget(self.table_readNodes) self.tab_readNodes.setLayout(self.tab_readNodes.layout) #----------------------- #within the 'self.table_readNodes' object, whenever a selection change occurs, invoke the 'self.__changeSelection_reads' function self.table_readNodes.itemSelectionChanged.connect( self.__changeSelection_reads) #--- END READ NODES TABLE ---# #--- WRITE NODES TABLE ---# #initializes the 'table_writeNodes' 'QtWidgets.QTableWidget()' object with default parameters self.table_writeNodes = QtWidgets.QTableWidget() self.table_writeNodes.setColumnCount(3) self.table_writeNodes.setHorizontalHeaderLabels( ['Absolute Pathnames', 'Relative Pathnames', 'Disable/Enable']) self.table_writeNodes.setWordWrap(0) self.table_writeNodes.setRowCount(0) #calls the '__generateWritesData()' private method that will populate the 'table_writeNodes' object based on the current working Nuke script self.generateTable_writes() #within the 'self.table_writeNodes' object, whenever a selection change occurs, invoke the 'self.__changeSelection_writes' function self.table_writeNodes.itemSelectionChanged.connect( self.__changeSelection_writes) #-------------------------- self.tab_writeNodes.layout.addWidget(self.table_writeNodes) self.tab_writeNodes.setLayout(self.tab_writeNodes.layout) #-------------------------- #--- END WRITE NODES TABLE ---# #adds the 'tabBar' object to the 'coreLayout' 'QtWidgets.Layout()' object coreLayout.addWidget(tabBar) #--- END TABS ---# txt_os = QtWidgets.QLabel() txt_os.setText(platform.platform()) txt_os.setAlignment(QtCore.Qt.AlignRight) coreLayout.addWidget(txt_os) self.primary_layout.addLayout(coreLayout)
import nuke import nextVersion nuke.menu("Nuke").addCommand('Scripts/nextVersion', 'nextVersion.nextVersion()') nuke.addOnUserCreate(nextVersion.addCustomElements, nodeClass="Write") nuke.addKnobChanged(nextVersion.performCustomAction, nodeClass="Write")
import sys import nuke def setViewerProcess(): nuke.thisNode().knob('viewerProcess').setValue('rec709') nuke.addOnUserCreate(setViewerProcess,nodeClass = 'Viewer') menubar = nuke.menu("Nuke") toolbar = nuke.toolbar("Nodes") # If Write dir does not exist, create it def createWriteDir(): file = nuke.filename(nuke.thisNode()) dir = os.path.dirname( file ) osdir = nuke.callbacks.filenameFilter( dir ) try: os.makedirs( osdir ) return except: return # Activate the createWriteDir function nuke.addBeforeRender(createWriteDir) # Make Read & Write node default to rec709 color space nuke.knobDefault('Read.mov.colorspace', 'rec709') nuke.knobDefault('Write.mov.colorspace', 'rec709') smoke_gizmos = toolbar.addMenu("Smoke gizmos", icon="sm.png")
def revealInFinder(): ''' get filepath and reveal src in finder ''' n = nuke.thisNode() k = nuke.thisKnob() if k.name() == "revealInFinder": f = n.knob("file").value() path = "/".join(f.split("/")[:-1]) openFolder(path) def addRevealButton(): ''' add custom tab in read node and add reveal button ''' n = nuke.thisNode() rB = nuke.PyScript_Knob('revealInFinder', 'reveal in finder', '') cT = nuke.Tab_Knob("custom", "custom") n.addKnob(cT) n.addKnob(rB) nuke.addOnUserCreate(addRevealButton, nodeClass = 'Read') nuke.addKnobChanged(revealInFinder, nodeClass="Read")
subprocess.check_call(['explorer', path]) def revealInFinder(): ''' get filepath and reveal src in finder ''' n = nuke.thisNode() k = nuke.thisKnob() if k.name() == "revealInFinder": f = n.knob("file").value() path = "/".join(f.split("/")[:-1]) openFolder(path) def addRevealButton(): ''' add custom tab in read node and add reveal button ''' n = nuke.thisNode() rB = nuke.PyScript_Knob('revealInFinder', 'reveal in finder', '') cT = nuke.Tab_Knob("custom", "custom") n.addKnob(cT) n.addKnob(rB) nuke.addOnUserCreate(addRevealButton, nodeClass='Read') nuke.addKnobChanged(revealInFinder, nodeClass="Read")
return (SHOWsn, SHOWln, EP, SEQ, SHOT) SHOWsn, SHOWln, EP, SEQ, SHOT = newParts() print SHOWsn, SHOWln, EP, SEQ, SHOT def checkNew(): nodeClass = nuke.thisNode().Class() if nodeClass == 'Root': newParts() return # Register the callback nuke.addOnUserCreate(checkNew) def nukeDir(): showParts = newParts() if showParts == 0: return #SHOWln = showParts[1] nkDir = os.path.join(SHOWln, "%s" % EP, "%s" % SEQ, "%s" % SHOT, 'nuke', os.environ['USERNAME'], 'scripts') pathCheck = os.path.join(SHOWln, "%s" % EP, "%s" % SEQ, "%s" % SHOT) if os.path.isdir(pathCheck): if not os.path.isdir(nkDir): os.makedirs(nkDir) else: nuke.message("Shot does not exist") nuke.load("panel")
def add_callbacks(): # Add an onUserCreate callback for Chromatik in order to have the center default to the center. # Workaround for the fact that nuke doesn't let us set defaults on knobs properly. nuke.addOnUserCreate(center_chromatik, nodeClass='Chromatik')
def populate_ftrack(self): import nuke import legacy from nukescripts import panels from ftrack_connect_nuke.ui.widget.crew import NukeCrew from ftrack_connect_nuke.connector import Connector # Check if QtWebKit or QWebEngine is avaliable. from FnAssetAPI.ui.toolkit import is_webwidget_supported has_webwidgets = is_webwidget_supported() Connector.registerAssets() # wrappers for initializing the widgets with # the correct connector object def wrapImportAssetDialog(*args, **kwargs): from ftrack_connect.ui.widget.import_asset import FtrackImportAssetDialog return FtrackImportAssetDialog(connector=Connector()) def wrapAssetManagerDialog(*args, **kwargs): from ftrack_connect.ui.widget.asset_manager import FtrackAssetManagerDialog return FtrackAssetManagerDialog(connector=Connector()) # Populate the ui nukeMenu = nuke.menu("Nuke") ftrackMenu = nukeMenu.addMenu("&ftrack") ftrackMenu.addSeparator() # add ftrack publish node to the menu ftrackMenu.addCommand('Create Publish Node', lambda: legacy.createFtrackPublish()) ftrackMenu.addSeparator() globals()['ftrackImportAssetClass'] = wrapImportAssetDialog panels.registerWidgetAsPanel( '{0}.{1}'.format(__name__, 'ftrackImportAssetClass'), 'ftrackImportAsset', 'ftrackDialogs.ftrackImportAssetDialog') ftrackMenu.addSeparator() ftrackMenu.addCommand( 'Import Asset', 'pane = nuke.getPaneFor("Properties.1");' 'panel = nukescripts.restorePanel("ftrackDialogs.ftrackImportAssetDialog");' 'panel.addToPane(pane)') globals()['ftrackAssetManagerDialogClass'] = wrapAssetManagerDialog # Create the asset manager dialog entry in the menu panels.registerWidgetAsPanel( '{0}.{1}'.format(__name__, 'ftrackAssetManagerDialogClass'), 'ftrackAssetManager', 'ftrackDialogs.ftrackAssetManagerDialog') ftrackMenu.addCommand( 'Asset Manager', 'pane = nuke.getPaneFor("Properties.1");' 'panel = nukescripts.restorePanel("ftrackDialogs.ftrackAssetManagerDialog");' 'panel.addToPane(pane)') if has_webwidgets: from ftrack_connect_foundry.ui.info_view import InfoView as _InfoView ftrackMenu.addCommand( _InfoView.getDisplayName(), 'pane = nuke.getPaneFor("Properties.1");' 'panel = nukescripts.restorePanel("{identifier}");' 'panel.addToPane(pane)'.format( identifier=_InfoView.getIdentifier())) ftrackMenu.addSeparator() if has_webwidgets: from ftrack_connect_foundry.ui.info_view import WorkingTaskInfoView as _WorkingTaskInfoView from ftrack_connect_foundry.ui.tasks_view import TasksView as _TasksView # Add Web Views located in the ftrack_connect_foundry package to the # menu for easier access. for widget in [_TasksView, _WorkingTaskInfoView]: ftrackMenu.addCommand( widget.getDisplayName(), 'pane = nuke.getPaneFor("Properties.1");' 'panel = nukescripts.restorePanel("{identifier}");' 'panel.addToPane(pane)'.format( identifier=widget.getIdentifier())) ftrackMenu.addSeparator() # Create the crew dialog entry in the menu panels.registerWidgetAsPanel( 'ftrack_connect_nuke.ui.widget.crew.NukeCrew', 'Crew', 'widget.Crew') ftrackMenu.addCommand( 'Crew', 'pane = nuke.getPaneFor("Properties.1");' 'panel = nukescripts.restorePanel("widget.Crew");' 'panel.addToPane(pane)') # Add new entries in the ftrack menu. ftrackMenu.addSeparator() if has_webwidgets: from ftrack_connect_nuke.ui.widget.publish_gizmo import GizmoPublisherDialog ftrackMenu.addCommand('Publish gizmo', GizmoPublisherDialog) # Add ftrack publish node toolbar = nuke.toolbar("Nodes") ftrackNodesMenu = toolbar.addMenu("ftrack", icon="logobox.png") ftrackNodesMenu.addCommand('ftrackPublish', lambda: legacy.createFtrackPublish()) # Set calbacks nuke.addOnScriptLoad(legacy.refAssetManager) nuke.addOnScriptLoad(legacy.scan_for_new_assets) nuke.addOnUserCreate(legacy.addFtrackComponentField, nodeClass='Write') nuke.addOnUserCreate(legacy.addFtrackComponentField, nodeClass='WriteGeo') nuke.addOnUserCreate(legacy.addFtrackComponentField, nodeClass='Read') nuke.addKnobChanged(legacy.ftrackPublishKnobChanged, nodeClass="Group") nuke.addOnCreate(legacy.ftrackPublishHieroInit)
nuke.menu("Nodes").addCommand("Transform/Tracker", "nuke.createNode('Tracker4')", "ctrl+alt+t", icon="Tracker.png", shortcutContext=2) # ---------------------------------------------------------------------------- # Knob Defaults ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: # ---------------------------------------------------------------------------- nuke.knobDefault("Tracker4.shutteroffset", "centered") nuke.knobDefault( "Tracker4.label", "Motion: [value transform]\nRef Frame: [value reference_frame]") nuke.addOnUserCreate( lambda: nuke.thisNode()["reference_frame"].setValue(nuke.frame()), nodeClass="Tracker4") nuke.addOnUserCreate( lambda: nuke.thisNode()["first_frame"].setValue(nuke.frame()), nodeClass="FrameHold") # ---------------------------------------------------------------------------- # Merge Operations Preset ::::::::::::::::::::::::::::::::::::::::::::::::::: # ---------------------------------------------------------------------------- mergeMenu = nuke.menu("Nodes").findItem("Merge/Merges") mergeMenu.addCommand("Stencil", "nuke.createNode('Merge2', 'operation stencil bbox B')", "alt+o", icon="frankTest.png",
elif cornerPinOption == 1: tracker.knob("createPinUseReferenceFrame").execute() elif cornerPinOption == 2: tracker.knob("createPinUseCurrentFrameBaked").execute() elif cornerPinOption == 3: tracker.knob("createPinUseReferenceFrameBaked").execute() elif cornerPinOption == 4: tracker.knob("createTransformStabilize").execute() elif cornerPinOption == 5: tracker.knob("createTransformMatchMove").execute() elif cornerPinOption == 6: tracker.knob("createTransformStabilizeBaked").execute() elif cornerPinOption == 7: tracker.knob("createTransformMatchMoveBaked").execute() ##---------------------------------------------------------## try: [node for node in nuke.allNodes("Transform", recurseGroups=True) if node not in allTranforms][0].knob("label").setValue("<html><center>From: " + trackerName + "\\nFrame: " + "%s</center></html>"%refFrame) [node for node in nuke.allNodes("Transform", recurseGroups=True) if node not in allTranforms][0].knob("shutteroffset").setValue("centred") except: [node for node in nuke.allNodes("CornerPin2D", recurseGroups=True) if node not in allCornerPin][0].knob("label").setValue("<html><center>From: " + trackerName + "\\nFrame: " + "%s</center></html>"%refFrame) [node for node in nuke.allNodes("CornerPin2D", recurseGroups=True) if node not in allCornerPin][0].knob("shutteroffset").setValue("centred") ''' nuke.thisNode().knob('createCornerPin').setValue(code) nuke.addOnUserCreate(customTracker, nodeClass="Tracker4")
def knobChanged( self, knob ): if knob in self.checkboxes: # MAKE SURE ONLY ONE KNOB IS CHECKED for cb in self.checkboxes: if knob == cb: # EXTRACT THE INDEX FORM THE NAME AGAIN index = int( knob.name().split('_')[-1] ) self.selectedScript = self.nkScripts[ index ] continue cb.setValue( False ) # HELPER FUNCTION FOR NUKE SCRIPT PANEL def nkPanelHelper(): # GET ALL NUKE SCRIPTS FOR CURRENT SHOT nkScripts = getNukeScripts() if not nkScripts: # IF THERE ARE NONE DON'T DO ANYTHING return # CREATE PANEL p = NkPanel( nkScripts ) # ADJUST SIZE p.setMinimumSize( 200, 200 ) # IF PANEL WAS CONFIRMED AND A NUKE SCRIPT WAS SELECTED, OPEN IT if p.showModalDialog(): if p.selectedScript: nuke.scriptOpen( p.selectedScript ) # AUTO LOAD SCRIPT SELECTOR nuke.addOnUserCreate( nkPanelHelper, nodeClass='Root')
#Reload Bool do_reload = True from helga.general.setup.global_variables import global_variables if (do_reload): reload(global_variables) #global_functions from helga.general.setup.global_functions import global_functions if (do_reload): reload(global_functions) #------------------------------------------------------------------ #set global stylesheet QtGui.qApp.setStyleSheet(global_variables.NUKE_STYLESHEET) #log print('Succesfully adjusted GUI') except: #log print('Error adjusting GUI') #Print to console instead of script editor print('Registering GUI adjustment callback') #add startup callback nuke.addOnUserCreate(set_nuke_stylesheet, nodeClass='Root')
import nuke import testVersion nuke.menu("Nuke").addCommand('Plugin/testVersion', 'testVersion.testVersion()') nuke.addOnUserCreate(testVersion.addCustomKnobs, nodeClass="Write") nuke.addKnobChanged(testVersion.performCustomAction, nodeClass="Write")
# This will make them all print something to stdout so you can see # when they are called. import nuke def _cb(name): nuke.tprint(name + " " + nuke.thisNode().name()) def _cbk(name): nuke.tprint(name + " " + nuke.thisNode().name() + "." + nuke.thisKnob().name()) nuke.addOnUserCreate(_cb, ("onUserCreate")) nuke.addOnCreate(_cb, ("onCreate")) nuke.addOnScriptLoad(_cb, ("onScriptLoad")) nuke.addOnScriptSave(_cb, ("onScriptSave")) nuke.addOnScriptClose(_cb, ("onScriptClose")) nuke.addOnDestroy(_cb, ("onDestroy")) nuke.addKnobChanged(_cbk, ("knobChanged")) nuke.addUpdateUI(_cb, ("updateUI"))
(pName, pExt) = os.path.splitext(os.path.basename(root['name'].value())) pData = etree.fromstring(root['tri_project_xml_formats'].value()) for group in nuke.allNodes("Group", nuke.root()): if 'triwrite_gizmo' not in group.knobs(): continue resultNode = group.node('result') dailiesNode = group.node('dailies') #--- update results params filename = pName + pData.find('result').find('file').get('numbers') + pData.find('result').find('file').get('file_type') filename = nukenormpath(filename) oldfilename = os.path.basename(resultNode['file'].value()) resultNode['file'].setValue(resultNode['file'].value().replace(oldfilename, filename)) #--- update dailies params filename = pName + pData.find('dailies').find('file').get('numbers') + pData.find('dailies').find('file').get('file_type') filename = nukenormpath(filename) oldfilename = os.path.basename(dailiesNode['file'].value()) dailiesNode['file'].setValue(dailiesNode['file'].value().replace(oldfilename, filename)) pData = None nuke.addOnUserCreate(tri_project_init, nodeClass="Root") nuke.addOnUserCreate(tri_writeGizmo_init, nodeClass="Group") nuke.addOnScriptSave(tri_writeGizmo_update, nodeClass="Root") nuke.addOnScriptLoad(tri_project_init, nodeClass="Root")
from words import words as wordList nuke.randomWord = lambda: wordList[randint(0,len(wordList)-1)].title() # ------------------------------------------------- # def saveRandomName(): """ Automatically saves comps on launch with a random name to NUKE_TEMP_DIR. To remove a comp from cache, delete all nodes, save, and quit. """ if nuke.root().name() == "Root": path = os.environ["NUKE_TEMP_DIR"]\ + "/" + nuke.randomWord()\ + "-" + nuke.randomWord()\ + "_" + datetime.now().strftime("%d-%B-%Y")\ + "_v1.nk" print "Default saved: " + path nuke.scriptSaveAs(path) nuke.removeOnUserCreate(saveRandomName) nuke.addOnUserCreate(saveRandomName) def deleteEmptyScript(): """ Removes any *saved* script if the script is empty.""" allNodes = nuke.allNodes() dirPath,script = os.path.split(nuke.root().name()) if len([n for n in allNodes if n.Class() != "Viewer"]) == 0 and script != "Root": os.chdir(dirPath) [os.remove(f) for f in os.listdir(dirPath) if re.search(script.split(".")[0],f)] nuke.addOnScriptClose(deleteEmptyScript)
import nuke import frameHoldSet nuke.addOnUserCreate(frameHoldSet.frame_hold_set, nodeClass="FrameHold")
nuke.message("No renderable Write nodes!") return else: _list = sorted(_list,key=lambda x: x['render_order'].value()) nuke.scriptSave("") for n in _list: if n['use_limit'].value(): first,last = n['first'].value(),n['last'].value() else: first,last = nuke.root().firstFrame(),nuke.root().lastFrame() print "Render started: %s" % n.name() nuke.execute(n.name(),int(first),int(last)) print "Render completed: %s" % n.name() nukescripts.execute_panel = custom_execute_panel nuke.addOnUserCreate(attach_switchRoute,(),{},"Write") nuke.getSwitchRoute = getSwitchRoute nuke.setSwitchRoute = setSwitchRoute
import nuke from assign import addAssign,addAssignAuto,getInfos,main,addAssignManual,getFilePaths nuke.addOnUserCreate(addAssignAuto,nodeClass=('Read')) menu=nuke.menu('Nuke') assignMenu=menu.addMenu('Mo_Tools/assign',icon="M.jpg") assignMenu.addCommand('assign',lambda:main(),icon="assign.png") assignMenu.addCommand('add assign tab to read',lambda:addAssignManual(),icon="addAssign.png") assignMenu.addCommand('Get File Paths only',lambda:getFilePaths(),'alt+d',icon="M.jpg")
createViews(views) # Remove the views that are not in views for existingView in viewsToDelete: nuke.root().deleteView(existingView) elif userChoice is nuke.ADD_VIEWS: # Create only the missing views createViews(missingViews) def onReadNodeCreated(): """ Callback when a Read node is created. Note that the knob values don't seem to be set when this callback occurs. Defer the check with a QTimer, which will cause the views check to be done when the Qt event loop next sends events. """ read = nuke.thisNode() QTimer.singleShot(0, lambda: checkReadNodeViews(read)) def onReadNodeKnobChanged(): """ Callback when a Read node knob changes. If it's the file knob, check for multi views. """ if nuke.thisKnob().name() == "file": checkReadNodeViews(nuke.thisNode()) # Register the callbacks nuke.addKnobChanged(onReadNodeKnobChanged, nodeClass="Read") nuke.addOnUserCreate(onReadNodeCreated, nodeClass="Read")
nuke.menu("Nodes").addCommand("Merge/Merges/From", "From()", icon = "Merge.png") def Divide(): Mask = nuke.createNode('Merge2') Mask.knob('operation').setValue('divide') nuke.menu("Nodes").addCommand("Merge/Merges/Divide", "Divide()", icon = "Merge.png") ###################################################################################################################################################### ###################################################################################################################################################### def FrameHoldCurFrame(): fh = nuke.thisNode() fh.knob('first_frame').setValue(nuke.frame()) nuke.addOnUserCreate(FrameHoldCurFrame, nodeClass='FrameHold') ###################################################################################################################################################### ###################################################################################################################################################### ###################################################################################################################################################### ###################################################################################################################################################### def updateFrameRange(): for readNodes in nuke.selectedNodes(): name = readNodes.knob('file').getValue().split('/')[-1].split('%')[0] fileType = readNodes.knob('file').getValue().split('/')[-1].split('.')[-1] path = '/'.join(readNodes.knob('file').getValue().split('/')[:-1])+'/' fRange = '' for frameNumbers in nuke.getFileNameList(path):
def add_callbacks(self): """Add nuke callbacks. """ nuke.removeOnDestroy(ChannelsRename.hide, args=(self)) nuke.addOnUserCreate(ChannelsRename.hide, args=(self))
if good == T: if oper == 1: try: [os.remove(f[0]) for f in files] ## TODO: This is not working nuke.message("All files were relocated") except: nuke.message("All files were copied but originals could not be removed") else: nuke.message("All files were copied") update_source = nuke.ask("Update node's source/target?") if update_source: filename = os.path.split(nuke.filename(n))[-1] set_new_path = rename and new_path or new_path + filename n["file"].setValue(set_new_path) else: print "Cannot acquire range" def attach_file_operations(*args): n = nuke.thisNode() if "fileops" not in n.knobs().keys(): n.addKnob(nuke.Text_Knob("fileops", "File Operations")) n.addKnob(nuke.PyScript_Knob("copyBtn", "Copy", "nuke.fileop(0)")) n.addKnob(nuke.PyScript_Knob("moveBtn", "Move", "nuke.fileop(1)")) return n nuke.addOnUserCreate(attach_file_operations, (), {}, "Write") nuke.addOnUserCreate(attach_file_operations, (), {}, "Read") nuke.fileop = fileop
nuke.menu('Nuke').addCommand('Chetan/Toggle Viewer Inputs', 'chet_utils.toggle_viewer_inputs()', 'alt+t') nuke.menu('Nuke').addCommand('Chetan/Commandline Render', 'ctz_cmd_render.start()') ### Create a custom menu - i.e.: # you need a gizmo to be placed in your '.nuke' folder structure # toolbar = nuke.menu('Nodes') # myMenu = toolbar.addMenu('myMenuElement', icon='myMenuIcon.png') # myMenu.addCommand('myElement', 'nuke.createNode("myGizmo")', icon='myGizmoIcon.png', index=0) #the index argument (optional) indicates the position of the item within the menu toolbar = nuke.menu('Nodes') myMenu = toolbar.addMenu('cTz', icon='script_folder.png') myMenu.addCommand('V_EdgeMatte', 'nuke.createNode("V_EdgeMatte")', icon='V_EdgeMatte.png') myMenu.addCommand('SliceTool', 'nuke.createNode("SliceTool")') myMenu.addCommand('akromatism_stRub', 'nuke.createNode("akromatism_stRub")') myMenu.addCommand('lenskernelFFT', 'nuke.createNode("LensKernelFFT_v01")') myMenu.addCommand('DespillMadness', 'nuke.createNode("DespillMadness")') myMenu.addCommand('glass', 'nuke.createNode("Glass")') myMenu.addCommand('P_Matte', 'nuke.createNode("P_Matte")') myMenu.addCommand('SynthEyesLensDistortion', 'nuke.createNode("SynthEyesLensDistortion")') myMenu.addCommand('Coc Depth of Field', 'nuke.createNode("CoCDoF")') myMenu.addCommand('ColExpand', 'nuke.createNode("ColExpand")') myMenu.addCommand('BadPixels', 'nuke.createNode("badpixels")') ### Run a command everytime a node of a specific type is created # set the value of a new framehold node to the currentframe. nuke.addOnUserCreate(lambda:nuke.thisNode()['first_frame'].setValue(nuke.frame()), nodeClass='FrameHold') # set the label of a new camera to show the current focal length nuke.addOnUserCreate(lambda:nuke.thisNode()['label'].setValue('F[expr round([value focal])]'), nodeClass='Camera') nuke.addOnUserCreate(lambda:nuke.thisNode()['label'].setValue('F[expr round([value focal])]'), nodeClass='Camera2')
#coding=utf-8 import nuke import reveal_in_finder node_classes = ["Read","Write","Camera","Camera2","ReadGeo","ReadGeo2","WriteGeo"] nuke.menu('Nuke').addCommand("common_func/utilities/添加查找路径",reveal_in_finder.add,"alt+o") for node in node_classes: nuke.addOnUserCreate(reveal_in_finder.add_reveal_button,nodeClass=node) nuke.addKnobChanged(reveal_in_finder.reveal_in_finder,nodeClass=node) #定义这个node的knob产生变化后运行的判断
# Set node's position given a centered position based on screen width # param: pos - 2dim list of int node positions if node.Class() == 'BackdropNode': return node.setXYpos(int(posx), int(posy)) else: return node.setXYpos(int(posx - node.screenWidth() / 2), int(posy - node.screenHeight() / 2)) def hide_panel(): # Always hide control panels on node creation nuke.thisNode().showControlPanel() nuke.thisNode().hideControlPanel() nuke.addOnUserCreate(hide_panel) def open_panels(nodes=None): # Open properties panels if not nodes: nodes = nuke.selectedNodes() ignored = ['Viewer'] if len(nodes) > 10: if not nuke.ask( 'Continuing will open {0} properties panels. \nAre you sure you want to continue?' .format(len(nodes))): return for node in nodes: if node.Class() not in ignored: # if node.shown():
import nuke import nextVersion nuke.menu("Nuke").addCommand('Scripts/nextVersion', 'nextVersion.nextVersion()') nuke.addOnUserCreate(nextVersion.addCustomElements, nodeClass = "Write") nuke.addKnobChanged(nextVersion.performCustomAction, nodeClass = "Write")