美文网首页CgTd
Nuke Python 与资产管理系统和生产线集成

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

作者: N景波 | 来源:发表于2016-11-22 11:53 被阅读0次

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

    目录结构的示例:


    大多数设备都用环境变量来辨识正在工作的镜头,一般会有相应的工具让制作人员设置环境变量。为了简化,仅设置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里面设置动态文件很简单。

    nuke.addFavoriteDir( name='NUKE SCRIPTS', directory = assertManager.nukeDir(), type = nuke.SCRIPT )
    

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

    保存工作流的自定义脚本

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

    def easySave():
        nkDir = nukeDir()
    

    让用户输入描述文字:

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

    有了nuke目录和用户描述,就可以添加版本和命令规范了。此例中,用show,sequence,shot name紧跟用户描述和版本.

    这就是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来保存了:


    添加自定义nuke菜单:

    shotMenu = '%s-%s' % ( os.getenv('SEQ'), osgetenv('SHOT'))
    nuke.menu('Nuke' ).addCommand( shotMenu + '/Easy Save', assertManger.easySave)
    

    想要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里面:

    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的值。如果需要打开所要求的脚本:

    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 )
    

    代码运行后的界面:


    自定义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:

    首先需要一个函数来分析目录中渲染的各种版本图像。现实中这应该是一个数据库调用来获取所有发布的图像序列。同时也需要图像序列的发现。但是还得先分析目录,并获取所有子目录 getFileSeq函数获取在version knob的所有字符串序列:

    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

    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模块代码:

    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的例子:

    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
    

    相关文章

      网友评论

        本文标题:Nuke Python 与资产管理系统和生产线集成

        本文链接:https://www.haomeiwen.com/subject/smmppttx.html