【NUKE教程】Nuke Python 与资产管理系统和生产线集成

14 九月, 2016
7
0

nukepythonmg
把资产管理系统集成到nuke里面,方法很多,不深入介绍,一切从简。 数据库用简单的目录结构替代,不过有一定的命名规范。代码中创建的工具都存在assertManager这个模块了。其他部分比如创建menu,hotkey等放在menu.py里面。因此记得导入import assertManger.

目录结构的示例:
Nuke Python 与资产管理系统和生产线集成

大多数设备都用环境变量来辨识正在工作的镜头,一般会有相应的工具让制作人员设置环境变量。为了简化,仅设置SHOW, SEQ,SHOT:

os.environ['SHOW'] = 'showA'
os.environ['SEQ'] = 'seq1'
os.environ['SHOT'] = 'shot2'

注意:也可以使用nuke的用户knob来保存这些变量。

方便函数,返回项目目录:

def rootDir():
    return '/Users/frank/Desktop/shows'

有了环境变量,以及rootDir()函数,就可以写各种函数来控制当前shot的输入输出。那就写一个获取当前镜头的nuke脚本吧:

def nukeDir():
    nkDir = os.path.join( root.Dir(), os.getenv('SHOW'), os.getenv('SEQ'), os.getenv('SHOT'), 'nuke')
    if not os.path.isdir( nkDir ):
        raise ValueError, 'NUKE directory dose not exit'
    return nkDir
```    
要获取当前shot的nuke目录,基于当前镜头的环境变量,在menu.py里面设置动态文件很简单。
```python
nuke.addFavoriteDir( name='NUKE SCRIPTS', directory = assertManager.nukeDir(), type = nuke.SCRIPT )

注意: 不要忘了在menu.py里面导入包含函数的模块,所有的代码都在assertManager模块

Nuke Python 与资产管理系统和生产线集成

保存工作流的自定义脚本

有了nukeDir(),咱就写一个easySave函数,用户无需打开保存界面,也不用担心命令转换,就能完成保存。

def easySave():
    nkDir = nukeDir()

让用户输入描述文字:

# GET DESCRIPTION FROM USER BUT STRIP ALL WHITE SPACES
description = nuke.getInput( 'script description', 'bashComp' ).replace( ' ', '' )

Nuke Python 与资产管理系统和生产线集成 有了nuke目录和用户描述,就可以添加版本和命令规范了。此例中,用show,sequence,shot name紧跟用户描述和版本. Nuke Python 与资产管理系统和生产线集成 这就是easySave函数,构造命名规范。版本从1开始,检查中如果文件存在,版本增加:

def easySave():
    nkDir = nukeDir()
    description = nuke.getInput(' script description' , 'bashCom' ). replace(' ', '')
    fileSaved = False
    version = 1
    while not fileSaved:
            nkName = '%s_%s_%s_%s_v%02d.nk' % ( os.getenv('SHOW'), os.getenv('SEQ'), os.getenv('SHOT'), description, version )
            nkPath = os.path.join( nkDir, nkName )
            if os.path.isfile( nkPath):
                version += 1
                coutinue
            nuke.scriptSaveAs( nkPath )
            fileSaved = True
    return nkPath
```    
上面代码用来第一次保存文件,以后就可以使用 菜单File 》 save new version来保存了:
![](https://docs.thefoundry.co.uk/products/nuke/developers/100/pythondevguide/_images/assetMan_06.png)
添加自定义nuke菜单:
```python
shotMenu = '%s-%s' % ( os.getenv('SEQ'), osgetenv('SHOT'))
nuke.menu('Nuke' ).addCommand( shotMenu + '/Easy Save', assertManger.easySave)

Nuke Python 与资产管理系统和生产线集成

想要nuke的脚本都带版本号保存(如果用户忽略easySave),就用脚本来检测文件名,时候包含大小写的v后跟数字来简单判断:

def checkScriptName():
    if not re.search( r' [vV]\d+', nuke.root().name() ):
            raise NameError, 'Please include a version number and save script again.'
