在nuke中有好幾種創(chuàng)建自定義panel的方法:
- 簡單的panel命令 -- 常用任務(wù)腮猖,比如讓用戶確認(rèn)或者獲取文件名
- 簡單panel對象 -- 定制某些擴(kuò)展饮焦,創(chuàng)建簡單闪湾。如果需要一個簡單的定制panel遍膜,可以添加大量knob
- python panel -- 創(chuàng)建更加復(fù)雜爵赵,提供node中所有的knob,callback洲劣,部分layout控制
簡單panel命令
nuke有大量的簡單panel备蚓,不用繁瑣的定制就能使用课蔬。
-
消息框
nuke.message('Just saying hi')
_images/panel_01.png 用戶查詢 給用戶一個“yes、no”選擇:
if nuke.ask('Are you sure you want to create a Blur node? '):
nuke.createNode('Blur')
![_images/panel_09.png](https://docs.thefoundry.co.uk/products/nuke/developers/100/pythondevguide/_images/panel_09.png)
- 顯示窗口
def showChannels():
return '\n'.join( nuke.thisNode().channels() )
node = nuke.selectedNode()
nuke.display('showChannels()', node, 'show channels for %s' % node.name() )
這是個非模式panel郊尝,運(yùn)行showChannels二跋,用其結(jié)果來填充窗口。node作為第二參數(shù)流昏。
![_images/panel_02.png](https://docs.thefoundry.co.uk/products/nuke/developers/100/pythondevguide/_images/panel_02.png)
- 獲取用戶輸入-從用戶獲取輸入并賦給所選節(jié)點的label
txt = nuke.getInput('Change label', 'new label')
if txt:
for n in nuke.selectedNodes():
n['label'].setValue(txt)
![_images/panel_03.png](https://docs.thefoundry.co.uk/products/nuke/developers/100/pythondevguide/_images/panel_03.png)
- 顏色選擇:給選定節(jié)點賦予指定顏色
col = nuke.getColor()
if col:
for n in nuke.selectedNodes():
n['title_color'].setValue(col)
n['gl_color'].setValue(col)
![_images/panel_04.png](https://docs.thefoundry.co.uk/products/nuke/developers/100/pythondevguide/_images/panel_04.png)
- 文件瀏覽扎即,返回文件全路徑:
filePath = nuke.getFilename('get file contents', '*.txt *.xml')
![_images/panel_05.png](https://docs.thefoundry.co.uk/products/nuke/developers/100/pythondevguide/_images/panel_05.png)
- 序列瀏覽器 列出圖像序列
seqPath = nuke.getClipname('get sequence')
![_images/panel_06.png](https://docs.thefoundry.co.uk/products/nuke/developers/100/pythondevguide/_images/panel_06.png)
- 獲取幀范圍,從用戶那獲取幀范圍横缔,如果是立體的铺遂,則附帶在哪個view操作:
ret = nuke.getFramesAndViews('get range', '1-10' )
返回值是字符串的list,和要求的view:
ret = nuke.getFramesAndViews('get range', '1-10')
range = ret[0]
views = ret[1]
print 'range is', range
print 'views are', views
![_images/panel_07.png](https://docs.thefoundry.co.uk/products/nuke/developers/100/pythondevguide/_images/panel_07.png)
多視圖腳本:
![_images/panel_08.png](https://docs.thefoundry.co.uk/products/nuke/developers/100/pythondevguide/_images/panel_08.png)
簡單panel對象
創(chuàng)建一個簡單panel對象:
p = nuke.Panel('my custom panel')
檢查哪些knob可用是個好主意:
dir(p)
添加knobs:
p.addClipnameSearch('clip path', '/tmp')
p.addFilenameSearch('file path', '/tmp')
p.addTextFontPulldown('font browser', '/myFonts/')
p.addRGBColorChip('some pretty color', '')
p.addExpressionInput('enter an expression', '4*25')
p.addBooleanCheckBox('yes or no?', True)
p.addEnumerationPulldown('my choices', 'A B C')
p.addScriptCommand('tcl or python code', '')
p.addSingleLineInput('just one line', 'not much space')
p.addMultilineTextInput('multiple lines of user input text', 'lineA\nlineB')
p.addNotepad('write something', 'some very long text could go in here. For now this is just some random default value')
p.addPasswordInput('password', 'donttellanyone')
p.addButton('push here')
p.addButton('or here')
p.addButton('or even here')
打開panel:
ret = p.show()
![_images/panel_10.png](https://docs.thefoundry.co.uk/products/nuke/developers/100/pythondevguide/_images/panel_10.png)
panel關(guān)閉后茎刚,獲取某個值:
print p.value('clip path')
pirnt p.value('file path')
python panel
shapePanel
有兩個下拉菜單的panel,第一個knob控制第二個knob shape的顯示
![_images/shapePanel_01.png_images/shapePanel_02.png](https://docs.thefoundry.co.uk/products/nuke/developers/100/pythondevguide/_images/shapePanel_01.png)
![](https://docs.thefoundry.co.uk/products/nuke/developers/100/pythondevguide/_images/shapePanel_02.png)
開始撤逢,創(chuàng)建一個繼承nukescripts.PythonPanels的類膛锭,運(yùn)行其構(gòu)造并傳入一個標(biāo)題
class ShapePanel( nukescripts.PythonPanel):
def __init__(self):
nukescripts.PythonPanel.__init__(self, 'RotoPaint Elements')
用參數(shù)傳遞我們想分析的節(jié)點,在init中的參數(shù)列表中蚊荣,賦值給self.rpNode:
class ShapePanel( nukescripts.PythonPanel):
def __init__(self, node):
nukescripts.PythonPanel.__init__(self, 'RotoPaint Elements')
self.rpNode = node
使用nuke.Enumeration_Knob創(chuàng)建下拉列表:
self.typeKnob = nuke.Enumeration_Knob('element', 'element', ['Shape', 'Strokes'])
再一次初狰,在全局命名空間中引用這個knob的值,讀取so easy互例。提供給knob的三個參數(shù)是:
- element knob的對象名字
- element knob的標(biāo)簽
- ['Shapes', 'Strokes'] 下拉菜單中顯示的選項
創(chuàng)建第二個enumeration knob奢入, 但其選項設(shè)置為空, 動態(tài)設(shè)置:
self.elementKnob = nuke.Enumeration_Knob('curve', 'curve', [])
現(xiàn)在添加兩個knob:
for k in ( self,typeKnob, self.elementKnob):
self.addKnob(k)
目前的代碼:
class ShapePanel( nukescripts.PythonPanel):
def __init__(self, node):
nukescripts.PythonPanel.__init__(self, 'RotoPaint Elements')
self.rpNode = node
self.typeKnob = nuke.Enumeration_Knob('element', 'element', ['Shapes', 'Strokes'])
self.elementKnob = nuke.Enumeration_Knob('curve', 'curve', [])
for k in (self.typeKnob, self.elementKnob):
self.addKnob(k)
如果你創(chuàng)建了這個類的例子媳叨,可以用showModalDialog()將其作為模式對話框打開腥光。創(chuàng)建一個RotoPaint節(jié)點,名為RotoPaint1.
node = nuke.toNode('RotoPaint1')
p = ShapePanel(node)
p.showModalDialog()
![_images/shapePanel_03.png](https://docs.thefoundry.co.uk/products/nuke/developers/100/pythondevguide/_images/shapePanel_03.png)
注意showModalDialog給我了帶ok糊秆,cancel按鈕的對話框武福。下面將其美化下:
默認(rèn)情況下,enumeration knob從新一行開始痘番,但是如果我們清除了這個標(biāo)志位捉片,就可以讓兩個knob在同一行:
class ShapePanel( nukescripts.PythonPanel):
def __init__(self, node):
nukescripts.PythonPanel.__init__(self, 'RotoPaint Elements')
self.rpNode = node
self.typeKnob = nuke.Enumeration_Knob('element', 'element/shape', ['Shapes', 'Strokes'])
self.elementKnob = nuke.Enumeration_Knob('curve', '', [])
self.elementKnob.clearFlag(nuke.STARTLINE)
for k in (self.typeKnob, self.elementKnob):
self.addKnob(k)
這也改變了標(biāo)簽上的第一個knob,并將第二個上的knob移除汞舱,這看上去更加簡潔:
![_images/shapePanel_04.png](https://docs.thefoundry.co.uk/products/nuke/developers/100/pythondevguide/_images/shapePanel_04.png)
最后一件在構(gòu)造時要做的事情就是 創(chuàng)建一個字典伍纫,按類型保存元素。在init函數(shù)最后面添加如下代碼:
self.curveDict = {}
實際上不真需要昂芜,可以init在全局范圍內(nèi)初始化變量, 用節(jié)點的shape和stroke來填充字典:
def getData( self):
self.curveDict={'Shapes':[], 'Strokes':[] }
rootLayer = self.rpNode['curves'].rootLayer
1. 用填充第一個enumeration knob的關(guān)鍵字來填充字典(shape莹规,stroke)
2. 抓取node的curve knob,其能列出Roto或者RotoPaint的所有元素说铃,并獲取其rootLayer
rootLayer是個可迭代的對象访惜,能產(chǎn)生curve knob中列出的所有元素嘹履。因此可以遍歷獲取其所有孩子元素。檢查對應(yīng)元素的類型债热,保存進(jìn)
對應(yīng)的字典:
for e in rootLayer:
if isinstance(e, nuke.rotopaint.Shape):
self.curveDict['Shapes'].append(e.name)
elif isinstance( e, nuke.rotopaint.Stroke):
self.curveDict['Stokes'].append( e.name)
用感興趣的數(shù)據(jù)填充字典砾嫉,通過'shape’和‘stroke’保存。在init最后添加如下代碼:
class ShapePanel(nukescripts.PythonPanel):
def __init__(self, node):
'''List all roto paint nodes and the name of their respective shapes and strokes'''
nukescripts.PythonPanel.__init__(self, 'RotoPaint Elements')
self.rpNode = node
# CREATE KNOBS
self.typeKnob = nuke.Enumeration_Knob('element', 'element / curve', ['Shapes', 'Strokes'])
self.elementKnob = nuke.Enumeration_Knob('curve', '', [])
self.elementKnob.clearFlag(nuke.STARTLINE)
# ADD KNOBS
for k in (self.typeKnob, self.elementKnob):
self.addKnob(k)
# STORE DICTIONARY OF ELEMENTS PER TYPE
self.curveDict = {}
# FILL DICTIONARY
self.getData()
最后窒篱,添加knobChanged方法焕刮,每次panel內(nèi)的knob有變化就觸發(fā)此函數(shù)。knobChanged方法接收一個額外的參數(shù)墙杯,將正在變化的knob傳入
函數(shù)內(nèi)配并。
來看運(yùn)行情況,或者調(diào)試高镐。當(dāng)你改變panel內(nèi)容是溉旋,這是打印knob名字的好方法,并留意腳本編輯器的輸出panel:
def knobChanged( self, knob):
print knob.name()
如果你在這步運(yùn)行panel的代碼嫉髓,注意用knob的showPanel打開panel的觸發(fā)器 knobChanged的回調(diào)函數(shù)观腊。
![_images/shapePanel_05.png](https://docs.thefoundry.co.uk/products/nuke/developers/100/pythondevguide/_images/shapePanel_05.png)
如果你換了第一個enumeration knob,你將看到它的名字會被打印到輸出panel上算行。
現(xiàn)在梧油,用它干點有用的事。我們已經(jīng)有一個字典存儲了所有的shape和stroke州邢,因此我們要做的就是讀取并賦予對應(yīng)的值(基于第一個knob
的當(dāng)前值):
def knobChanged(self, knob):
self.elementKnob.setValues(self.curveDict[ self.typeKnob.value() ])
通過typeKnob里面找到的當(dāng)前值可以從字典里面讀取self.elementKnob的值儡陨。但是我們僅想panel打開或者typeKnob改變時才做:
def knobChanged(self, knob):
if knob is self.typeKnob or knob.name()=='showPanel':
self.elementKnob.setValues(self.curveDict[ self.typeKnob.value() ])
現(xiàn)在可以把第一個enumeration knob從shape轉(zhuǎn)換到stroke,并看到第二個konb自動更新自己的content
最終代碼:
import nuke
import nukescripts
class ShapePanel( nukescripts.PythonPanel ):
def __init__( self, node ):
'''List all roto paint nodes and the name of their respective shapes and strokes'''
nukescripts.PythonPanel.__init__( self, 'RotoPaint Elements' )
self.rpNode = node
# CREATE KNOBS
self.typeKnob = nuke.Enumeration_Knob( 'element', 'element / curve', ['Shapes', 'Strokes'] )
self.elementKnob = nuke.Enumeration_Knob( 'curve', '', [] )
self.elementKnob.clearFlag( nuke.STARTLINE )
# ADD KNOBS
for k in ( self.typeKnob, self.elementKnob ):
self.addKnob( k )
# STORE DICTIONARY OF ELEMENTS PER TYPE
self.curveDict = {}
# FILL DICTIONARY
self.getData()
def getData( self ):
'''return a nested dictionary of all shapes and strokes per node'''
self.curveDict={ 'Shapes':[], 'Strokes':[] }
rootLayer = self.rpNode['curves'].rootLayer
for e in rootLayer:
if isinstance( e, nuke.rotopaint.Shape ):
self.curveDict[ 'Shapes' ].append( e.name )
elif isinstance( e, nuke.rotopaint.Stroke ):
self.curveDict[ 'Strokes' ].append( e.name )
def knobChanged( self, knob ):
if knob is self.typeKnob or knob.name()=='showPanel':
self.elementKnob.setValues( self.curveDict[ self.typeKnob.value() ] )
通過showModalDialog模塊來調(diào)用panel量淌,我們知道用戶的決定骗村,任何事情發(fā)生前的確認(rèn)或者取消。用戶點ok就返回true类少,否則返回false叙身。
在全局范圍內(nèi)引用knobs,在panel關(guān)閉后也可以讀取knob的值硫狞。例子:
node = nuke.toNode('RotoPaint1')
p = ShapePanel(node)
if p.showModalDialog():
print p.elementKnob.value()
當(dāng)點擊ok關(guān)閉panel時信轿,就打印出值,否則啥也不發(fā)生残吩。
ShapeAndCVPanel
這個簡單的面板掃描Roto或RotoPaint節(jié)點的shape财忽,并將找到的所有shape的name放入下拉列表中。允許輸入幀范圍泣侮,指定CV編號即彪。這是
用來trackCV
![](https://docs.thefoundry.co.uk/products/nuke/developers/100/pythondevguide/_images/shapeAndCVPanel_01.png)
從導(dǎo)入使用的包開始
import nuke
import nukescripts
import nuke.rotopaint as rp
然后創(chuàng)建一個類繼承nukescripts中的PythonPanel,接收一個名為node的參數(shù)。調(diào)用父類的構(gòu)造函數(shù)給panle一個標(biāo)題隶校。
class ShapeAndCVPanel(nukescripts.PythonPanel):
def __init__(self, node):
nukescripts.PythonPanel.__init__(self, 'Get Shape and CV index')
在全局命名空間中引用傳給panel的node漏益,方便在其他地方使用。
self.rpNode = node
從node的curvesknob中獲取root 層深胳。
root = node['curves'].rootLayer
使用列表生成來獲取節(jié)點中的所有shape name:
shapeNames = [c.name for c in root if isinstance(c, rp.Shape)]
添加快速檢測:
if not shapeNames:
nuke.message('No Shapes found in %s' % node.name()
return
將knob添加到panel绰疤。字符串knob用來接收幀范圍:
self.fRange = nuke.String_Knob('fRange', 'Track Range')
注意 我們在panel的全局命名空間中引用knob(self.),因此后續(xù)可以在panle外訪問
String knob接收第三個可選參數(shù),來提供默認(rèn)值舞终。 此處使用來設(shè)置默認(rèn)值轻庆。
self.fRange = nuke.String_Knob('fRange', 'Track Range', '%s-%s' % (nuke.root().fristFrame(), nuke.root().lastFrame()))
接下來就是將名字添加到 enumeration knob中:
self.shape = nuke.Enumeration_Knob('shape', 'Shape', shapeNames)
添加一個整數(shù)knob讓用戶輸入CV的編號:
self.cv = nuke.Int_Knob('pointNumber', 'Point Number')
檢查整數(shù)knob,如果有錯敛劝,需要顯示提示信息:
self.warning = nuke.Text_Knob('warning', 'invalid index')
使用Html代碼讓提示更加醒目:
self.warning = nuke.Text_Knob('warning', '<span style="color:red">invalid index</span>')
靜態(tài)文本knob挨著pointNumber knob余爆,在此加入一行來分割:
self.warning.clearFlag(nuke.STARTLINE)
打開panle是需要隱藏警告提示的內(nèi)容:
self.warning.setVisible(False)
最后將四個knob添加到panel:
self.addKnob(self.fRange)
self.addKnob(self.shape)
self.addKnob(self.cv)
self.addKnob(self.warning)
想偷懶的可以這么做:
for k in (self.fRange, self.shape, self.cv, self.warning):
self.addKnob(k)
零碎代碼整合起來后如下:
import nuke
import nukescripts
import nuke.rotopaint as rp
class ShapeAndCVPanel(nukescripts.PythonPanel):
def __init__(self, node):
nukescripts.PythonPanel.__init__(self, 'Get Shape and CV index')
self.rpNode = node
# GET THE NODES ROOT LAYER AND COLLECT ALL SHAPES IN IT
root = node['curves'].rootLayer
shapeNames = [ c.name for c in root if isinstance(c, rp.Shape) ]
if not shapeNames:
nuke.message('No Shapes found in %s' % node.name())
return
# CREATE KNOBS
self.fRange = nuke.String_Knob('fRange', 'Track Range', '%s-%s' % (nuke.root().firstFrame(), nuke.root().lastFrame()))
self.shape = nuke.Enumeration_Knob('shape', 'Shape', shapeNames)
self.cv = nuke.Int_Knob('pointNumber', 'Point Number')
self.warning = nuke.Text_Knob('warning', 'invalid index')
self.warning.clearFlag(nuke.STARTLINE)
self.warning.setVisible(False)
# ADD KNOBS
for k in (self.fRange, self.shape, self.cv, self.warning):
self.addKnob(k)
為了檢測給定點編號是否正確,需要使用knobChanged方法夸盟,當(dāng)knob改變時會自動運(yùn)行蛾方。此方法需要knob參數(shù),其提供了此
功能满俗,并且knob觸發(fā)了這個回調(diào):
def knobChanged(self, knob):
僅需要self.cv或self.shape索引的knob變動時調(diào)用此函數(shù):
def knobChanged(self, knob):
if knob in(self.cv, self.shape):
如上發(fā)生转捕,就要講shape的knob設(shè)置如下:
currentShape = self.rpNode['curves'].toElement(self.shape.value())
我們僅想知道shape中有多少個點:
size = len([pt for pt in currentShape])
現(xiàn)在檢測pointNumber的值是否在合理范圍內(nèi):
validNumber = -1 < knob.value() < size
如果點在合理范圍內(nèi),上面的判別式返回true唆垃,否則返回false。取非來作為控制warnging knob顯示的開關(guān)痘儡。
self.warning.setVisible(not validNumber)
最終代碼如下:
import nuke
import nukescripts
import nuke.rotopaint as rp
class ShapeAndCVPanel( nukescripts.PythonPanel ):
def __init__( self, node ):
nukescripts.PythonPanel.__init__( self, 'Get Shape and CV index' )
self.rpNode = node
# GET THE NODES ROOT LAYER AND COLLECT ALL SHAPES IN IT
root = node['curves'].rootLayer
shapeNames = [ c.name for c in root if isinstance( c, rp.Shape ) ]
if not shapeNames:
nuke.message( 'No Shapes found in %s' % node.name() )
return
# CREATE KOBS
self.fRange = nuke.String_Knob( 'fRange', 'Track Range', '%s-%s' % ( nuke.root().firstFrame(), nuke.root().lastFrame() ) )
self.shape = nuke.Enumeration_Knob( 'shape', 'Shape', shapeNames )
self.cv = nuke.Int_Knob( 'pointNumber', 'Point Number' )
self.warning = nuke.Text_Knob( 'warning', '<span style="color:red">invalid index</span>' )
self.warning.clearFlag( nuke.STARTLINE )
self.warning.setVisible( False )
# ADD KOBS
for k in ( self.fRange, self.shape, self.cv, self.warning ):
self.addKnob( k )
def knobChanged( self, knob ):
# IF AN INVALID INDEX IS SHOWN DISPLAY THE WARNING TEXT
if knob in( self.cv, self.shape ):
currentShape = self.rpNode['curves'].toElement( self.shape.value() )
size = len( [pt for pt in currentShape] )
validNumber = -1 < knob.value() < size
self.warning.setVisible( not validNumber )
測試panle辕万,創(chuàng)建Roto或RotoPaint節(jié)點,新的shape沉删,運(yùn)行:
ShapeAndCVPanel(nuke.selectedNode()).showModalDialog()
panel顯示如下:
![](https://docs.thefoundry.co.uk/products/nuke/developers/100/pythondevguide/_images/shapeAndCVPanel_02.png)
如果點編號不合法顯示如下:
![](https://docs.thefoundry.co.uk/products/nuke/developers/100/pythondevguide/_images/shapeAndCVPanel_03.png)
增加panel的寬度渐尿,顯示完整的警告信息:
p = ShapeAndCVPanel(nuke.selectedNode())
p.setMinimumSize(400, 50)
p.showModalDialog()
搜索和替換面板
下面的代碼創(chuàng)建了一個面板,在NUKE腳本中執(zhí)行搜索和替換操作矾瑰。其使用了幫助函數(shù)search來簡化代碼砖茸。視頻教程在Nukepedia.
def search(searchstr, nodes):
""" Search in nodes with file knobs. """
fileKnobNodes = [i for i in nodes if __NodeHasKnobWithName(i, 'file')]
proxyKnobNodes = [i for i in nodes if __NodeHasKnobWithName(i, 'proxy')]
if not fileKnobNodes and not proxyKnobNodes: raise ValueError, "No file nodes selected"
nodeMatches = []
knobMatches = []
for i in fileKnobNodes:
if __FindNode(searchstr, i['file']):
nodeMatches.append(i)
knobMatches.append(i['file'])
for i in proxyKnobNodes:
if __FindNode(searchstr, i['proxy']):
nodeMatches.append(i)
knobMatches.append(i['proxy'])
return nodeMatches, knobMatches
URL是構(gòu)造函數(shù)第二個參數(shù),這能讓面板保存或作為自定義布局的一部分再次喚醒:
class SearchReplacePanel(nukescripts.PythonPanel):
def __init__(self):
nukescripts.PythonPanel.__init__(self, 'Search and Replace', 'com.ohufx.SearchReplace')
# CREATE KNOBS
self.nodesChoice = nuke.Enumeration_Knob('nodes', 'Source Nodes', ['all', 'selected'])
self.searchStr = nuke.String_Knob('searchStr', 'Search for:')
self.update = nuke.PyScript_Knob('update', 'Update')
self.info = nuke.Multiline_Eval_String_Knob('info', 'Found')
self.info.setEnabled(False)
self.replaceStr = nuke.String_Knob('replaceStr', 'Replace with:')
self.replace = nuke.PyScript_Knob('replace', 'Replace')
# ADD KNOBS
self.addKnob(self.nodesChoice)
self.addKnob(self.searchStr)
self.addKnob(self.update)
self.addKnob(self.info)
self.addKnob(self.replaceStr)
self.addKnob(self.replace)
self.matches = None
def search(self, nodes):
nodeMatches, knobMatches = search(self.searchStr.value(), nodes)
nodes = [n.name() for n in nodeMatches]
infoStr = '%s node(s) found:\n\t%s' % (len(nodes), ', '.join(nodes))
self.info.setValue(infoStr)
return knobMatches
def knobChanged(self, knob):
if knob in (self.searchStr, self.update, self.nodesChoice):
srcNodes = { 'all': nuke.allNodes(), 'selected': nuke.selectedNodes() }
self.matches = self.search(srcNodes[self.nodesChoice.value()])
elif knob is self.replace and self.matches is not None:
for k in self.matches:
newStr = re.sub(self.searchStr.value(), self.replaceStr.value(), k.value())
k.setValue(newStr)
下面是將面板添加到Panel的默認(rèn)方法(下面代碼放入menu.py):
def addSRPanel():
global srPanel
srPanel = SearchReplacePanel()
return srPanel.addToPane()
paneMenu = nuke.menu('Pane')
paneMenu.addCommand('SearchReplace', addSRPanel)
nukescripts.registerPanel('com.ohufx.SearchReplace', addSRPanel)
![](https://docs.thefoundry.co.uk/products/nuke/developers/100/pythondevguide/_images/srPanel_01.png)