```            
将此函数挂接到一个回调函数上,这最好放到menu.py里面:
```python
nuke.addOnScriptSave( assertManager.checkScriptName )

如果文件不包含版本信息,此函数会抛出异常,存储就会终止,进而强迫输入版本信息。其函数的其他好处:

    • 确保NUKE脚本仅保存在nuke目录下面
  • 保存过程写到log文件
  • 备份nuke脚本到其他地方

自定义脚本加载流程

现在看看如何通过自定义面板显示镜头nuke下的所有脚本并加载的。首先,写一个函数返回目录结构下的所有脚本,可以用glob来抓取nukeDir()目录:

def getNukeScripts():
    nkFiles = glob( os.path.join( nukeDir(), '*.nk' ))
    return nkFiles

给找到的基本弄个checkbox:

class NkPanel( nukescripts.PythonPanel ):
    def __init__( self, nkScripts ):
        nukescripts.PythonPanel.__init__(self, 'Open NUKE Script' )
        self.checkboxs = []
        self.nkScripts = nkScripts
        
        for i, n in enumerate( self.nkScripts ):
            k = nuke.boolean_Knob( ' nk_%s', % i, os.path.basename( n ))
            self.addKnob( k )
            k.setFlag( nuke.STARTLine)
            self.checkboxs.append( k )

理想状态,我们会用单选按钮来让用户选择一个脚本,但nuke没此功能,那我们就挂载上一个knobChanged来保证仅有一个checkbox选中:

def knobChanged( self, knob ):
    if knob in self.checkboxes:
        for cb in self.checkboxes:
            if knob == cb:
                index = int( knob.name().split('_')[-1] )
                self.selectedScript = self.nkScript[ index ]
                continue
            cb.setValue( False )

下面是完整的panel代码:

class NkPanel( nukescripts.PythonPanel ):
    def __init__( self, nkScripts ):
        nukescripts.PythonPanel.__init__(self, 'Open nuke script' )
        self.checkboxes = []
        self.nkScripts = nkScripts
        self.selectedScript = ''
    
        for i, n in enumerate( self.nkScripts ):
            k = nuke.Boolean_Knob('nk_%s' % i, os.path.basename(n))
            self.addKnob( k)
            k.setFlag( nuke.STARTLINE )
            self.checkboxes.append(k)
    def knobChanged( self, knob ):
        if knob in self.checkboxes:
            for cb in self.checkboxes:
                if konb == cb:
                    index = int(knob.name().split('_')[-1]
                    self.selectedScript = self.nkScripts[ index ]
                    continue
                cb.setValue( False )
```                
更多信息请看custom panel
有了panel代码,创建一个帮助函数(在menu.py)里面来打开panel,并返回check的值。如果需要打开所要求的脚本:
```python
def nkPanelHelper():
    nkScripts = assertManager.getNukeScripts(0
    if not nkScirpts:
        return
    p = assertManager.NkPanel( nkScripts )
    p.setMinimumSize( 200, 200 )
    
    if p.showModalDialog():
        if p.selectedScript:
            nuke.scriptOpen( p.selectedScript )
```            
代码运行后的界面:
![](https://docs.thefoundry.co.uk/products/nuke/developers/100/pythondevguide/_images/assetMan_07.png)
##### 自定义write节点
很多机构都用自己的write节点,或者修改后的write节点,来约束制作人员遵守命名规范和工作目录。如果这个有效,那将降低可能的人为错误,看看例子吧。
    _images/assetMan_08.png
    这仅仅是一个单独的write节点使用python和tcl挂载了gizmo属性面板。制作人员选择渲染类型就可以了(其将决定目标文件夹),赋给版本号和描述(用来建立正确的文件名)点击 “Render”,看看内容:
    _images/assetMan_11.png
    两个用户knob再加上后缀.exr就构成了file控制选项( 在这个例子里,我们总是渲染exr文件)
    _images/assetMan_12.png
    两个用户knob是dirname和filename,临时变量,因此我们不用将所有代码压缩到file控制里面:
    _images/assetMan_13.png
    driname是靠组合root目录,环境变量和knob type来组成的:
    os.path.join( assertManager.rootDir(), os.getenv('SHOW', os.getenv('SEQ'), os.getenv('SHOT'), nuke.thisParent.knob('type').value() )
    
    fileName 实际使用TCL语法来建立文件名,这种情况,tcl比python更加简洁:
    [value parent.type]_[value parent.description]_v[format %02d [ value parent.version]]
    如果你真想用python,如下:
    [python '%s_%s_v%02d' % ( nuke.thisParent().knob('type').value(), nuke.thisParent().knob('description').value(), nuke.thisParent().knob('version').value() )]
    有时好的TCL也不赖。
    有时会保存成名为WriteAssert的gizmo,因为以Write开始的节点类,nuke会自动显示其文件名。
    _images/assetMan_14.png
    在menu.py将新的gizmo挂载上Image菜单,并赋给w快捷键:
    nuke.menu('Nodes').addCommand('Image/WriteAssert', lambda: nuke.createNode(' WriteAssert' ), 'w' )
    为了让新的gizmo正常工作,在输出目录不存在时,要自动创建。代码如下:
    def createOutDirs():
        trgDir = os.path.dirname( nuke.filename( nuke.thisNode() ) )
        if not os.path.isdir( trgDir ):
            os.makedirs( trgDir )
    
    想让这段代码运行有两个法子:放到write节点的beforeRender  knob里面 或者 全局回调函数,需要在menu.py里面添加如下语句:
    nuke.addbeforeRender( assertManager.createOutDirs, nodeClass = 'Write' )
    这让beforeRender的knob很干净,但是也有缺点,就是某些东西出错后,很难丢掉。
    
    想用write的beforeRender,只需放到menu.py里面就好了
    nuke.knobDefault( 'Write.beforeRender', 'assertManager.createOutDirs() ' )
    _images/assetMan_09.png
这意味着很容易就能摆脱回调,这在出错时算是好事。
##### 自定义读节点
现在来自定义Read节点,从数据库或者目录结构加载图像:
为此目的,在Read节点上添加自定义knob:
![](https://docs.thefoundry.co.uk/products/nuke/developers/100/pythondevguide/_images/assetMan_10.png)
首先需要一个函数来分析目录中渲染的各种版本图像。现实中这应该是一个数据库调用来获取所有发布的图像序列。同时也需要图像序列的发现。但是还得先分析目录,并获取所有子目录 getFileSeq函数获取在version knob的所有字符串序列:
```python
def getVersions():
    types = ['plates', 'cg', 'comp', 'roto' ]
    versionDict = {}
    shotDir = os.path.join( rootDir(), os.getenv('SHOW'), os.getenv('SEQ'), os.getenv('SHOT') )
    for t in types:
        versionDict[t] = []
        typeDir = os.path.join( shotDir, t)
        for d in os.listdir( typeDir ):
            path = os.path.join( typeDir, d)
            if os.path.isdir( path):
                versionDict[t].append( getFileSeq( path)) 
    return versionDict

其中getFileSeq函数返回每个子目录的序列符号,注意这是简化版,实际中会用数据库调用取代。

def getFileSeq( dirPath ):
    dirName = os.path.basename( dirPath )
    files = glob( os.path.join( dirPath, '%s.*.*' % dirName ) )
    firstString = re.findall( r'\d+', files[0] )[-1]
    padding = len(firstString )
    paddingString = '%02s' % padding
    first  = int( firstString )
    last = int( re.findall( r'\d+', files[-1] )[-1] )
    ext = os.path.splitext( files[0] )[-1]
    fileName = '%s.%%%sd%s %s-%s' % ( dirName, str(padding).zfill(2), ext, first, last)
    return os.path.join( dirPath, fileName)

有上面的代码,或者合适的数据库查询,我们能获取一个字典,包含所有版本信息。

getVersions()

可以把上面的东西放入Read节点里面, 创建上图的user konb,创建一个函数更新version knob来显示硬盘上的东西(数据库)

def createVersionKnobs():
    # CREATE USER KNOBS
    node = nuke.thisNode()
    tabKnob = nuke.Tab_Knob( 'DB', 'DB' )
    typeKnob = nuke.Enumeration_Knob( 'versionType', 'type', ['plates', 'cg', 'roto'] )
    updateKnob = nuke.PyScript_Knob( 'update', 'update' )
    updateKnob.setValue( 'assetManager.updateVersionKnob()' )
    versionKnob = nuke.Enumeration_Knob( '_version', 'version', [] ) # DO NOT USE "VERSION" AS THE KNOB NAME AS THE READ NODE ALREADY HAS A "VERSION" KNOB
    loadKnob = nuke.PyScript_Knob( 'load', 'load' )
    # ASSIGN PYTHON SCRIPT AS ONE LARGE STRING
    loadScript = '''#THIS ASSUMES NO WHITE SPACES IN FILE PATH
        node = nuke.thisNode()
        path, range = node['_version'].value().split()
        first, last = range.split('-')
        node['file'].setValue( path )
        node['first'].setValue( int(first) )
        node['last'].setValue( int(last) )'''
    loadKnob.setValue( loadScript )
    # ADD NEW KNOBS TO NODE
    for k in ( tabKnob, typeKnob, updateKnob, versionKnob, loadKnob ):
        node.addKnob( k )
    # UPDATE THE VERSION KNOB SO IT SHOWS WHAT'S ON DISK / IN THE DATABASE
    updateVersionKnob()

updateVersionKnob函数如下:

def updateVersionKnob():
    node = nuke.thisNode()
    knob = nuke.thisKnob()
    # RUN ONLY IF THE TYPE KNOB CHANGES OR IF THE NODE PANEL IS OPENED
    if not knob or knob.name() in [ 'versionType', 'showPanel' ]:
        # GET THE VERSION DICTIONARY
        versionDict = getVersions()
        # POPULATE THE VERSION KNOB WITH THE VERSIONS REQUESTED THROUGH THE TYPE KNOB
        node['_version'].setValues( versionDict[ node['versionType'].value() ] )
        # SET THE A VALUE TO THE FIRST ITEM IN THE LIST
        node['_version'].setValue(0)
```        
因为createVersionKnobs和updateVersionKnob是回调函数,可以用nuke.thiNode() nuke.thisKnob()来引用对应的节点和knob。
在menu.py菜单中,当Read 节点创建时自动运行createVersionKnobs
```python
nuke.addOnUserCreate( assertManager.createVersionKnobs, nodeClass = 'Read' )

同时添加回调更新version knob:

	
nuke.addKnobChanged( assertManger.updateVersionKnob, nodeClass = 'Read')

最后重写 热键r 来创建空的Read节点,并且附带新的DB,这样制作人员就可以快速选择一个版本
并点击laod,而不是一个个来浏览了。把这个mini函数添加到menu.py里面

def customRead():
    n = nuke.creatNode('Read')
    n['DB'].setFlag( 0 )
```    
这将其赋给菜单项和热键:

nuke.menu(‘Nodes’).addCommand( ‘Image/Read’, customRead, ‘r’ )

下面是整个assertManager模块代码:
```python
import nukescripts
import nuke
import re
import os
from glob import glob
# SET UP EXAMPLE ENVIRONMENT
os.environ['SHOW'] = 'showA'
os.environ['SEQ'] = 'seq1'
os.environ['SHOT'] = 'shot2'
# DEFINE FACILITY ROOT
def rootDir():
    return '/Users/frank/Desktop/shows'
# DEFINE SHOT'S NUKE DIR
def nukeDir():
    nkDir = os.path.join( rootDir(), os.getenv('SHOW'), os.getenv('SEQ'), os.getenv('SHOT'), 'nuke' )
    if not os.path.isdir( nkDir ):
        raise ValueError, 'Nuke directory does not exist'
    return nkDir
def easySave():
    nkDir = nukeDir()
    # GET DESCRIPTION FROM USER BUT STRIP ALL WHITE SPACES
    description = nuke.getInput( 'script description', 'bashComp' ).replace( ' ', '' )
    fileSaved = False
    version = 1
    while not fileSaved:
        # CONSTRUCT FILE NAME
        nkName = '%s_%s_%s_%s_v%02d.nk' % ( os.getenv( 'SHOW'), os.getenv( 'SEQ'), os.getenv( 'SHOT'), description, version )
        # JOIN DIRECTORY AND NAME TO FORM FULL FILE PATH
        nkPath = os.path.join( nkDir, nkName )
        # IF FILE EXISTS VERSION UP        
        if os.path.isfile( nkPath ):
            version += 1
            continue
        # SAVE NUKE SCRIPT
        nuke.scriptSaveAs( nkPath )
        fileSaved = True
    return nkPath
# CHECK FOR VERSION IN SCRIPT NAME
def checkScriptName():
    if not re.search( r'[vV]\d+', nuke.root().name() ):
        raise NameError, 'Please include a version number and save script again.'
# GET ALL NUKE SCRIPTS FOR CURRENT SHOT
def getNukeScripts():
    nukeDir = os.path.join( rootDir(), os.getenv('SHOW'), os.getenv('SEQ'), os.getenv('SHOT'), 'nuke' )
    nkFiles = glob( os.path.join( nukeDir, '*.nk'  ) )
    return nkFiles
# PARSE "DATABASE" FOR AVAILABLE IMAGE SEQUENCES
def getVersions():
    '''Return a dictionary of rendered versions per type'''
    # DEFINE THE DIRECTORIES YOU WANT TO INCLUDE
    types = [ 'plates', 'cg', 'comp', 'roto' ]
    # INITIALISE THE DICTIONARY WE WILL RETURN AT THE END OF THE FUNCTION
    versionDict = {}
    # GET THE DIRECTORY BASED ON THE CURRENT SHOT ENVIRONMENT
    shotDir = os.path.join( rootDir(), os.getenv('SHOW'), os.getenv('SEQ'), os.getenv('SHOT') )
    # LOOP THROUGH THE FOLDERS INSIDE THE SHOT DIRECTORY AND COLLECT THE IMAGE SEQUENCES THEY CONTAIN
    for t in types:
        versionDict[t] = [] # THIS WILL HOLD THE FOUND SEQUENCES
        typeDir = os.path.join( shotDir, t ) # GET THE CURRENT DIRECTORY PATH
        for d in os.listdir( typeDir ): # LOOP THROUGH IT'S CONTENTS
            path = os.path.join( typeDir, d)
            if os.path.isdir( path ): # LOOP THROUGH SUB DIRECTORIES
                versionDict[t].append( getFileSeq( path ) ) # RUN THE getFileSeq() FUNCTION AND APPEND IT'S OUTPUT TO THE LIST
    return versionDict
# ONUSERCREATE CALLBACK FOR READ NODE
def createVersionKnobs():
    '''
    Add as callback to add user knobs in Read nodes.
    In menu.py or init.py:
       nuke.addOnUserCreate( assetManager.createVersionKnobs, nodeClass='Read' )        
    '''
    # CREATE USER KNOBS
    node = nuke.thisNode()
    tabKnob = nuke.Tab_Knob( 'DB', 'DB' )
    typeKnob = nuke.Enumeration_Knob( 'versionType', 'type', ['plates', 'cg', 'roto'] )
    updateKnob = nuke.PyScript_Knob( 'update', 'update' )
    updateKnob.setValue( 'assetManager.updateVersionKnob()' )
    versionKnob = nuke.Enumeration_Knob( '_version', 'version', [] ) # DO NOT USE "VERSION" AS THE KNOB NAME AS THE READ NODE ALREADY HAS A "VERSION" KNOB
    loadKnob = nuke.PyScript_Knob( 'load', 'load' )
    
    # ASSIGN PYTHON SCRIPT AS ONE LARGE STRING
    loadScript = '''#THIS ASSUMES NO WHITE SPACES IN FILE PATH
node = nuke.thisNode()
path, range = node['_version'].value().split()
first, last = range.split('-')
node['file'].setValue( path )
node['first'].setValue( int(first) )
node['last'].setValue( int(last) )'''
    loadKnob.setValue( loadScript )
    
    # ADD NEW KNOBS TO NODE
    for k in ( tabKnob, typeKnob, updateKnob, versionKnob, loadKnob ):
        node.addKnob( k )
    # UPDATE THE VERSION KNOB SO IT SHOWS WHAT'S ON DISK / IN THE DATABASE
    updateVersionKnob()
       
# KNOBCHANGED CALLBACK FOR CUSTOMISED READ NODE
def updateVersionKnob():
    '''
    Add as callback to list versions per type in Read node's user knob
    In menu.py or init.py:
       nuke.addKnobChanged( assetManager.updateVersionKnob, nodeClass='Read' )  
    '''
    node = nuke.thisNode()
    knob = nuke.thisKnob()
    # RUN ONLY IF THE TYPE KNOB CHANGES OR IF THE NODE PANEL IS OPENED.
    if not knob or knob.name() in [ 'versionType', 'showPanel' ]:
        # GET THE VERSION DICTIONARY
        versionDict = getVersions()
        # POPULATE THE VERSION KNOB WITH THE VERSIONS REQUESTED THROUGH THE TYPE KNOB
        node['_version'].setValues( versionDict[ node['versionType'].value() ] )
        # SET THE A VALUE TO THE FIRST ITEM IN THE LIST
        node['_version'].setValue(0)
# BEFORERENDER CALLBACK FOR WRITE ASSET GIZMO
def createOutDirs():
    '''
    Create output directory if it doesn't exist.
    Add as callback to Write node's.    
    In menu.py or init.py:
       # CALLBACK VIA KNOB DEFAULT
       nuke.knobDefault( 'Write.beforeRender', 'assetManager.createOutDirs()')
    OR:
       nuke.addBeforeRender( assetManager.createOutDirs, nodeClass='Write' )
    '''
    trgDir = os.path.dirname( nuke.filename( nuke.thisNode() ) )
    if not os.path.isdir( trgDir ):
        os.makedirs( trgDir )               
def getFileSeq( dirPath ):
    '''Return file sequence with same name as the parent directory. Very loose example!!'''
    dirName = os.path.basename( dirPath )
    # COLLECT ALL FILES IN THE DIRECTORY THAT HVE THE SAME NAME AS THE DIRECTORY
    files = glob( os.path.join( dirPath, '%s.*.*' % dirName ) )
    # GRAB THE RIGHT MOST DIGIT IN THE FIRST FRAME'S FILE NAME
    firstString = re.findall( r'\d+', files[0] )[-1]
    # GET THE PADDING FROM THE AMOUNT OF DIGITS
    padding = len( firstString )
    # CREATE PADDING STRING FRO SEQUENCE NOTATION
    paddingString = '%02s' % padding
    # CONVERT TO INTEGER
    first = int( firstString )
    # GET LAST FRAME
    last = int( re.findall( r'\d+', files[-1] )[-1] )
    # GET EXTENSION
    ext = os.path.splitext( files[0] )[-1]
    # BUILD SEQUENCE NOTATION
    fileName = '%s.%%%sd%s %s-%s' % ( dirName, str(padding).zfill(2), ext, first, last )
    # RETURN FULL PATH AS SEQUENCE NOTATION
    return os.path.join( dirPath, fileName )
# PANEL TO SHOW NUKE SCRIPS FOR CURRENT SHOT
class NkPanel( nukescripts.PythonPanel ):
    def __init__( self, nkScripts ):
        nukescripts.PythonPanel.__init__( self, 'Open Nuke Script' )
        self.checkboxes = []
        self.nkScripts = nkScripts
        self.selectedScript = ''
        
        for i, n in enumerate( self.nkScripts ):
            # PUT INDEX INTO KNOB NAMES SO WE CAN IDENTIFY THEM LATER
            k = nuke.Boolean_Knob( 'nk_%s' % i, os.path.basename( n ) )
            self.addKnob( k )
            k.setFlag( nuke.STARTLINE )
            self.checkboxes.append( k )
 
    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 )

此部分中menu.py的代码:

import assetManager
# CREATE A READ NODE AND OPEN THE "DB" TAB
def customRead():
        n = nuke.createNode( 'Read' )
        n['DB'].setFlag( 0 )
# ADD CUSTOM READ AND WRITE TO TOOLBAR
nuke.menu( 'Nodes' ).addCommand( 'Image/WriteAsset', lambda: nuke.createNode( 'WriteAsset' ), 'w' )
nuke.menu( 'Nodes' ).addCommand( 'Image/Read', customRead, 'r' )
# ADD EASY SAVE TO SHOT MENU
shotMenu = '%s - %s' % ( os.getenv( 'SEQ' ), os.getenv('SHOT') )
nuke.menu( 'Nuke' ).addCommand( shotMenu+'/Easy Save', assetManager.easySave )
# SET FILE BROWSER FAVORITES
nuke.addFavoriteDir(
    name = 'NUKE SCRIPTS',
    directory = assetManager.nukeDir(),
    type = nuke.SCRIPT)
# HELPER FUNCTION FOR NUKE SCRIPT PANEL
def nkPanelHelper():
        # GET ALL NUKE SCRIPTS FOR CURRENT SHOT
        nkScripts = assetManager.getNukeScripts()
        if not nkScripts:
                # IF THERE ARE NONE DON'T DO ANYTHING
                return
        # CREATE PANEL
        p = assetManager.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 )
# ADD CALLBACKS
nuke.addOnScriptSave( assetManager.checkScriptName )
nuke.addOnUserCreate( nkPanelHelper, nodeClass='Root')
nuke.addOnUserCreate( assetManager.createVersionKnobs, nodeClass='Read' )
nuke.addKnobChanged( assetManager.updateVersionKnob, nodeClass='Read' )
#nuke.addBeforeRender( assetManager.createOutDirs, nodeClass='Write' )
nuke.knobDefault( 'Write.beforeRender', 'assetManager.createOutDirs()')

自定义UDIM分析函数

UDIM import用的是标准分析转换。被UDIM import使用的分析函数可以重定义,使其支持其他文件名转换。想用个人UMIM分析那就运行下面代码:

	
nukescripts.udim_import( myParsingFunc, "UV" )

第一个参数是分析函数名。第二个参数是导入对话框中的列名,来确认标题坐标。
通过唯一值可以确定纹理(UDIM)或者数值对(u,v或者s,t)。UDIM import python脚本两者都支持。重定义的分析函数需要解码文件名字符串并返回UDIM或者u,v分块纹理或者一个元组整形数值。如果如法确认,它将返回None

下面是一个分析函数的列子, 会解码文件名: filename.1003.v10.tif:

import re
def myParsingFunc( f ):
    sequences = re.split("[._]+", f )
    udim = None
    for s in sequences:
        try:
            udim = int(s)
        except ValueError:
            udim = None
        if udim>1000 and udim <2000:
             break
     if udim == None:
         return None
     return udim

这是一个分析 filename._u00_v00.tif:

import re
def myParsingFunc(f):
        sequences = re.split("[._]+", f)
        u = None
        v = None
        for s in sequences:
            try:
                head = s[0]
                tail = s[1: len(s) ]
                if head == 'u':
                        u = int(tail)
                if head == 'v':
                        v = int(tail)
            except ValueError:
                    u = None
                    v = None
        if u ==None or v ==None:
                return None
        return u,v
```        
下面是分析filename_s04t00_00_v019.tif的例子:
```python    
import re
def myParsingFunc( f):
        sequences = f.split("_")
        s_value = None
        t_value = None
        
        for s in sequences:
                if len(s) != 6:
                    continue
                 if s[0] != 's' or s[3] !='t':
                    continue
                try:
                    s_value = int( s[1:3] )
                    t_value = int( s[4:6] )   
                except ValueError:
                    s_value = None
                    t_value = None
        if s_value == None or t_value ==None:
            return None
        return s_value, t_value